Upgrade to ExtJS 3.3.1 - Released 11/30/2010
[extjs.git] / ext-all-debug-w-comments.js
1 /*!
2  * Ext JS Library 3.3.1
3  * Copyright(c) 2006-2010 Sencha Inc.
4  * licensing@sencha.com
5  * http://www.sencha.com/license
6  */
7 (function(){
8
9 var EXTUTIL = Ext.util,
10     EACH = Ext.each,
11     TRUE = true,
12     FALSE = false;
13 /**
14  * @class Ext.util.Observable
15  * Base class that provides a common interface for publishing events. Subclasses are expected to
16  * to have a property "events" with all the events defined, and, optionally, a property "listeners"
17  * with configured listeners defined.<br>
18  * For example:
19  * <pre><code>
20 Employee = Ext.extend(Ext.util.Observable, {
21     constructor: function(config){
22         this.name = config.name;
23         this.addEvents({
24             "fired" : true,
25             "quit" : true
26         });
27
28         // Copy configured listeners into *this* object so that the base class&#39;s
29         // constructor will add them.
30         this.listeners = config.listeners;
31
32         // Call our superclass constructor to complete construction process.
33         Employee.superclass.constructor.call(this, config)
34     }
35 });
36 </code></pre>
37  * This could then be used like this:<pre><code>
38 var newEmployee = new Employee({
39     name: employeeName,
40     listeners: {
41         quit: function() {
42             // By default, "this" will be the object that fired the event.
43             alert(this.name + " has quit!");
44         }
45     }
46 });
47 </code></pre>
48  */
49 EXTUTIL.Observable = function(){
50     /**
51      * @cfg {Object} listeners (optional) <p>A config object containing one or more event handlers to be added to this
52      * object during initialization.  This should be a valid listeners config object as specified in the
53      * {@link #addListener} example for attaching multiple handlers at once.</p>
54      * <br><p><b><u>DOM events from ExtJs {@link Ext.Component Components}</u></b></p>
55      * <br><p>While <i>some</i> ExtJs Component classes export selected DOM events (e.g. "click", "mouseover" etc), this
56      * is usually only done when extra value can be added. For example the {@link Ext.DataView DataView}'s
57      * <b><code>{@link Ext.DataView#click click}</code></b> event passing the node clicked on. To access DOM
58      * events directly from a Component's HTMLElement, listeners must be added to the <i>{@link Ext.Component#getEl Element}</i> after the Component
59      * has been rendered. A plugin can simplify this step:<pre><code>
60 // Plugin is configured with a listeners config object.
61 // The Component is appended to the argument list of all handler functions.
62 Ext.DomObserver = Ext.extend(Object, {
63     constructor: function(config) {
64         this.listeners = config.listeners ? config.listeners : config;
65     },
66
67     // Component passes itself into plugin&#39;s init method
68     init: function(c) {
69         var p, l = this.listeners;
70         for (p in l) {
71             if (Ext.isFunction(l[p])) {
72                 l[p] = this.createHandler(l[p], c);
73             } else {
74                 l[p].fn = this.createHandler(l[p].fn, c);
75             }
76         }
77
78         // Add the listeners to the Element immediately following the render call
79         c.render = c.render.{@link Function#createSequence createSequence}(function() {
80             var e = c.getEl();
81             if (e) {
82                 e.on(l);
83             }
84         });
85     },
86
87     createHandler: function(fn, c) {
88         return function(e) {
89             fn.call(this, e, c);
90         };
91     }
92 });
93
94 var combo = new Ext.form.ComboBox({
95
96     // Collapse combo when its element is clicked on
97     plugins: [ new Ext.DomObserver({
98         click: function(evt, comp) {
99             comp.collapse();
100         }
101     })],
102     store: myStore,
103     typeAhead: true,
104     mode: 'local',
105     triggerAction: 'all'
106 });
107      * </code></pre></p>
108      */
109     var me = this, e = me.events;
110     if(me.listeners){
111         me.on(me.listeners);
112         delete me.listeners;
113     }
114     me.events = e || {};
115 };
116
117 EXTUTIL.Observable.prototype = {
118     // private
119     filterOptRe : /^(?:scope|delay|buffer|single)$/,
120
121     /**
122      * <p>Fires the specified event with the passed parameters (minus the event name).</p>
123      * <p>An event may be set to bubble up an Observable parent hierarchy (See {@link Ext.Component#getBubbleTarget})
124      * by calling {@link #enableBubble}.</p>
125      * @param {String} eventName The name of the event to fire.
126      * @param {Object...} args Variable number of parameters are passed to handlers.
127      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true.
128      */
129     fireEvent : function(){
130         var a = Array.prototype.slice.call(arguments, 0),
131             ename = a[0].toLowerCase(),
132             me = this,
133             ret = TRUE,
134             ce = me.events[ename],
135             cc,
136             q,
137             c;
138         if (me.eventsSuspended === TRUE) {
139             if (q = me.eventQueue) {
140                 q.push(a);
141             }
142         }
143         else if(typeof ce == 'object') {
144             if (ce.bubble){
145                 if(ce.fire.apply(ce, a.slice(1)) === FALSE) {
146                     return FALSE;
147                 }
148                 c = me.getBubbleTarget && me.getBubbleTarget();
149                 if(c && c.enableBubble) {
150                     cc = c.events[ename];
151                     if(!cc || typeof cc != 'object' || !cc.bubble) {
152                         c.enableBubble(ename);
153                     }
154                     return c.fireEvent.apply(c, a);
155                 }
156             }
157             else {
158                 a.shift();
159                 ret = ce.fire.apply(ce, a);
160             }
161         }
162         return ret;
163     },
164
165     /**
166      * Appends an event handler to this object.
167      * @param {String}   eventName The name of the event to listen for.
168      * @param {Function} handler The method the event invokes.
169      * @param {Object}   scope (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.
170      * <b>If omitted, defaults to the object which fired the event.</b>
171      * @param {Object}   options (optional) An object containing handler configuration.
172      * properties. This may contain any of the following properties:<ul>
173      * <li><b>scope</b> : Object<div class="sub-desc">The scope (<code><b>this</b></code> reference) in which the handler function is executed.
174      * <b>If omitted, defaults to the object which fired the event.</b></div></li>
175      * <li><b>delay</b> : Number<div class="sub-desc">The number of milliseconds to delay the invocation of the handler after the event fires.</div></li>
176      * <li><b>single</b> : Boolean<div class="sub-desc">True to add a handler to handle just the next firing of the event, and then remove itself.</div></li>
177      * <li><b>buffer</b> : Number<div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
178      * by the specified number of milliseconds. If the event fires again within that time, the original
179      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
180      * <li><b>target</b> : Observable<div class="sub-desc">Only call the handler if the event was fired on the target Observable, <i>not</i>
181      * if the event was bubbled up from a child Observable.</div></li>
182      * </ul><br>
183      * <p>
184      * <b>Combining Options</b><br>
185      * Using the options argument, it is possible to combine different types of listeners:<br>
186      * <br>
187      * A delayed, one-time listener.
188      * <pre><code>
189 myDataView.on('click', this.onClick, this, {
190 single: true,
191 delay: 100
192 });</code></pre>
193      * <p>
194      * <b>Attaching multiple handlers in 1 call</b><br>
195      * The method also allows for a single argument to be passed which is a config object containing properties
196      * which specify multiple handlers.
197      * <p>
198      * <pre><code>
199 myGridPanel.on({
200 'click' : {
201     fn: this.onClick,
202     scope: this,
203     delay: 100
204 },
205 'mouseover' : {
206     fn: this.onMouseOver,
207     scope: this
208 },
209 'mouseout' : {
210     fn: this.onMouseOut,
211     scope: this
212 }
213 });</code></pre>
214  * <p>
215  * Or a shorthand syntax:<br>
216  * <pre><code>
217 myGridPanel.on({
218 'click' : this.onClick,
219 'mouseover' : this.onMouseOver,
220 'mouseout' : this.onMouseOut,
221  scope: this
222 });</code></pre>
223      */
224     addListener : function(eventName, fn, scope, o){
225         var me = this,
226             e,
227             oe,
228             ce;
229             
230         if (typeof eventName == 'object') {
231             o = eventName;
232             for (e in o) {
233                 oe = o[e];
234                 if (!me.filterOptRe.test(e)) {
235                     me.addListener(e, oe.fn || oe, oe.scope || o.scope, oe.fn ? oe : o);
236                 }
237             }
238         } else {
239             eventName = eventName.toLowerCase();
240             ce = me.events[eventName] || TRUE;
241             if (typeof ce == 'boolean') {
242                 me.events[eventName] = ce = new EXTUTIL.Event(me, eventName);
243             }
244             ce.addListener(fn, scope, typeof o == 'object' ? o : {});
245         }
246     },
247
248     /**
249      * Removes an event handler.
250      * @param {String}   eventName The type of event the handler was associated with.
251      * @param {Function} handler   The handler to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
252      * @param {Object}   scope     (optional) The scope originally specified for the handler.
253      */
254     removeListener : function(eventName, fn, scope){
255         var ce = this.events[eventName.toLowerCase()];
256         if (typeof ce == 'object') {
257             ce.removeListener(fn, scope);
258         }
259     },
260
261     /**
262      * Removes all listeners for this object
263      */
264     purgeListeners : function(){
265         var events = this.events,
266             evt,
267             key;
268         for(key in events){
269             evt = events[key];
270             if(typeof evt == 'object'){
271                 evt.clearListeners();
272             }
273         }
274     },
275
276     /**
277      * Adds the specified events to the list of events which this Observable may fire.
278      * @param {Object|String} o Either an object with event names as properties with a value of <code>true</code>
279      * or the first event name string if multiple event names are being passed as separate parameters.
280      * @param {string} Optional. Event name if multiple event names are being passed as separate parameters.
281      * Usage:<pre><code>
282 this.addEvents('storeloaded', 'storecleared');
283 </code></pre>
284      */
285     addEvents : function(o){
286         var me = this;
287         me.events = me.events || {};
288         if (typeof o == 'string') {
289             var a = arguments,
290                 i = a.length;
291             while(i--) {
292                 me.events[a[i]] = me.events[a[i]] || TRUE;
293             }
294         } else {
295             Ext.applyIf(me.events, o);
296         }
297     },
298
299     /**
300      * Checks to see if this object has any listeners for a specified event
301      * @param {String} eventName The name of the event to check for
302      * @return {Boolean} True if the event is being listened for, else false
303      */
304     hasListener : function(eventName){
305         var e = this.events[eventName.toLowerCase()];
306         return typeof e == 'object' && e.listeners.length > 0;
307     },
308
309     /**
310      * Suspend the firing of all events. (see {@link #resumeEvents})
311      * @param {Boolean} queueSuspended Pass as true to queue up suspended events to be fired
312      * after the {@link #resumeEvents} call instead of discarding all suspended events;
313      */
314     suspendEvents : function(queueSuspended){
315         this.eventsSuspended = TRUE;
316         if(queueSuspended && !this.eventQueue){
317             this.eventQueue = [];
318         }
319     },
320
321     /**
322      * Resume firing events. (see {@link #suspendEvents})
323      * If events were suspended using the <tt><b>queueSuspended</b></tt> parameter, then all
324      * events fired during event suspension will be sent to any listeners now.
325      */
326     resumeEvents : function(){
327         var me = this,
328             queued = me.eventQueue || [];
329         me.eventsSuspended = FALSE;
330         delete me.eventQueue;
331         EACH(queued, function(e) {
332             me.fireEvent.apply(me, e);
333         });
334     }
335 };
336
337 var OBSERVABLE = EXTUTIL.Observable.prototype;
338 /**
339  * Appends an event handler to this object (shorthand for {@link #addListener}.)
340  * @param {String}   eventName     The type of event to listen for
341  * @param {Function} handler       The method the event invokes
342  * @param {Object}   scope         (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.
343  * <b>If omitted, defaults to the object which fired the event.</b>
344  * @param {Object}   options       (optional) An object containing handler configuration.
345  * @method
346  */
347 OBSERVABLE.on = OBSERVABLE.addListener;
348 /**
349  * Removes an event handler (shorthand for {@link #removeListener}.)
350  * @param {String}   eventName     The type of event the handler was associated with.
351  * @param {Function} handler       The handler to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
352  * @param {Object}   scope         (optional) The scope originally specified for the handler.
353  * @method
354  */
355 OBSERVABLE.un = OBSERVABLE.removeListener;
356
357 /**
358  * Removes <b>all</b> added captures from the Observable.
359  * @param {Observable} o The Observable to release
360  * @static
361  */
362 EXTUTIL.Observable.releaseCapture = function(o){
363     o.fireEvent = OBSERVABLE.fireEvent;
364 };
365
366 function createTargeted(h, o, scope){
367     return function(){
368         if(o.target == arguments[0]){
369             h.apply(scope, Array.prototype.slice.call(arguments, 0));
370         }
371     };
372 };
373
374 function createBuffered(h, o, l, scope){
375     l.task = new EXTUTIL.DelayedTask();
376     return function(){
377         l.task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
378     };
379 };
380
381 function createSingle(h, e, fn, scope){
382     return function(){
383         e.removeListener(fn, scope);
384         return h.apply(scope, arguments);
385     };
386 };
387
388 function createDelayed(h, o, l, scope){
389     return function(){
390         var task = new EXTUTIL.DelayedTask(),
391             args = Array.prototype.slice.call(arguments, 0);
392         if(!l.tasks) {
393             l.tasks = [];
394         }
395         l.tasks.push(task);
396         task.delay(o.delay || 10, function(){
397             l.tasks.remove(task);
398             h.apply(scope, args);
399         }, scope);
400     };
401 };
402
403 EXTUTIL.Event = function(obj, name){
404     this.name = name;
405     this.obj = obj;
406     this.listeners = [];
407 };
408
409 EXTUTIL.Event.prototype = {
410     addListener : function(fn, scope, options){
411         var me = this,
412             l;
413         scope = scope || me.obj;
414         if(!me.isListening(fn, scope)){
415             l = me.createListener(fn, scope, options);
416             if(me.firing){ // if we are currently firing this event, don't disturb the listener loop
417                 me.listeners = me.listeners.slice(0);
418             }
419             me.listeners.push(l);
420         }
421     },
422
423     createListener: function(fn, scope, o){
424         o = o || {};
425         scope = scope || this.obj;
426         var l = {
427             fn: fn,
428             scope: scope,
429             options: o
430         }, h = fn;
431         if(o.target){
432             h = createTargeted(h, o, scope);
433         }
434         if(o.delay){
435             h = createDelayed(h, o, l, scope);
436         }
437         if(o.single){
438             h = createSingle(h, this, fn, scope);
439         }
440         if(o.buffer){
441             h = createBuffered(h, o, l, scope);
442         }
443         l.fireFn = h;
444         return l;
445     },
446
447     findListener : function(fn, scope){
448         var list = this.listeners,
449             i = list.length,
450             l;
451
452         scope = scope || this.obj;
453         while(i--){
454             l = list[i];
455             if(l){
456                 if(l.fn == fn && l.scope == scope){
457                     return i;
458                 }
459             }
460         }
461         return -1;
462     },
463
464     isListening : function(fn, scope){
465         return this.findListener(fn, scope) != -1;
466     },
467
468     removeListener : function(fn, scope){
469         var index,
470             l,
471             k,
472             me = this,
473             ret = FALSE;
474         if((index = me.findListener(fn, scope)) != -1){
475             if (me.firing) {
476                 me.listeners = me.listeners.slice(0);
477             }
478             l = me.listeners[index];
479             if(l.task) {
480                 l.task.cancel();
481                 delete l.task;
482             }
483             k = l.tasks && l.tasks.length;
484             if(k) {
485                 while(k--) {
486                     l.tasks[k].cancel();
487                 }
488                 delete l.tasks;
489             }
490             me.listeners.splice(index, 1);
491             ret = TRUE;
492         }
493         return ret;
494     },
495
496     // Iterate to stop any buffered/delayed events
497     clearListeners : function(){
498         var me = this,
499             l = me.listeners,
500             i = l.length;
501         while(i--) {
502             me.removeListener(l[i].fn, l[i].scope);
503         }
504     },
505
506     fire : function(){
507         var me = this,
508             listeners = me.listeners,
509             len = listeners.length,
510             i = 0,
511             l;
512
513         if(len > 0){
514             me.firing = TRUE;
515             var args = Array.prototype.slice.call(arguments, 0);
516             for (; i < len; i++) {
517                 l = listeners[i];
518                 if(l && l.fireFn.apply(l.scope || me.obj || window, args) === FALSE) {
519                     return (me.firing = FALSE);
520                 }
521             }
522         }
523         me.firing = FALSE;
524         return TRUE;
525     }
526
527 };
528 })();
529 /**
530  * @class Ext.DomHelper
531  * <p>The DomHelper class provides a layer of abstraction from DOM and transparently supports creating
532  * elements via DOM or using HTML fragments. It also has the ability to create HTML fragment templates
533  * from your DOM building code.</p>
534  *
535  * <p><b><u>DomHelper element specification object</u></b></p>
536  * <p>A specification object is used when creating elements. Attributes of this object
537  * are assumed to be element attributes, except for 4 special attributes:
538  * <div class="mdetail-params"><ul>
539  * <li><b><tt>tag</tt></b> : <div class="sub-desc">The tag name of the element</div></li>
540  * <li><b><tt>children</tt></b> : or <tt>cn</tt><div class="sub-desc">An array of the
541  * same kind of element definition objects to be created and appended. These can be nested
542  * as deep as you want.</div></li>
543  * <li><b><tt>cls</tt></b> : <div class="sub-desc">The class attribute of the element.
544  * This will end up being either the "class" attribute on a HTML fragment or className
545  * for a DOM node, depending on whether DomHelper is using fragments or DOM.</div></li>
546  * <li><b><tt>html</tt></b> : <div class="sub-desc">The innerHTML for the element</div></li>
547  * </ul></div></p>
548  *
549  * <p><b><u>Insertion methods</u></b></p>
550  * <p>Commonly used insertion methods:
551  * <div class="mdetail-params"><ul>
552  * <li><b><tt>{@link #append}</tt></b> : <div class="sub-desc"></div></li>
553  * <li><b><tt>{@link #insertBefore}</tt></b> : <div class="sub-desc"></div></li>
554  * <li><b><tt>{@link #insertAfter}</tt></b> : <div class="sub-desc"></div></li>
555  * <li><b><tt>{@link #overwrite}</tt></b> : <div class="sub-desc"></div></li>
556  * <li><b><tt>{@link #createTemplate}</tt></b> : <div class="sub-desc"></div></li>
557  * <li><b><tt>{@link #insertHtml}</tt></b> : <div class="sub-desc"></div></li>
558  * </ul></div></p>
559  *
560  * <p><b><u>Example</u></b></p>
561  * <p>This is an example, where an unordered list with 3 children items is appended to an existing
562  * element with id <tt>'my-div'</tt>:<br>
563  <pre><code>
564 var dh = Ext.DomHelper; // create shorthand alias
565 // specification object
566 var spec = {
567     id: 'my-ul',
568     tag: 'ul',
569     cls: 'my-list',
570     // append children after creating
571     children: [     // may also specify 'cn' instead of 'children'
572         {tag: 'li', id: 'item0', html: 'List Item 0'},
573         {tag: 'li', id: 'item1', html: 'List Item 1'},
574         {tag: 'li', id: 'item2', html: 'List Item 2'}
575     ]
576 };
577 var list = dh.append(
578     'my-div', // the context element 'my-div' can either be the id or the actual node
579     spec      // the specification object
580 );
581  </code></pre></p>
582  * <p>Element creation specification parameters in this class may also be passed as an Array of
583  * specification objects. This can be used to insert multiple sibling nodes into an existing
584  * container very efficiently. For example, to add more list items to the example above:<pre><code>
585 dh.append('my-ul', [
586     {tag: 'li', id: 'item3', html: 'List Item 3'},
587     {tag: 'li', id: 'item4', html: 'List Item 4'}
588 ]);
589  * </code></pre></p>
590  *
591  * <p><b><u>Templating</u></b></p>
592  * <p>The real power is in the built-in templating. Instead of creating or appending any elements,
593  * <tt>{@link #createTemplate}</tt> returns a Template object which can be used over and over to
594  * insert new elements. Revisiting the example above, we could utilize templating this time:
595  * <pre><code>
596 // create the node
597 var list = dh.append('my-div', {tag: 'ul', cls: 'my-list'});
598 // get template
599 var tpl = dh.createTemplate({tag: 'li', id: 'item{0}', html: 'List Item {0}'});
600
601 for(var i = 0; i < 5, i++){
602     tpl.append(list, [i]); // use template to append to the actual node
603 }
604  * </code></pre></p>
605  * <p>An example using a template:<pre><code>
606 var html = '<a id="{0}" href="{1}" class="nav">{2}</a>';
607
608 var tpl = new Ext.DomHelper.createTemplate(html);
609 tpl.append('blog-roll', ['link1', 'http://www.jackslocum.com/', "Jack&#39;s Site"]);
610 tpl.append('blog-roll', ['link2', 'http://www.dustindiaz.com/', "Dustin&#39;s Site"]);
611  * </code></pre></p>
612  *
613  * <p>The same example using named parameters:<pre><code>
614 var html = '<a id="{id}" href="{url}" class="nav">{text}</a>';
615
616 var tpl = new Ext.DomHelper.createTemplate(html);
617 tpl.append('blog-roll', {
618     id: 'link1',
619     url: 'http://www.jackslocum.com/',
620     text: "Jack&#39;s Site"
621 });
622 tpl.append('blog-roll', {
623     id: 'link2',
624     url: 'http://www.dustindiaz.com/',
625     text: "Dustin&#39;s Site"
626 });
627  * </code></pre></p>
628  *
629  * <p><b><u>Compiling Templates</u></b></p>
630  * <p>Templates are applied using regular expressions. The performance is great, but if
631  * you are adding a bunch of DOM elements using the same template, you can increase
632  * performance even further by {@link Ext.Template#compile "compiling"} the template.
633  * The way "{@link Ext.Template#compile compile()}" works is the template is parsed and
634  * broken up at the different variable points and a dynamic function is created and eval'ed.
635  * The generated function performs string concatenation of these parts and the passed
636  * variables instead of using regular expressions.
637  * <pre><code>
638 var html = '<a id="{id}" href="{url}" class="nav">{text}</a>';
639
640 var tpl = new Ext.DomHelper.createTemplate(html);
641 tpl.compile();
642
643 //... use template like normal
644  * </code></pre></p>
645  *
646  * <p><b><u>Performance Boost</u></b></p>
647  * <p>DomHelper will transparently create HTML fragments when it can. Using HTML fragments instead
648  * of DOM can significantly boost performance.</p>
649  * <p>Element creation specification parameters may also be strings. If {@link #useDom} is <tt>false</tt>,
650  * then the string is used as innerHTML. If {@link #useDom} is <tt>true</tt>, a string specification
651  * results in the creation of a text node. Usage:</p>
652  * <pre><code>
653 Ext.DomHelper.useDom = true; // force it to use DOM; reduces performance
654  * </code></pre>
655  * @singleton
656  */
657 Ext.DomHelper = function(){
658     var tempTableEl = null,
659         emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i,
660         tableRe = /^table|tbody|tr|td$/i,
661         confRe = /tag|children|cn|html$/i,
662         tableElRe = /td|tr|tbody/i,
663         cssRe = /([a-z0-9-]+)\s*:\s*([^;\s]+(?:\s*[^;\s]+)*);?/gi,
664         endRe = /end/i,
665         pub,
666         // kill repeat to save bytes
667         afterbegin = 'afterbegin',
668         afterend = 'afterend',
669         beforebegin = 'beforebegin',
670         beforeend = 'beforeend',
671         ts = '<table>',
672         te = '</table>',
673         tbs = ts+'<tbody>',
674         tbe = '</tbody>'+te,
675         trs = tbs + '<tr>',
676         tre = '</tr>'+tbe;
677
678     // private
679     function doInsert(el, o, returnElement, pos, sibling, append){
680         var newNode = pub.insertHtml(pos, Ext.getDom(el), createHtml(o));
681         return returnElement ? Ext.get(newNode, true) : newNode;
682     }
683
684     // build as innerHTML where available
685     function createHtml(o){
686         var b = '',
687             attr,
688             val,
689             key,
690             cn;
691
692         if(typeof o == "string"){
693             b = o;
694         } else if (Ext.isArray(o)) {
695             for (var i=0; i < o.length; i++) {
696                 if(o[i]) {
697                     b += createHtml(o[i]);
698                 }
699             };
700         } else {
701             b += '<' + (o.tag = o.tag || 'div');
702             for (attr in o) {
703                 val = o[attr];
704                 if(!confRe.test(attr)){
705                     if (typeof val == "object") {
706                         b += ' ' + attr + '="';
707                         for (key in val) {
708                             b += key + ':' + val[key] + ';';
709                         };
710                         b += '"';
711                     }else{
712                         b += ' ' + ({cls : 'class', htmlFor : 'for'}[attr] || attr) + '="' + val + '"';
713                     }
714                 }
715             };
716             // Now either just close the tag or try to add children and close the tag.
717             if (emptyTags.test(o.tag)) {
718                 b += '/>';
719             } else {
720                 b += '>';
721                 if ((cn = o.children || o.cn)) {
722                     b += createHtml(cn);
723                 } else if(o.html){
724                     b += o.html;
725                 }
726                 b += '</' + o.tag + '>';
727             }
728         }
729         return b;
730     }
731
732     function ieTable(depth, s, h, e){
733         tempTableEl.innerHTML = [s, h, e].join('');
734         var i = -1,
735             el = tempTableEl,
736             ns;
737         while(++i < depth){
738             el = el.firstChild;
739         }
740 //      If the result is multiple siblings, then encapsulate them into one fragment.
741         if(ns = el.nextSibling){
742             var df = document.createDocumentFragment();
743             while(el){
744                 ns = el.nextSibling;
745                 df.appendChild(el);
746                 el = ns;
747             }
748             el = df;
749         }
750         return el;
751     }
752
753     /**
754      * @ignore
755      * Nasty code for IE's broken table implementation
756      */
757     function insertIntoTable(tag, where, el, html) {
758         var node,
759             before;
760
761         tempTableEl = tempTableEl || document.createElement('div');
762
763         if(tag == 'td' && (where == afterbegin || where == beforeend) ||
764            !tableElRe.test(tag) && (where == beforebegin || where == afterend)) {
765             return;
766         }
767         before = where == beforebegin ? el :
768                  where == afterend ? el.nextSibling :
769                  where == afterbegin ? el.firstChild : null;
770
771         if (where == beforebegin || where == afterend) {
772             el = el.parentNode;
773         }
774
775         if (tag == 'td' || (tag == 'tr' && (where == beforeend || where == afterbegin))) {
776             node = ieTable(4, trs, html, tre);
777         } else if ((tag == 'tbody' && (where == beforeend || where == afterbegin)) ||
778                    (tag == 'tr' && (where == beforebegin || where == afterend))) {
779             node = ieTable(3, tbs, html, tbe);
780         } else {
781             node = ieTable(2, ts, html, te);
782         }
783         el.insertBefore(node, before);
784         return node;
785     }
786
787
788     pub = {
789         /**
790          * Returns the markup for the passed Element(s) config.
791          * @param {Object} o The DOM object spec (and children)
792          * @return {String}
793          */
794         markup : function(o){
795             return createHtml(o);
796         },
797
798         /**
799          * Applies a style specification to an element.
800          * @param {String/HTMLElement} el The element to apply styles to
801          * @param {String/Object/Function} styles A style specification string e.g. 'width:100px', or object in the form {width:'100px'}, or
802          * a function which returns such a specification.
803          */
804         applyStyles : function(el, styles){
805             if (styles) {
806                 var matches;
807
808                 el = Ext.fly(el);
809                 if (typeof styles == "function") {
810                     styles = styles.call();
811                 }
812                 if (typeof styles == "string") {
813                     /**
814                      * Since we're using the g flag on the regex, we need to set the lastIndex.
815                      * This automatically happens on some implementations, but not others, see:
816                      * http://stackoverflow.com/questions/2645273/javascript-regular-expression-literal-persists-between-function-calls
817                      * http://blog.stevenlevithan.com/archives/fixing-javascript-regexp
818                      */
819                     cssRe.lastIndex = 0;
820                     while ((matches = cssRe.exec(styles))) {
821                         el.setStyle(matches[1], matches[2]);
822                     }
823                 } else if (typeof styles == "object") {
824                     el.setStyle(styles);
825                 }
826             }
827         },
828
829         /**
830          * Inserts an HTML fragment into the DOM.
831          * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
832          * @param {HTMLElement} el The context element
833          * @param {String} html The HTML fragment
834          * @return {HTMLElement} The new node
835          */
836         insertHtml : function(where, el, html){
837             var hash = {},
838                 hashVal,
839                 setStart,
840                 range,
841                 frag,
842                 rangeEl,
843                 rs;
844
845             where = where.toLowerCase();
846             // add these here because they are used in both branches of the condition.
847             hash[beforebegin] = ['BeforeBegin', 'previousSibling'];
848             hash[afterend] = ['AfterEnd', 'nextSibling'];
849
850             if (el.insertAdjacentHTML) {
851                 if(tableRe.test(el.tagName) && (rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html))){
852                     return rs;
853                 }
854                 // add these two to the hash.
855                 hash[afterbegin] = ['AfterBegin', 'firstChild'];
856                 hash[beforeend] = ['BeforeEnd', 'lastChild'];
857                 if ((hashVal = hash[where])) {
858                     el.insertAdjacentHTML(hashVal[0], html);
859                     return el[hashVal[1]];
860                 }
861             } else {
862                 range = el.ownerDocument.createRange();
863                 setStart = 'setStart' + (endRe.test(where) ? 'After' : 'Before');
864                 if (hash[where]) {
865                     range[setStart](el);
866                     frag = range.createContextualFragment(html);
867                     el.parentNode.insertBefore(frag, where == beforebegin ? el : el.nextSibling);
868                     return el[(where == beforebegin ? 'previous' : 'next') + 'Sibling'];
869                 } else {
870                     rangeEl = (where == afterbegin ? 'first' : 'last') + 'Child';
871                     if (el.firstChild) {
872                         range[setStart](el[rangeEl]);
873                         frag = range.createContextualFragment(html);
874                         if(where == afterbegin){
875                             el.insertBefore(frag, el.firstChild);
876                         }else{
877                             el.appendChild(frag);
878                         }
879                     } else {
880                         el.innerHTML = html;
881                     }
882                     return el[rangeEl];
883                 }
884             }
885             throw 'Illegal insertion point -> "' + where + '"';
886         },
887
888         /**
889          * Creates new DOM element(s) and inserts them before el.
890          * @param {Mixed} el The context element
891          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
892          * @param {Boolean} returnElement (optional) true to return a Ext.Element
893          * @return {HTMLElement/Ext.Element} The new node
894          */
895         insertBefore : function(el, o, returnElement){
896             return doInsert(el, o, returnElement, beforebegin);
897         },
898
899         /**
900          * Creates new DOM element(s) and inserts them after el.
901          * @param {Mixed} el The context element
902          * @param {Object} o The DOM object spec (and children)
903          * @param {Boolean} returnElement (optional) true to return a Ext.Element
904          * @return {HTMLElement/Ext.Element} The new node
905          */
906         insertAfter : function(el, o, returnElement){
907             return doInsert(el, o, returnElement, afterend, 'nextSibling');
908         },
909
910         /**
911          * Creates new DOM element(s) and inserts them as the first child of el.
912          * @param {Mixed} el The context element
913          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
914          * @param {Boolean} returnElement (optional) true to return a Ext.Element
915          * @return {HTMLElement/Ext.Element} The new node
916          */
917         insertFirst : function(el, o, returnElement){
918             return doInsert(el, o, returnElement, afterbegin, 'firstChild');
919         },
920
921         /**
922          * Creates new DOM element(s) and appends them to el.
923          * @param {Mixed} el The context element
924          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
925          * @param {Boolean} returnElement (optional) true to return a Ext.Element
926          * @return {HTMLElement/Ext.Element} The new node
927          */
928         append : function(el, o, returnElement){
929             return doInsert(el, o, returnElement, beforeend, '', true);
930         },
931
932         /**
933          * Creates new DOM element(s) and overwrites the contents of el with them.
934          * @param {Mixed} el The context element
935          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
936          * @param {Boolean} returnElement (optional) true to return a Ext.Element
937          * @return {HTMLElement/Ext.Element} The new node
938          */
939         overwrite : function(el, o, returnElement){
940             el = Ext.getDom(el);
941             el.innerHTML = createHtml(o);
942             return returnElement ? Ext.get(el.firstChild) : el.firstChild;
943         },
944
945         createHtml : createHtml
946     };
947     return pub;
948 }();
949 /**
950  * @class Ext.Template
951  * <p>Represents an HTML fragment template. Templates may be {@link #compile precompiled}
952  * for greater performance.</p>
953  * <p>For example usage {@link #Template see the constructor}.</p>
954  *
955  * @constructor
956  * An instance of this class may be created by passing to the constructor either
957  * a single argument, or multiple arguments:
958  * <div class="mdetail-params"><ul>
959  * <li><b>single argument</b> : String/Array
960  * <div class="sub-desc">
961  * The single argument may be either a String or an Array:<ul>
962  * <li><tt>String</tt> : </li><pre><code>
963 var t = new Ext.Template("&lt;div>Hello {0}.&lt;/div>");
964 t.{@link #append}('some-element', ['foo']);
965  * </code></pre>
966  * <li><tt>Array</tt> : </li>
967  * An Array will be combined with <code>join('')</code>.
968 <pre><code>
969 var t = new Ext.Template([
970     '&lt;div name="{id}"&gt;',
971         '&lt;span class="{cls}"&gt;{name:trim} {value:ellipsis(10)}&lt;/span&gt;',
972     '&lt;/div&gt;',
973 ]);
974 t.{@link #compile}();
975 t.{@link #append}('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
976 </code></pre>
977  * </ul></div></li>
978  * <li><b>multiple arguments</b> : String, Object, Array, ...
979  * <div class="sub-desc">
980  * Multiple arguments will be combined with <code>join('')</code>.
981  * <pre><code>
982 var t = new Ext.Template(
983     '&lt;div name="{id}"&gt;',
984         '&lt;span class="{cls}"&gt;{name} {value}&lt;/span&gt;',
985     '&lt;/div&gt;',
986     // a configuration object:
987     {
988         compiled: true,      // {@link #compile} immediately
989         disableFormats: true // See Notes below.
990     }
991 );
992  * </code></pre>
993  * <p><b>Notes</b>:</p>
994  * <div class="mdetail-params"><ul>
995  * <li>Formatting and <code>disableFormats</code> are not applicable for Ext Core.</li>
996  * <li>For a list of available format functions, see {@link Ext.util.Format}.</li>
997  * <li><code>disableFormats</code> reduces <code>{@link #apply}</code> time
998  * when no formatting is required.</li>
999  * </ul></div>
1000  * </div></li>
1001  * </ul></div>
1002  * @param {Mixed} config
1003  */
1004 Ext.Template = function(html){
1005     var me = this,
1006         a = arguments,
1007         buf = [],
1008         v;
1009
1010     if (Ext.isArray(html)) {
1011         html = html.join("");
1012     } else if (a.length > 1) {
1013         for(var i = 0, len = a.length; i < len; i++){
1014             v = a[i];
1015             if(typeof v == 'object'){
1016                 Ext.apply(me, v);
1017             } else {
1018                 buf.push(v);
1019             }
1020         };
1021         html = buf.join('');
1022     }
1023
1024     /**@private*/
1025     me.html = html;
1026     /**
1027      * @cfg {Boolean} compiled Specify <tt>true</tt> to compile the template
1028      * immediately (see <code>{@link #compile}</code>).
1029      * Defaults to <tt>false</tt>.
1030      */
1031     if (me.compiled) {
1032         me.compile();
1033     }
1034 };
1035 Ext.Template.prototype = {
1036     /**
1037      * @cfg {RegExp} re The regular expression used to match template variables.
1038      * Defaults to:<pre><code>
1039      * re : /\{([\w-]+)\}/g                                     // for Ext Core
1040      * re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g      // for Ext JS
1041      * </code></pre>
1042      */
1043     re : /\{([\w-]+)\}/g,
1044     /**
1045      * See <code>{@link #re}</code>.
1046      * @type RegExp
1047      * @property re
1048      */
1049
1050     /**
1051      * Returns an HTML fragment of this template with the specified <code>values</code> applied.
1052      * @param {Object/Array} values
1053      * The template values. Can be an array if the params are numeric (i.e. <code>{0}</code>)
1054      * or an object (i.e. <code>{foo: 'bar'}</code>).
1055      * @return {String} The HTML fragment
1056      */
1057     applyTemplate : function(values){
1058         var me = this;
1059
1060         return me.compiled ?
1061                 me.compiled(values) :
1062                 me.html.replace(me.re, function(m, name){
1063                     return values[name] !== undefined ? values[name] : "";
1064                 });
1065     },
1066
1067     /**
1068      * Sets the HTML used as the template and optionally compiles it.
1069      * @param {String} html
1070      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
1071      * @return {Ext.Template} this
1072      */
1073     set : function(html, compile){
1074         var me = this;
1075         me.html = html;
1076         me.compiled = null;
1077         return compile ? me.compile() : me;
1078     },
1079
1080     /**
1081      * Compiles the template into an internal function, eliminating the RegEx overhead.
1082      * @return {Ext.Template} this
1083      */
1084     compile : function(){
1085         var me = this,
1086             sep = Ext.isGecko ? "+" : ",";
1087
1088         function fn(m, name){
1089             name = "values['" + name + "']";
1090             return "'"+ sep + '(' + name + " == undefined ? '' : " + name + ')' + sep + "'";
1091         }
1092
1093         eval("this.compiled = function(values){ return " + (Ext.isGecko ? "'" : "['") +
1094              me.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
1095              (Ext.isGecko ?  "';};" : "'].join('');};"));
1096         return me;
1097     },
1098
1099     /**
1100      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
1101      * @param {Mixed} el The context element
1102      * @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'})
1103      * @param {Boolean} returnElement (optional) true to return a Ext.Element (defaults to undefined)
1104      * @return {HTMLElement/Ext.Element} The new node or Element
1105      */
1106     insertFirst: function(el, values, returnElement){
1107         return this.doInsert('afterBegin', el, values, returnElement);
1108     },
1109
1110     /**
1111      * Applies the supplied values to the template and inserts the new node(s) before el.
1112      * @param {Mixed} el The context element
1113      * @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'})
1114      * @param {Boolean} returnElement (optional) true to return a Ext.Element (defaults to undefined)
1115      * @return {HTMLElement/Ext.Element} The new node or Element
1116      */
1117     insertBefore: function(el, values, returnElement){
1118         return this.doInsert('beforeBegin', el, values, returnElement);
1119     },
1120
1121     /**
1122      * Applies the supplied values to the template and inserts the new node(s) after el.
1123      * @param {Mixed} el The context element
1124      * @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'})
1125      * @param {Boolean} returnElement (optional) true to return a Ext.Element (defaults to undefined)
1126      * @return {HTMLElement/Ext.Element} The new node or Element
1127      */
1128     insertAfter : function(el, values, returnElement){
1129         return this.doInsert('afterEnd', el, values, returnElement);
1130     },
1131
1132     /**
1133      * Applies the supplied <code>values</code> to the template and appends
1134      * the new node(s) to the specified <code>el</code>.
1135      * <p>For example usage {@link #Template see the constructor}.</p>
1136      * @param {Mixed} el The context element
1137      * @param {Object/Array} values
1138      * The template values. Can be an array if the params are numeric (i.e. <code>{0}</code>)
1139      * or an object (i.e. <code>{foo: 'bar'}</code>).
1140      * @param {Boolean} returnElement (optional) true to return an Ext.Element (defaults to undefined)
1141      * @return {HTMLElement/Ext.Element} The new node or Element
1142      */
1143     append : function(el, values, returnElement){
1144         return this.doInsert('beforeEnd', el, values, returnElement);
1145     },
1146
1147     doInsert : function(where, el, values, returnEl){
1148         el = Ext.getDom(el);
1149         var newNode = Ext.DomHelper.insertHtml(where, el, this.applyTemplate(values));
1150         return returnEl ? Ext.get(newNode, true) : newNode;
1151     },
1152
1153     /**
1154      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
1155      * @param {Mixed} el The context element
1156      * @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'})
1157      * @param {Boolean} returnElement (optional) true to return a Ext.Element (defaults to undefined)
1158      * @return {HTMLElement/Ext.Element} The new node or Element
1159      */
1160     overwrite : function(el, values, returnElement){
1161         el = Ext.getDom(el);
1162         el.innerHTML = this.applyTemplate(values);
1163         return returnElement ? Ext.get(el.firstChild, true) : el.firstChild;
1164     }
1165 };
1166 /**
1167  * Alias for {@link #applyTemplate}
1168  * Returns an HTML fragment of this template with the specified <code>values</code> applied.
1169  * @param {Object/Array} values
1170  * The template values. Can be an array if the params are numeric (i.e. <code>{0}</code>)
1171  * or an object (i.e. <code>{foo: 'bar'}</code>).
1172  * @return {String} The HTML fragment
1173  * @member Ext.Template
1174  * @method apply
1175  */
1176 Ext.Template.prototype.apply = Ext.Template.prototype.applyTemplate;
1177
1178 /**
1179  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
1180  * @param {String/HTMLElement} el A DOM element or its id
1181  * @param {Object} config A configuration object
1182  * @return {Ext.Template} The created template
1183  * @static
1184  */
1185 Ext.Template.from = function(el, config){
1186     el = Ext.getDom(el);
1187     return new Ext.Template(el.value || el.innerHTML, config || '');
1188 };
1189 /*
1190  * This is code is also distributed under MIT license for use
1191  * with jQuery and prototype JavaScript libraries.
1192  */
1193 /**
1194  * @class Ext.DomQuery
1195 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
1196 <p>
1197 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/#selectors">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
1198
1199 <p>
1200 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
1201 </p>
1202 <h4>Element Selectors:</h4>
1203 <ul class="list">
1204     <li> <b>*</b> any element</li>
1205     <li> <b>E</b> an element with the tag E</li>
1206     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
1207     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
1208     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
1209     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
1210 </ul>
1211 <h4>Attribute Selectors:</h4>
1212 <p>The use of &#64; and quotes are optional. For example, div[&#64;foo='bar'] is also a valid attribute selector.</p>
1213 <ul class="list">
1214     <li> <b>E[foo]</b> has an attribute "foo"</li>
1215     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
1216     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
1217     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
1218     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
1219     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
1220     <li> <b>E[foo!=bar]</b> attribute "foo" does not equal "bar"</li>
1221 </ul>
1222 <h4>Pseudo Classes:</h4>
1223 <ul class="list">
1224     <li> <b>E:first-child</b> E is the first child of its parent</li>
1225     <li> <b>E:last-child</b> E is the last child of its parent</li>
1226     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
1227     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
1228     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
1229     <li> <b>E:only-child</b> E is the only child of its parent</li>
1230     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
1231     <li> <b>E:first</b> the first E in the resultset</li>
1232     <li> <b>E:last</b> the last E in the resultset</li>
1233     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
1234     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
1235     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
1236     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
1237     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
1238     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
1239     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
1240     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
1241     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
1242     <li> <b>E:any(S1|S2|S2)</b> an E element which matches any of the simple selectors S1, S2 or S3//\\</li>
1243 </ul>
1244 <h4>CSS Value Selectors:</h4>
1245 <ul class="list">
1246     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
1247     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
1248     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
1249     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
1250     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
1251     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
1252 </ul>
1253  * @singleton
1254  */
1255 Ext.DomQuery = function(){
1256     var cache = {}, 
1257         simpleCache = {}, 
1258         valueCache = {},
1259         nonSpace = /\S/,
1260         trimRe = /^\s+|\s+$/g,
1261         tplRe = /\{(\d+)\}/g,
1262         modeRe = /^(\s?[\/>+~]\s?|\s|$)/,
1263         tagTokenRe = /^(#)?([\w-\*]+)/,
1264         nthRe = /(\d*)n\+?(\d*)/, 
1265         nthRe2 = /\D/,
1266         // This is for IE MSXML which does not support expandos.
1267         // IE runs the same speed using setAttribute, however FF slows way down
1268         // and Safari completely fails so they need to continue to use expandos.
1269         isIE = window.ActiveXObject ? true : false,
1270         key = 30803;
1271     
1272     // this eval is stop the compressor from
1273     // renaming the variable to something shorter
1274     eval("var batch = 30803;");         
1275
1276     // Retrieve the child node from a particular
1277     // parent at the specified index.
1278     function child(parent, index){
1279         var i = 0,
1280             n = parent.firstChild;
1281         while(n){
1282             if(n.nodeType == 1){
1283                if(++i == index){
1284                    return n;
1285                }
1286             }
1287             n = n.nextSibling;
1288         }
1289         return null;
1290     }
1291
1292     // retrieve the next element node
1293     function next(n){   
1294         while((n = n.nextSibling) && n.nodeType != 1);
1295         return n;
1296     }
1297
1298     // retrieve the previous element node 
1299     function prev(n){
1300         while((n = n.previousSibling) && n.nodeType != 1);
1301         return n;
1302     }
1303
1304     // Mark each child node with a nodeIndex skipping and
1305     // removing empty text nodes.
1306     function children(parent){
1307         var n = parent.firstChild,
1308             nodeIndex = -1,
1309             nextNode;
1310         while(n){
1311             nextNode = n.nextSibling;
1312             // clean worthless empty nodes.
1313             if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
1314                 parent.removeChild(n);
1315             }else{
1316                 // add an expando nodeIndex
1317                 n.nodeIndex = ++nodeIndex;
1318             }
1319             n = nextNode;
1320         }
1321         return this;
1322     }
1323
1324
1325     // nodeSet - array of nodes
1326     // cls - CSS Class
1327     function byClassName(nodeSet, cls){
1328         if(!cls){
1329             return nodeSet;
1330         }
1331         var result = [], ri = -1;
1332         for(var i = 0, ci; ci = nodeSet[i]; i++){
1333             if((' '+ci.className+' ').indexOf(cls) != -1){
1334                 result[++ri] = ci;
1335             }
1336         }
1337         return result;
1338     };
1339
1340     function attrValue(n, attr){
1341         // if its an array, use the first node.
1342         if(!n.tagName && typeof n.length != "undefined"){
1343             n = n[0];
1344         }
1345         if(!n){
1346             return null;
1347         }
1348
1349         if(attr == "for"){
1350             return n.htmlFor;
1351         }
1352         if(attr == "class" || attr == "className"){
1353             return n.className;
1354         }
1355         return n.getAttribute(attr) || n[attr];
1356
1357     };
1358
1359
1360     // ns - nodes
1361     // mode - false, /, >, +, ~
1362     // tagName - defaults to "*"
1363     function getNodes(ns, mode, tagName){
1364         var result = [], ri = -1, cs;
1365         if(!ns){
1366             return result;
1367         }
1368         tagName = tagName || "*";
1369         // convert to array
1370         if(typeof ns.getElementsByTagName != "undefined"){
1371             ns = [ns];
1372         }
1373         
1374         // no mode specified, grab all elements by tagName
1375         // at any depth
1376         if(!mode){
1377             for(var i = 0, ni; ni = ns[i]; i++){
1378                 cs = ni.getElementsByTagName(tagName);
1379                 for(var j = 0, ci; ci = cs[j]; j++){
1380                     result[++ri] = ci;
1381                 }
1382             }
1383         // Direct Child mode (/ or >)
1384         // E > F or E/F all direct children elements of E that have the tag     
1385         } else if(mode == "/" || mode == ">"){
1386             var utag = tagName.toUpperCase();
1387             for(var i = 0, ni, cn; ni = ns[i]; i++){
1388                 cn = ni.childNodes;
1389                 for(var j = 0, cj; cj = cn[j]; j++){
1390                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
1391                         result[++ri] = cj;
1392                     }
1393                 }
1394             }
1395         // Immediately Preceding mode (+)
1396         // E + F all elements with the tag F that are immediately preceded by an element with the tag E
1397         }else if(mode == "+"){
1398             var utag = tagName.toUpperCase();
1399             for(var i = 0, n; n = ns[i]; i++){
1400                 while((n = n.nextSibling) && n.nodeType != 1);
1401                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
1402                     result[++ri] = n;
1403                 }
1404             }
1405         // Sibling mode (~)
1406         // E ~ F all elements with the tag F that are preceded by a sibling element with the tag E
1407         }else if(mode == "~"){
1408             var utag = tagName.toUpperCase();
1409             for(var i = 0, n; n = ns[i]; i++){
1410                 while((n = n.nextSibling)){
1411                     if (n.nodeName == utag || n.nodeName == tagName || tagName == '*'){
1412                         result[++ri] = n;
1413                     }
1414                 }
1415             }
1416         }
1417         return result;
1418     }
1419
1420     function concat(a, b){
1421         if(b.slice){
1422             return a.concat(b);
1423         }
1424         for(var i = 0, l = b.length; i < l; i++){
1425             a[a.length] = b[i];
1426         }
1427         return a;
1428     }
1429
1430     function byTag(cs, tagName){
1431         if(cs.tagName || cs == document){
1432             cs = [cs];
1433         }
1434         if(!tagName){
1435             return cs;
1436         }
1437         var result = [], ri = -1;
1438         tagName = tagName.toLowerCase();
1439         for(var i = 0, ci; ci = cs[i]; i++){
1440             if(ci.nodeType == 1 && ci.tagName.toLowerCase() == tagName){
1441                 result[++ri] = ci;
1442             }
1443         }
1444         return result;
1445     }
1446
1447     function byId(cs, id){
1448         if(cs.tagName || cs == document){
1449             cs = [cs];
1450         }
1451         if(!id){
1452             return cs;
1453         }
1454         var result = [], ri = -1;
1455         for(var i = 0, ci; ci = cs[i]; i++){
1456             if(ci && ci.id == id){
1457                 result[++ri] = ci;
1458                 return result;
1459             }
1460         }
1461         return result;
1462     }
1463
1464     // operators are =, !=, ^=, $=, *=, %=, |= and ~=
1465     // custom can be "{"
1466     function byAttribute(cs, attr, value, op, custom){
1467         var result = [], 
1468             ri = -1, 
1469             useGetStyle = custom == "{",            
1470             fn = Ext.DomQuery.operators[op],        
1471             a,
1472             xml,
1473             hasXml;
1474             
1475         for(var i = 0, ci; ci = cs[i]; i++){
1476             // skip non-element nodes.
1477             if(ci.nodeType != 1){
1478                 continue;
1479             }
1480             // only need to do this for the first node
1481             if(!hasXml){
1482                 xml = Ext.DomQuery.isXml(ci);
1483                 hasXml = true;
1484             }
1485             
1486             // we only need to change the property names if we're dealing with html nodes, not XML
1487             if(!xml){
1488                 if(useGetStyle){
1489                     a = Ext.DomQuery.getStyle(ci, attr);
1490                 } else if (attr == "class" || attr == "className"){
1491                     a = ci.className;
1492                 } else if (attr == "for"){
1493                     a = ci.htmlFor;
1494                 } else if (attr == "href"){
1495                     // getAttribute href bug
1496                     // http://www.glennjones.net/Post/809/getAttributehrefbug.htm
1497                     a = ci.getAttribute("href", 2);
1498                 } else{
1499                     a = ci.getAttribute(attr);
1500                 }
1501             }else{
1502                 a = ci.getAttribute(attr);
1503             }
1504             if((fn && fn(a, value)) || (!fn && a)){
1505                 result[++ri] = ci;
1506             }
1507         }
1508         return result;
1509     }
1510
1511     function byPseudo(cs, name, value){
1512         return Ext.DomQuery.pseudos[name](cs, value);
1513     }
1514
1515     function nodupIEXml(cs){
1516         var d = ++key, 
1517             r;
1518         cs[0].setAttribute("_nodup", d);
1519         r = [cs[0]];
1520         for(var i = 1, len = cs.length; i < len; i++){
1521             var c = cs[i];
1522             if(!c.getAttribute("_nodup") != d){
1523                 c.setAttribute("_nodup", d);
1524                 r[r.length] = c;
1525             }
1526         }
1527         for(var i = 0, len = cs.length; i < len; i++){
1528             cs[i].removeAttribute("_nodup");
1529         }
1530         return r;
1531     }
1532
1533     function nodup(cs){
1534         if(!cs){
1535             return [];
1536         }
1537         var len = cs.length, c, i, r = cs, cj, ri = -1;
1538         if(!len || typeof cs.nodeType != "undefined" || len == 1){
1539             return cs;
1540         }
1541         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
1542             return nodupIEXml(cs);
1543         }
1544         var d = ++key;
1545         cs[0]._nodup = d;
1546         for(i = 1; c = cs[i]; i++){
1547             if(c._nodup != d){
1548                 c._nodup = d;
1549             }else{
1550                 r = [];
1551                 for(var j = 0; j < i; j++){
1552                     r[++ri] = cs[j];
1553                 }
1554                 for(j = i+1; cj = cs[j]; j++){
1555                     if(cj._nodup != d){
1556                         cj._nodup = d;
1557                         r[++ri] = cj;
1558                     }
1559                 }
1560                 return r;
1561             }
1562         }
1563         return r;
1564     }
1565
1566     function quickDiffIEXml(c1, c2){
1567         var d = ++key,
1568             r = [];
1569         for(var i = 0, len = c1.length; i < len; i++){
1570             c1[i].setAttribute("_qdiff", d);
1571         }        
1572         for(var i = 0, len = c2.length; i < len; i++){
1573             if(c2[i].getAttribute("_qdiff") != d){
1574                 r[r.length] = c2[i];
1575             }
1576         }
1577         for(var i = 0, len = c1.length; i < len; i++){
1578            c1[i].removeAttribute("_qdiff");
1579         }
1580         return r;
1581     }
1582
1583     function quickDiff(c1, c2){
1584         var len1 = c1.length,
1585                 d = ++key,
1586                 r = [];
1587         if(!len1){
1588             return c2;
1589         }
1590         if(isIE && typeof c1[0].selectSingleNode != "undefined"){
1591             return quickDiffIEXml(c1, c2);
1592         }        
1593         for(var i = 0; i < len1; i++){
1594             c1[i]._qdiff = d;
1595         }        
1596         for(var i = 0, len = c2.length; i < len; i++){
1597             if(c2[i]._qdiff != d){
1598                 r[r.length] = c2[i];
1599             }
1600         }
1601         return r;
1602     }
1603
1604     function quickId(ns, mode, root, id){
1605         if(ns == root){
1606            var d = root.ownerDocument || root;
1607            return d.getElementById(id);
1608         }
1609         ns = getNodes(ns, mode, "*");
1610         return byId(ns, id);
1611     }
1612
1613     return {
1614         getStyle : function(el, name){
1615             return Ext.fly(el).getStyle(name);
1616         },
1617         /**
1618          * Compiles a selector/xpath query into a reusable function. The returned function
1619          * takes one parameter "root" (optional), which is the context node from where the query should start.
1620          * @param {String} selector The selector/xpath query
1621          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
1622          * @return {Function}
1623          */
1624         compile : function(path, type){
1625             type = type || "select";
1626
1627             // setup fn preamble
1628             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"],
1629                         mode,           
1630                         lastPath,
1631                 matchers = Ext.DomQuery.matchers,
1632                 matchersLn = matchers.length,
1633                 modeMatch,
1634                 // accept leading mode switch
1635                 lmode = path.match(modeRe);
1636             
1637             if(lmode && lmode[1]){
1638                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
1639                 path = path.replace(lmode[1], "");
1640             }
1641             
1642             // strip leading slashes
1643             while(path.substr(0, 1)=="/"){
1644                 path = path.substr(1);
1645             }
1646
1647             while(path && lastPath != path){
1648                 lastPath = path;
1649                 var tokenMatch = path.match(tagTokenRe);
1650                 if(type == "select"){
1651                     if(tokenMatch){
1652                         // ID Selector
1653                         if(tokenMatch[1] == "#"){
1654                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tokenMatch[2]+'");';                 
1655                         }else{
1656                             fn[fn.length] = 'n = getNodes(n, mode, "'+tokenMatch[2]+'");';
1657                         }
1658                         path = path.replace(tokenMatch[0], "");
1659                     }else if(path.substr(0, 1) != '@'){
1660                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
1661                     }
1662                 // type of "simple"
1663                 }else{
1664                     if(tokenMatch){
1665                         if(tokenMatch[1] == "#"){
1666                             fn[fn.length] = 'n = byId(n, "'+tokenMatch[2]+'");';
1667                         }else{
1668                             fn[fn.length] = 'n = byTag(n, "'+tokenMatch[2]+'");';
1669                         }
1670                         path = path.replace(tokenMatch[0], "");
1671                     }
1672                 }
1673                 while(!(modeMatch = path.match(modeRe))){
1674                     var matched = false;
1675                     for(var j = 0; j < matchersLn; j++){
1676                         var t = matchers[j];
1677                         var m = path.match(t.re);
1678                         if(m){
1679                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
1680                                 return m[i];
1681                             });
1682                             path = path.replace(m[0], "");
1683                             matched = true;
1684                             break;
1685                         }
1686                     }
1687                     // prevent infinite loop on bad selector
1688                     if(!matched){
1689                         throw 'Error parsing selector, parsing failed at "' + path + '"';
1690                     }
1691                 }
1692                 if(modeMatch[1]){
1693                     fn[fn.length] = 'mode="'+modeMatch[1].replace(trimRe, "")+'";';
1694                     path = path.replace(modeMatch[1], "");
1695                 }
1696             }
1697             // close fn out
1698             fn[fn.length] = "return nodup(n);\n}";
1699             
1700             // eval fn and return it
1701             eval(fn.join(""));
1702             return f;
1703         },
1704
1705         /**
1706          * Selects a group of elements.
1707          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
1708          * @param {Node/String} root (optional) The start of the query (defaults to document).
1709          * @return {Array} An Array of DOM elements which match the selector. If there are
1710          * no matches, and empty Array is returned.
1711          */
1712         jsSelect: function(path, root, type){
1713             // set root to doc if not specified.
1714             root = root || document;
1715             
1716             if(typeof root == "string"){
1717                 root = document.getElementById(root);
1718             }
1719             var paths = path.split(","),
1720                 results = [];
1721                 
1722             // loop over each selector
1723             for(var i = 0, len = paths.length; i < len; i++){           
1724                 var subPath = paths[i].replace(trimRe, "");
1725                 // compile and place in cache
1726                 if(!cache[subPath]){
1727                     cache[subPath] = Ext.DomQuery.compile(subPath);
1728                     if(!cache[subPath]){
1729                         throw subPath + " is not a valid selector";
1730                     }
1731                 }
1732                 var result = cache[subPath](root);
1733                 if(result && result != document){
1734                     results = results.concat(result);
1735                 }
1736             }
1737             
1738             // if there were multiple selectors, make sure dups
1739             // are eliminated
1740             if(paths.length > 1){
1741                 return nodup(results);
1742             }
1743             return results;
1744         },
1745         isXml: function(el) {
1746             var docEl = (el ? el.ownerDocument || el : 0).documentElement;
1747             return docEl ? docEl.nodeName !== "HTML" : false;
1748         },
1749         select : document.querySelectorAll ? function(path, root, type) {
1750             root = root || document;
1751             if (!Ext.DomQuery.isXml(root)) {
1752                 try {
1753                     var cs = root.querySelectorAll(path);
1754                     return Ext.toArray(cs);
1755                 }
1756                 catch (ex) {}           
1757             }       
1758             return Ext.DomQuery.jsSelect.call(this, path, root, type);
1759         } : function(path, root, type) {
1760             return Ext.DomQuery.jsSelect.call(this, path, root, type);
1761         },
1762
1763         /**
1764          * Selects a single element.
1765          * @param {String} selector The selector/xpath query
1766          * @param {Node} root (optional) The start of the query (defaults to document).
1767          * @return {Element} The DOM element which matched the selector.
1768          */
1769         selectNode : function(path, root){
1770             return Ext.DomQuery.select(path, root)[0];
1771         },
1772
1773         /**
1774          * Selects the value of a node, optionally replacing null with the defaultValue.
1775          * @param {String} selector The selector/xpath query
1776          * @param {Node} root (optional) The start of the query (defaults to document).
1777          * @param {String} defaultValue
1778          * @return {String}
1779          */
1780         selectValue : function(path, root, defaultValue){
1781             path = path.replace(trimRe, "");
1782             if(!valueCache[path]){
1783                 valueCache[path] = Ext.DomQuery.compile(path, "select");
1784             }
1785             var n = valueCache[path](root), v;
1786             n = n[0] ? n[0] : n;
1787                     
1788             // overcome a limitation of maximum textnode size
1789             // Rumored to potentially crash IE6 but has not been confirmed.
1790             // http://reference.sitepoint.com/javascript/Node/normalize
1791             // https://developer.mozilla.org/En/DOM/Node.normalize          
1792             if (typeof n.normalize == 'function') n.normalize();
1793             
1794             v = (n && n.firstChild ? n.firstChild.nodeValue : null);
1795             return ((v === null||v === undefined||v==='') ? defaultValue : v);
1796         },
1797
1798         /**
1799          * Selects the value of a node, parsing integers and floats. Returns the defaultValue, or 0 if none is specified.
1800          * @param {String} selector The selector/xpath query
1801          * @param {Node} root (optional) The start of the query (defaults to document).
1802          * @param {Number} defaultValue
1803          * @return {Number}
1804          */
1805         selectNumber : function(path, root, defaultValue){
1806             var v = Ext.DomQuery.selectValue(path, root, defaultValue || 0);
1807             return parseFloat(v);
1808         },
1809
1810         /**
1811          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
1812          * @param {String/HTMLElement/Array} el An element id, element or array of elements
1813          * @param {String} selector The simple selector to test
1814          * @return {Boolean}
1815          */
1816         is : function(el, ss){
1817             if(typeof el == "string"){
1818                 el = document.getElementById(el);
1819             }
1820             var isArray = Ext.isArray(el),
1821                 result = Ext.DomQuery.filter(isArray ? el : [el], ss);
1822             return isArray ? (result.length == el.length) : (result.length > 0);
1823         },
1824
1825         /**
1826          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
1827          * @param {Array} el An array of elements to filter
1828          * @param {String} selector The simple selector to test
1829          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
1830          * the selector instead of the ones that match
1831          * @return {Array} An Array of DOM elements which match the selector. If there are
1832          * no matches, and empty Array is returned.
1833          */
1834         filter : function(els, ss, nonMatches){
1835             ss = ss.replace(trimRe, "");
1836             if(!simpleCache[ss]){
1837                 simpleCache[ss] = Ext.DomQuery.compile(ss, "simple");
1838             }
1839             var result = simpleCache[ss](els);
1840             return nonMatches ? quickDiff(result, els) : result;
1841         },
1842
1843         /**
1844          * Collection of matching regular expressions and code snippets.
1845          * Each capture group within () will be replace the {} in the select
1846          * statement as specified by their index.
1847          */
1848         matchers : [{
1849                 re: /^\.([\w-]+)/,
1850                 select: 'n = byClassName(n, " {1} ");'
1851             }, {
1852                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
1853                 select: 'n = byPseudo(n, "{1}", "{2}");'
1854             },{
1855                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
1856                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
1857             }, {
1858                 re: /^#([\w-]+)/,
1859                 select: 'n = byId(n, "{1}");'
1860             },{
1861                 re: /^@([\w-]+)/,
1862                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
1863             }
1864         ],
1865
1866         /**
1867          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
1868          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
1869          */
1870         operators : {
1871             "=" : function(a, v){
1872                 return a == v;
1873             },
1874             "!=" : function(a, v){
1875                 return a != v;
1876             },
1877             "^=" : function(a, v){
1878                 return a && a.substr(0, v.length) == v;
1879             },
1880             "$=" : function(a, v){
1881                 return a && a.substr(a.length-v.length) == v;
1882             },
1883             "*=" : function(a, v){
1884                 return a && a.indexOf(v) !== -1;
1885             },
1886             "%=" : function(a, v){
1887                 return (a % v) == 0;
1888             },
1889             "|=" : function(a, v){
1890                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
1891             },
1892             "~=" : function(a, v){
1893                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
1894             }
1895         },
1896
1897         /**
1898          * <p>Object hash of "pseudo class" filter functions which are used when filtering selections. Each function is passed
1899          * two parameters:</p><div class="mdetail-params"><ul>
1900          * <li><b>c</b> : Array<div class="sub-desc">An Array of DOM elements to filter.</div></li>
1901          * <li><b>v</b> : String<div class="sub-desc">The argument (if any) supplied in the selector.</div></li>
1902          * </ul></div>
1903          * <p>A filter function returns an Array of DOM elements which conform to the pseudo class.</p>
1904          * <p>In addition to the provided pseudo classes listed above such as <code>first-child</code> and <code>nth-child</code>,
1905          * developers may add additional, custom psuedo class filters to select elements according to application-specific requirements.</p>
1906          * <p>For example, to filter <code>&lt;a></code> elements to only return links to <i>external</i> resources:</p>
1907          * <code><pre>
1908 Ext.DomQuery.pseudos.external = function(c, v){
1909     var r = [], ri = -1;
1910     for(var i = 0, ci; ci = c[i]; i++){
1911 //      Include in result set only if it's a link to an external resource
1912         if(ci.hostname != location.hostname){
1913             r[++ri] = ci;
1914         }
1915     }
1916     return r;
1917 };</pre></code>
1918          * Then external links could be gathered with the following statement:<code><pre>
1919 var externalLinks = Ext.select("a:external");
1920 </code></pre>
1921          */
1922         pseudos : {
1923             "first-child" : function(c){
1924                 var r = [], ri = -1, n;
1925                 for(var i = 0, ci; ci = n = c[i]; i++){
1926                     while((n = n.previousSibling) && n.nodeType != 1);
1927                     if(!n){
1928                         r[++ri] = ci;
1929                     }
1930                 }
1931                 return r;
1932             },
1933
1934             "last-child" : function(c){
1935                 var r = [], ri = -1, n;
1936                 for(var i = 0, ci; ci = n = c[i]; i++){
1937                     while((n = n.nextSibling) && n.nodeType != 1);
1938                     if(!n){
1939                         r[++ri] = ci;
1940                     }
1941                 }
1942                 return r;
1943             },
1944
1945             "nth-child" : function(c, a) {
1946                 var r = [], ri = -1,
1947                         m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a),
1948                         f = (m[1] || 1) - 0, l = m[2] - 0;
1949                 for(var i = 0, n; n = c[i]; i++){
1950                     var pn = n.parentNode;
1951                     if (batch != pn._batch) {
1952                         var j = 0;
1953                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
1954                             if(cn.nodeType == 1){
1955                                cn.nodeIndex = ++j;
1956                             }
1957                         }
1958                         pn._batch = batch;
1959                     }
1960                     if (f == 1) {
1961                         if (l == 0 || n.nodeIndex == l){
1962                             r[++ri] = n;
1963                         }
1964                     } else if ((n.nodeIndex + l) % f == 0){
1965                         r[++ri] = n;
1966                     }
1967                 }
1968
1969                 return r;
1970             },
1971
1972             "only-child" : function(c){
1973                 var r = [], ri = -1;;
1974                 for(var i = 0, ci; ci = c[i]; i++){
1975                     if(!prev(ci) && !next(ci)){
1976                         r[++ri] = ci;
1977                     }
1978                 }
1979                 return r;
1980             },
1981
1982             "empty" : function(c){
1983                 var r = [], ri = -1;
1984                 for(var i = 0, ci; ci = c[i]; i++){
1985                     var cns = ci.childNodes, j = 0, cn, empty = true;
1986                     while(cn = cns[j]){
1987                         ++j;
1988                         if(cn.nodeType == 1 || cn.nodeType == 3){
1989                             empty = false;
1990                             break;
1991                         }
1992                     }
1993                     if(empty){
1994                         r[++ri] = ci;
1995                     }
1996                 }
1997                 return r;
1998             },
1999
2000             "contains" : function(c, v){
2001                 var r = [], ri = -1;
2002                 for(var i = 0, ci; ci = c[i]; i++){
2003                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
2004                         r[++ri] = ci;
2005                     }
2006                 }
2007                 return r;
2008             },
2009
2010             "nodeValue" : function(c, v){
2011                 var r = [], ri = -1;
2012                 for(var i = 0, ci; ci = c[i]; i++){
2013                     if(ci.firstChild && ci.firstChild.nodeValue == v){
2014                         r[++ri] = ci;
2015                     }
2016                 }
2017                 return r;
2018             },
2019
2020             "checked" : function(c){
2021                 var r = [], ri = -1;
2022                 for(var i = 0, ci; ci = c[i]; i++){
2023                     if(ci.checked == true){
2024                         r[++ri] = ci;
2025                     }
2026                 }
2027                 return r;
2028             },
2029
2030             "not" : function(c, ss){
2031                 return Ext.DomQuery.filter(c, ss, true);
2032             },
2033
2034             "any" : function(c, selectors){
2035                 var ss = selectors.split('|'),
2036                         r = [], ri = -1, s;
2037                 for(var i = 0, ci; ci = c[i]; i++){
2038                     for(var j = 0; s = ss[j]; j++){
2039                         if(Ext.DomQuery.is(ci, s)){
2040                             r[++ri] = ci;
2041                             break;
2042                         }
2043                     }
2044                 }
2045                 return r;
2046             },
2047
2048             "odd" : function(c){
2049                 return this["nth-child"](c, "odd");
2050             },
2051
2052             "even" : function(c){
2053                 return this["nth-child"](c, "even");
2054             },
2055
2056             "nth" : function(c, a){
2057                 return c[a-1] || [];
2058             },
2059
2060             "first" : function(c){
2061                 return c[0] || [];
2062             },
2063
2064             "last" : function(c){
2065                 return c[c.length-1] || [];
2066             },
2067
2068             "has" : function(c, ss){
2069                 var s = Ext.DomQuery.select,
2070                         r = [], ri = -1;
2071                 for(var i = 0, ci; ci = c[i]; i++){
2072                     if(s(ss, ci).length > 0){
2073                         r[++ri] = ci;
2074                     }
2075                 }
2076                 return r;
2077             },
2078
2079             "next" : function(c, ss){
2080                 var is = Ext.DomQuery.is,
2081                         r = [], ri = -1;
2082                 for(var i = 0, ci; ci = c[i]; i++){
2083                     var n = next(ci);
2084                     if(n && is(n, ss)){
2085                         r[++ri] = ci;
2086                     }
2087                 }
2088                 return r;
2089             },
2090
2091             "prev" : function(c, ss){
2092                 var is = Ext.DomQuery.is,
2093                         r = [], ri = -1;
2094                 for(var i = 0, ci; ci = c[i]; i++){
2095                     var n = prev(ci);
2096                     if(n && is(n, ss)){
2097                         r[++ri] = ci;
2098                     }
2099                 }
2100                 return r;
2101             }
2102         }
2103     };
2104 }();
2105
2106 /**
2107  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Ext.DomQuery#select}
2108  * @param {String} path The selector/xpath query
2109  * @param {Node} root (optional) The start of the query (defaults to document).
2110  * @return {Array}
2111  * @member Ext
2112  * @method query
2113  */
2114 Ext.query = Ext.DomQuery.select;
2115 /**
2116  * @class Ext.util.DelayedTask
2117  * <p> The DelayedTask class provides a convenient way to "buffer" the execution of a method,
2118  * performing setTimeout where a new timeout cancels the old timeout. When called, the
2119  * task will wait the specified time period before executing. If durng that time period,
2120  * the task is called again, the original call will be cancelled. This continues so that
2121  * the function is only called a single time for each iteration.</p>
2122  * <p>This method is especially useful for things like detecting whether a user has finished
2123  * typing in a text field. An example would be performing validation on a keypress. You can
2124  * use this class to buffer the keypress events for a certain number of milliseconds, and
2125  * perform only if they stop for that amount of time.  Usage:</p><pre><code>
2126 var task = new Ext.util.DelayedTask(function(){
2127     alert(Ext.getDom('myInputField').value.length);
2128 });
2129 // Wait 500ms before calling our function. If the user presses another key 
2130 // during that 500ms, it will be cancelled and we'll wait another 500ms.
2131 Ext.get('myInputField').on('keypress', function(){
2132     task.{@link #delay}(500); 
2133 });
2134  * </code></pre> 
2135  * <p>Note that we are using a DelayedTask here to illustrate a point. The configuration
2136  * option <tt>buffer</tt> for {@link Ext.util.Observable#addListener addListener/on} will
2137  * also setup a delayed task for you to buffer events.</p> 
2138  * @constructor The parameters to this constructor serve as defaults and are not required.
2139  * @param {Function} fn (optional) The default function to call.
2140  * @param {Object} scope The default scope (The <code><b>this</b></code> reference) in which the
2141  * function is called. If not specified, <code>this</code> will refer to the browser window.
2142  * @param {Array} args (optional) The default Array of arguments.
2143  */
2144 Ext.util.DelayedTask = function(fn, scope, args){
2145     var me = this,
2146         id,     
2147         call = function(){
2148                 clearInterval(id);
2149                 id = null;
2150                 fn.apply(scope, args || []);
2151             };
2152             
2153     /**
2154      * Cancels any pending timeout and queues a new one
2155      * @param {Number} delay The milliseconds to delay
2156      * @param {Function} newFn (optional) Overrides function passed to constructor
2157      * @param {Object} newScope (optional) Overrides scope passed to constructor. Remember that if no scope
2158      * is specified, <code>this</code> will refer to the browser window.
2159      * @param {Array} newArgs (optional) Overrides args passed to constructor
2160      */
2161     me.delay = function(delay, newFn, newScope, newArgs){
2162         me.cancel();
2163         fn = newFn || fn;
2164         scope = newScope || scope;
2165         args = newArgs || args;
2166         id = setInterval(call, delay);
2167     };
2168
2169     /**
2170      * Cancel the last queued timeout
2171      */
2172     me.cancel = function(){
2173         if(id){
2174             clearInterval(id);
2175             id = null;
2176         }
2177     };
2178 };/**
2179  * @class Ext.Element
2180  * <p>Encapsulates a DOM element, adding simple DOM manipulation facilities, normalizing for browser differences.</p>
2181  * <p>All instances of this class inherit the methods of {@link Ext.Fx} making visual effects easily available to all DOM elements.</p>
2182  * <p>Note that the events documented in this class are not Ext events, they encapsulate browser events. To
2183  * access the underlying browser event, see {@link Ext.EventObject#browserEvent}. Some older
2184  * browsers may not support the full range of events. Which events are supported is beyond the control of ExtJs.</p>
2185  * Usage:<br>
2186 <pre><code>
2187 // by id
2188 var el = Ext.get("my-div");
2189
2190 // by DOM element reference
2191 var el = Ext.get(myDivElement);
2192 </code></pre>
2193  * <b>Animations</b><br />
2194  * <p>When an element is manipulated, by default there is no animation.</p>
2195  * <pre><code>
2196 var el = Ext.get("my-div");
2197
2198 // no animation
2199 el.setWidth(100);
2200  * </code></pre>
2201  * <p>Many of the functions for manipulating an element have an optional "animate" parameter.  This
2202  * parameter can be specified as boolean (<tt>true</tt>) for default animation effects.</p>
2203  * <pre><code>
2204 // default animation
2205 el.setWidth(100, true);
2206  * </code></pre>
2207  *
2208  * <p>To configure the effects, an object literal with animation options to use as the Element animation
2209  * configuration object can also be specified. Note that the supported Element animation configuration
2210  * options are a subset of the {@link Ext.Fx} animation options specific to Fx effects.  The supported
2211  * Element animation configuration options are:</p>
2212 <pre>
2213 Option    Default   Description
2214 --------- --------  ---------------------------------------------
2215 {@link Ext.Fx#duration duration}  .35       The duration of the animation in seconds
2216 {@link Ext.Fx#easing easing}    easeOut   The easing method
2217 {@link Ext.Fx#callback callback}  none      A function to execute when the anim completes
2218 {@link Ext.Fx#scope scope}     this      The scope (this) of the callback function
2219 </pre>
2220  *
2221  * <pre><code>
2222 // Element animation options object
2223 var opt = {
2224     {@link Ext.Fx#duration duration}: 1,
2225     {@link Ext.Fx#easing easing}: 'elasticIn',
2226     {@link Ext.Fx#callback callback}: this.foo,
2227     {@link Ext.Fx#scope scope}: this
2228 };
2229 // animation with some options set
2230 el.setWidth(100, opt);
2231  * </code></pre>
2232  * <p>The Element animation object being used for the animation will be set on the options
2233  * object as "anim", which allows you to stop or manipulate the animation. Here is an example:</p>
2234  * <pre><code>
2235 // using the "anim" property to get the Anim object
2236 if(opt.anim.isAnimated()){
2237     opt.anim.stop();
2238 }
2239  * </code></pre>
2240  * <p>Also see the <tt>{@link #animate}</tt> method for another animation technique.</p>
2241  * <p><b> Composite (Collections of) Elements</b></p>
2242  * <p>For working with collections of Elements, see {@link Ext.CompositeElement}</p>
2243  * @constructor Create a new Element directly.
2244  * @param {String/HTMLElement} element
2245  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
2246  */
2247 (function(){
2248 var DOC = document;
2249
2250 Ext.Element = function(element, forceNew){
2251     var dom = typeof element == "string" ?
2252               DOC.getElementById(element) : element,
2253         id;
2254
2255     if(!dom) return null;
2256
2257     id = dom.id;
2258
2259     if(!forceNew && id && Ext.elCache[id]){ // element object already exists
2260         return Ext.elCache[id].el;
2261     }
2262
2263     /**
2264      * The DOM element
2265      * @type HTMLElement
2266      */
2267     this.dom = dom;
2268
2269     /**
2270      * The DOM element ID
2271      * @type String
2272      */
2273     this.id = id || Ext.id(dom);
2274 };
2275
2276 var DH = Ext.DomHelper,
2277     El = Ext.Element,
2278     EC = Ext.elCache;
2279
2280 El.prototype = {
2281     /**
2282      * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
2283      * @param {Object} o The object with the attributes
2284      * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
2285      * @return {Ext.Element} this
2286      */
2287     set : function(o, useSet){
2288         var el = this.dom,
2289             attr,
2290             val,
2291             useSet = (useSet !== false) && !!el.setAttribute;
2292
2293         for (attr in o) {
2294             if (o.hasOwnProperty(attr)) {
2295                 val = o[attr];
2296                 if (attr == 'style') {
2297                     DH.applyStyles(el, val);
2298                 } else if (attr == 'cls') {
2299                     el.className = val;
2300                 } else if (useSet) {
2301                     el.setAttribute(attr, val);
2302                 } else {
2303                     el[attr] = val;
2304                 }
2305             }
2306         }
2307         return this;
2308     },
2309
2310 //  Mouse events
2311     /**
2312      * @event click
2313      * Fires when a mouse click is detected within the element.
2314      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
2315      * @param {HtmlElement} t The target of the event.
2316      * @param {Object} o The options configuration passed to the {@link #addListener} call.
2317      */
2318     /**
2319      * @event contextmenu
2320      * Fires when a right click is detected within the element.
2321      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
2322      * @param {HtmlElement} t The target of the event.
2323      * @param {Object} o The options configuration passed to the {@link #addListener} call.
2324      */
2325     /**
2326      * @event dblclick
2327      * Fires when a mouse double click is detected within the element.
2328      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
2329      * @param {HtmlElement} t The target of the event.
2330      * @param {Object} o The options configuration passed to the {@link #addListener} call.
2331      */
2332     /**
2333      * @event mousedown
2334      * Fires when a mousedown is detected within the element.
2335      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
2336      * @param {HtmlElement} t The target of the event.
2337      * @param {Object} o The options configuration passed to the {@link #addListener} call.
2338      */
2339     /**
2340      * @event mouseup
2341      * Fires when a mouseup is detected within the element.
2342      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
2343      * @param {HtmlElement} t The target of the event.
2344      * @param {Object} o The options configuration passed to the {@link #addListener} call.
2345      */
2346     /**
2347      * @event mouseover
2348      * Fires when a mouseover is detected within the element.
2349      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
2350      * @param {HtmlElement} t The target of the event.
2351      * @param {Object} o The options configuration passed to the {@link #addListener} call.
2352      */
2353     /**
2354      * @event mousemove
2355      * Fires when a mousemove is detected with the element.
2356      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
2357      * @param {HtmlElement} t The target of the event.
2358      * @param {Object} o The options configuration passed to the {@link #addListener} call.
2359      */
2360     /**
2361      * @event mouseout
2362      * Fires when a mouseout is detected with the element.
2363      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
2364      * @param {HtmlElement} t The target of the event.
2365      * @param {Object} o The options configuration passed to the {@link #addListener} call.
2366      */
2367     /**
2368      * @event mouseenter
2369      * Fires when the mouse enters the element.
2370      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
2371      * @param {HtmlElement} t The target of the event.
2372      * @param {Object} o The options configuration passed to the {@link #addListener} call.
2373      */
2374     /**
2375      * @event mouseleave
2376      * Fires when the mouse leaves the element.
2377      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
2378      * @param {HtmlElement} t The target of the event.
2379      * @param {Object} o The options configuration passed to the {@link #addListener} call.
2380      */
2381
2382 //  Keyboard events
2383     /**
2384      * @event keypress
2385      * Fires when a keypress is detected within the element.
2386      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
2387      * @param {HtmlElement} t The target of the event.
2388      * @param {Object} o The options configuration passed to the {@link #addListener} call.
2389      */
2390     /**
2391      * @event keydown
2392      * Fires when a keydown is detected within the element.
2393      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
2394      * @param {HtmlElement} t The target of the event.
2395      * @param {Object} o The options configuration passed to the {@link #addListener} call.
2396      */
2397     /**
2398      * @event keyup
2399      * Fires when a keyup is detected within the element.
2400      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
2401      * @param {HtmlElement} t The target of the event.
2402      * @param {Object} o The options configuration passed to the {@link #addListener} call.
2403      */
2404
2405
2406 //  HTML frame/object events
2407     /**
2408      * @event load
2409      * Fires when the user agent finishes loading all content within the element. Only supported by window, frames, objects and images.
2410      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
2411      * @param {HtmlElement} t The target of the event.
2412      * @param {Object} o The options configuration passed to the {@link #addListener} call.
2413      */
2414     /**
2415      * @event unload
2416      * Fires when the user agent removes all content from a window or frame. For elements, it fires when the target element or any of its content has been removed.
2417      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
2418      * @param {HtmlElement} t The target of the event.
2419      * @param {Object} o The options configuration passed to the {@link #addListener} call.
2420      */
2421     /**
2422      * @event abort
2423      * Fires when an object/image is stopped from loading before completely loaded.
2424      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
2425      * @param {HtmlElement} t The target of the event.
2426      * @param {Object} o The options configuration passed to the {@link #addListener} call.
2427      */
2428     /**
2429      * @event error
2430      * Fires when an object/image/frame cannot be loaded properly.
2431      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
2432      * @param {HtmlElement} t The target of the event.
2433      * @param {Object} o The options configuration passed to the {@link #addListener} call.
2434      */
2435     /**
2436      * @event resize
2437      * Fires when a document view is resized.
2438      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
2439      * @param {HtmlElement} t The target of the event.
2440      * @param {Object} o The options configuration passed to the {@link #addListener} call.
2441      */
2442     /**
2443      * @event scroll
2444      * Fires when a document view is scrolled.
2445      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
2446      * @param {HtmlElement} t The target of the event.
2447      * @param {Object} o The options configuration passed to the {@link #addListener} call.
2448      */
2449
2450 //  Form events
2451     /**
2452      * @event select
2453      * Fires when a user selects some text in a text field, including input and textarea.
2454      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
2455      * @param {HtmlElement} t The target of the event.
2456      * @param {Object} o The options configuration passed to the {@link #addListener} call.
2457      */
2458     /**
2459      * @event change
2460      * Fires when a control loses the input focus and its value has been modified since gaining focus.
2461      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
2462      * @param {HtmlElement} t The target of the event.
2463      * @param {Object} o The options configuration passed to the {@link #addListener} call.
2464      */
2465     /**
2466      * @event submit
2467      * Fires when a form is submitted.
2468      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
2469      * @param {HtmlElement} t The target of the event.
2470      * @param {Object} o The options configuration passed to the {@link #addListener} call.
2471      */
2472     /**
2473      * @event reset
2474      * Fires when a form is reset.
2475      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
2476      * @param {HtmlElement} t The target of the event.
2477      * @param {Object} o The options configuration passed to the {@link #addListener} call.
2478      */
2479     /**
2480      * @event focus
2481      * Fires when an element receives focus either via the pointing device or by tab navigation.
2482      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
2483      * @param {HtmlElement} t The target of the event.
2484      * @param {Object} o The options configuration passed to the {@link #addListener} call.
2485      */
2486     /**
2487      * @event blur
2488      * Fires when an element loses focus either via the pointing device or by tabbing navigation.
2489      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
2490      * @param {HtmlElement} t The target of the event.
2491      * @param {Object} o The options configuration passed to the {@link #addListener} call.
2492      */
2493
2494 //  User Interface events
2495     /**
2496      * @event DOMFocusIn
2497      * Where supported. Similar to HTML focus event, but can be applied to any focusable element.
2498      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
2499      * @param {HtmlElement} t The target of the event.
2500      * @param {Object} o The options configuration passed to the {@link #addListener} call.
2501      */
2502     /**
2503      * @event DOMFocusOut
2504      * Where supported. Similar to HTML blur event, but can be applied to any focusable element.
2505      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
2506      * @param {HtmlElement} t The target of the event.
2507      * @param {Object} o The options configuration passed to the {@link #addListener} call.
2508      */
2509     /**
2510      * @event DOMActivate
2511      * Where supported. Fires when an element is activated, for instance, through a mouse click or a keypress.
2512      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
2513      * @param {HtmlElement} t The target of the event.
2514      * @param {Object} o The options configuration passed to the {@link #addListener} call.
2515      */
2516
2517 //  DOM Mutation events
2518     /**
2519      * @event DOMSubtreeModified
2520      * Where supported. Fires when the subtree is modified.
2521      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
2522      * @param {HtmlElement} t The target of the event.
2523      * @param {Object} o The options configuration passed to the {@link #addListener} call.
2524      */
2525     /**
2526      * @event DOMNodeInserted
2527      * Where supported. Fires when a node has been added as a child of another node.
2528      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
2529      * @param {HtmlElement} t The target of the event.
2530      * @param {Object} o The options configuration passed to the {@link #addListener} call.
2531      */
2532     /**
2533      * @event DOMNodeRemoved
2534      * Where supported. Fires when a descendant node of the element is removed.
2535      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
2536      * @param {HtmlElement} t The target of the event.
2537      * @param {Object} o The options configuration passed to the {@link #addListener} call.
2538      */
2539     /**
2540      * @event DOMNodeRemovedFromDocument
2541      * Where supported. Fires when a node is being removed from a document.
2542      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
2543      * @param {HtmlElement} t The target of the event.
2544      * @param {Object} o The options configuration passed to the {@link #addListener} call.
2545      */
2546     /**
2547      * @event DOMNodeInsertedIntoDocument
2548      * Where supported. Fires when a node is being inserted into a document.
2549      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
2550      * @param {HtmlElement} t The target of the event.
2551      * @param {Object} o The options configuration passed to the {@link #addListener} call.
2552      */
2553     /**
2554      * @event DOMAttrModified
2555      * Where supported. Fires when an attribute has been modified.
2556      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
2557      * @param {HtmlElement} t The target of the event.
2558      * @param {Object} o The options configuration passed to the {@link #addListener} call.
2559      */
2560     /**
2561      * @event DOMCharacterDataModified
2562      * Where supported. Fires when the character data has been modified.
2563      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
2564      * @param {HtmlElement} t The target of the event.
2565      * @param {Object} o The options configuration passed to the {@link #addListener} call.
2566      */
2567
2568     /**
2569      * The default unit to append to CSS values where a unit isn't provided (defaults to px).
2570      * @type String
2571      */
2572     defaultUnit : "px",
2573
2574     /**
2575      * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
2576      * @param {String} selector The simple selector to test
2577      * @return {Boolean} True if this element matches the selector, else false
2578      */
2579     is : function(simpleSelector){
2580         return Ext.DomQuery.is(this.dom, simpleSelector);
2581     },
2582
2583     /**
2584      * Tries to focus the element. Any exceptions are caught and ignored.
2585      * @param {Number} defer (optional) Milliseconds to defer the focus
2586      * @return {Ext.Element} this
2587      */
2588     focus : function(defer, /* private */ dom) {
2589         var me = this,
2590             dom = dom || me.dom;
2591         try{
2592             if(Number(defer)){
2593                 me.focus.defer(defer, null, [null, dom]);
2594             }else{
2595                 dom.focus();
2596             }
2597         }catch(e){}
2598         return me;
2599     },
2600
2601     /**
2602      * Tries to blur the element. Any exceptions are caught and ignored.
2603      * @return {Ext.Element} this
2604      */
2605     blur : function() {
2606         try{
2607             this.dom.blur();
2608         }catch(e){}
2609         return this;
2610     },
2611
2612     /**
2613      * Returns the value of the "value" attribute
2614      * @param {Boolean} asNumber true to parse the value as a number
2615      * @return {String/Number}
2616      */
2617     getValue : function(asNumber){
2618         var val = this.dom.value;
2619         return asNumber ? parseInt(val, 10) : val;
2620     },
2621
2622     /**
2623      * Appends an event handler to this element.  The shorthand version {@link #on} is equivalent.
2624      * @param {String} eventName The name of event to handle.
2625      * @param {Function} fn The handler function the event invokes. This function is passed
2626      * the following parameters:<ul>
2627      * <li><b>evt</b> : EventObject<div class="sub-desc">The {@link Ext.EventObject EventObject} describing the event.</div></li>
2628      * <li><b>el</b> : HtmlElement<div class="sub-desc">The DOM element which was the target of the event.
2629      * Note that this may be filtered by using the <tt>delegate</tt> option.</div></li>
2630      * <li><b>o</b> : Object<div class="sub-desc">The options object from the addListener call.</div></li>
2631      * </ul>
2632      * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.
2633      * <b>If omitted, defaults to this Element.</b>.
2634      * @param {Object} options (optional) An object containing handler configuration properties.
2635      * This may contain any of the following properties:<ul>
2636      * <li><b>scope</b> Object : <div class="sub-desc">The scope (<code><b>this</b></code> reference) in which the handler function is executed.
2637      * <b>If omitted, defaults to this Element.</b></div></li>
2638      * <li><b>delegate</b> String: <div class="sub-desc">A simple selector to filter the target or look for a descendant of the target. See below for additional details.</div></li>
2639      * <li><b>stopEvent</b> Boolean: <div class="sub-desc">True to stop the event. That is stop propagation, and prevent the default action.</div></li>
2640      * <li><b>preventDefault</b> Boolean: <div class="sub-desc">True to prevent the default action</div></li>
2641      * <li><b>stopPropagation</b> Boolean: <div class="sub-desc">True to prevent event propagation</div></li>
2642      * <li><b>normalized</b> Boolean: <div class="sub-desc">False to pass a browser event to the handler function instead of an Ext.EventObject</div></li>
2643      * <li><b>target</b> Ext.Element: <div class="sub-desc">Only call the handler if the event was fired on the target Element, <i>not</i> if the event was bubbled up from a child node.</div></li>
2644      * <li><b>delay</b> Number: <div class="sub-desc">The number of milliseconds to delay the invocation of the handler after the event fires.</div></li>
2645      * <li><b>single</b> Boolean: <div class="sub-desc">True to add a handler to handle just the next firing of the event, and then remove itself.</div></li>
2646      * <li><b>buffer</b> Number: <div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
2647      * by the specified number of milliseconds. If the event fires again within that time, the original
2648      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
2649      * </ul><br>
2650      * <p>
2651      * <b>Combining Options</b><br>
2652      * In the following examples, the shorthand form {@link #on} is used rather than the more verbose
2653      * addListener.  The two are equivalent.  Using the options argument, it is possible to combine different
2654      * types of listeners:<br>
2655      * <br>
2656      * A delayed, one-time listener that auto stops the event and adds a custom argument (forumId) to the
2657      * options object. The options object is available as the third parameter in the handler function.<div style="margin: 5px 20px 20px;">
2658      * Code:<pre><code>
2659 el.on('click', this.onClick, this, {
2660     single: true,
2661     delay: 100,
2662     stopEvent : true,
2663     forumId: 4
2664 });</code></pre></p>
2665      * <p>
2666      * <b>Attaching multiple handlers in 1 call</b><br>
2667      * The method also allows for a single argument to be passed which is a config object containing properties
2668      * which specify multiple handlers.</p>
2669      * <p>
2670      * Code:<pre><code>
2671 el.on({
2672     'click' : {
2673         fn: this.onClick,
2674         scope: this,
2675         delay: 100
2676     },
2677     'mouseover' : {
2678         fn: this.onMouseOver,
2679         scope: this
2680     },
2681     'mouseout' : {
2682         fn: this.onMouseOut,
2683         scope: this
2684     }
2685 });</code></pre>
2686      * <p>
2687      * Or a shorthand syntax:<br>
2688      * Code:<pre><code></p>
2689 el.on({
2690     'click' : this.onClick,
2691     'mouseover' : this.onMouseOver,
2692     'mouseout' : this.onMouseOut,
2693     scope: this
2694 });
2695      * </code></pre></p>
2696      * <p><b>delegate</b></p>
2697      * <p>This is a configuration option that you can pass along when registering a handler for
2698      * an event to assist with event delegation. Event delegation is a technique that is used to
2699      * reduce memory consumption and prevent exposure to memory-leaks. By registering an event
2700      * for a container element as opposed to each element within a container. By setting this
2701      * configuration option to a simple selector, the target element will be filtered to look for
2702      * a descendant of the target.
2703      * For example:<pre><code>
2704 // using this markup:
2705 &lt;div id='elId'>
2706     &lt;p id='p1'>paragraph one&lt;/p>
2707     &lt;p id='p2' class='clickable'>paragraph two&lt;/p>
2708     &lt;p id='p3'>paragraph three&lt;/p>
2709 &lt;/div>
2710 // utilize event delegation to registering just one handler on the container element:
2711 el = Ext.get('elId');
2712 el.on(
2713     'click',
2714     function(e,t) {
2715         // handle click
2716         console.info(t.id); // 'p2'
2717     },
2718     this,
2719     {
2720         // filter the target element to be a descendant with the class 'clickable'
2721         delegate: '.clickable'
2722     }
2723 );
2724      * </code></pre></p>
2725      * @return {Ext.Element} this
2726      */
2727     addListener : function(eventName, fn, scope, options){
2728         Ext.EventManager.on(this.dom,  eventName, fn, scope || this, options);
2729         return this;
2730     },
2731
2732     /**
2733      * Removes an event handler from this element.  The shorthand version {@link #un} is equivalent.
2734      * <b>Note</b>: if a <i>scope</i> was explicitly specified when {@link #addListener adding} the
2735      * listener, the same scope must be specified here.
2736      * Example:
2737      * <pre><code>
2738 el.removeListener('click', this.handlerFn);
2739 // or
2740 el.un('click', this.handlerFn);
2741 </code></pre>
2742      * @param {String} eventName The name of the event from which to remove the handler.
2743      * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
2744      * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
2745      * then this must refer to the same object.
2746      * @return {Ext.Element} this
2747      */
2748     removeListener : function(eventName, fn, scope){
2749         Ext.EventManager.removeListener(this.dom,  eventName, fn, scope || this);
2750         return this;
2751     },
2752
2753     /**
2754      * Removes all previous added listeners from this element
2755      * @return {Ext.Element} this
2756      */
2757     removeAllListeners : function(){
2758         Ext.EventManager.removeAll(this.dom);
2759         return this;
2760     },
2761
2762     /**
2763      * Recursively removes all previous added listeners from this element and its children
2764      * @return {Ext.Element} this
2765      */
2766     purgeAllListeners : function() {
2767         Ext.EventManager.purgeElement(this, true);
2768         return this;
2769     },
2770     /**
2771      * @private Test if size has a unit, otherwise appends the default
2772      */
2773     addUnits : function(size){
2774         if(size === "" || size == "auto" || size === undefined){
2775             size = size || '';
2776         } else if(!isNaN(size) || !unitPattern.test(size)){
2777             size = size + (this.defaultUnit || 'px');
2778         }
2779         return size;
2780     },
2781
2782     /**
2783      * <p>Updates the <a href="http://developer.mozilla.org/en/DOM/element.innerHTML">innerHTML</a> of this Element
2784      * from a specified URL. Note that this is subject to the <a href="http://en.wikipedia.org/wiki/Same_origin_policy">Same Origin Policy</a></p>
2785      * <p>Updating innerHTML of an element will <b>not</b> execute embedded <tt>&lt;script></tt> elements. This is a browser restriction.</p>
2786      * @param {Mixed} options. Either a sring containing the URL from which to load the HTML, or an {@link Ext.Ajax#request} options object specifying
2787      * exactly how to request the HTML.
2788      * @return {Ext.Element} this
2789      */
2790     load : function(url, params, cb){
2791         Ext.Ajax.request(Ext.apply({
2792             params: params,
2793             url: url.url || url,
2794             callback: cb,
2795             el: this.dom,
2796             indicatorText: url.indicatorText || ''
2797         }, Ext.isObject(url) ? url : {}));
2798         return this;
2799     },
2800
2801     /**
2802      * Tests various css rules/browsers to determine if this element uses a border box
2803      * @return {Boolean}
2804      */
2805     isBorderBox : function(){
2806         return Ext.isBorderBox || Ext.isForcedBorderBox || noBoxAdjust[(this.dom.tagName || "").toLowerCase()];
2807     },
2808
2809     /**
2810      * <p>Removes this element's dom reference.  Note that event and cache removal is handled at {@link Ext#removeNode}</p>
2811      */
2812     remove : function(){
2813         var me = this,
2814             dom = me.dom;
2815
2816         if (dom) {
2817             delete me.dom;
2818             Ext.removeNode(dom);
2819         }
2820     },
2821
2822     /**
2823      * Sets up event handlers to call the passed functions when the mouse is moved into and out of the Element.
2824      * @param {Function} overFn The function to call when the mouse enters the Element.
2825      * @param {Function} outFn The function to call when the mouse leaves the Element.
2826      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the functions are executed. Defaults to the Element's DOM element.
2827      * @param {Object} options (optional) Options for the listener. See {@link Ext.util.Observable#addListener the <tt>options</tt> parameter}.
2828      * @return {Ext.Element} this
2829      */
2830     hover : function(overFn, outFn, scope, options){
2831         var me = this;
2832         me.on('mouseenter', overFn, scope || me.dom, options);
2833         me.on('mouseleave', outFn, scope || me.dom, options);
2834         return me;
2835     },
2836
2837     /**
2838      * Returns true if this element is an ancestor of the passed element
2839      * @param {HTMLElement/String} el The element to check
2840      * @return {Boolean} True if this element is an ancestor of el, else false
2841      */
2842     contains : function(el){
2843         return !el ? false : Ext.lib.Dom.isAncestor(this.dom, el.dom ? el.dom : el);
2844     },
2845
2846     /**
2847      * Returns the value of a namespaced attribute from the element's underlying DOM node.
2848      * @param {String} namespace The namespace in which to look for the attribute
2849      * @param {String} name The attribute name
2850      * @return {String} The attribute value
2851      * @deprecated
2852      */
2853     getAttributeNS : function(ns, name){
2854         return this.getAttribute(name, ns);
2855     },
2856
2857     /**
2858      * Returns the value of an attribute from the element's underlying DOM node.
2859      * @param {String} name The attribute name
2860      * @param {String} namespace (optional) The namespace in which to look for the attribute
2861      * @return {String} The attribute value
2862      */
2863     getAttribute : Ext.isIE ? function(name, ns){
2864         var d = this.dom,
2865             type = typeof d[ns + ":" + name];
2866
2867         if(['undefined', 'unknown'].indexOf(type) == -1){
2868             return d[ns + ":" + name];
2869         }
2870         return d[name];
2871     } : function(name, ns){
2872         var d = this.dom;
2873         return d.getAttributeNS(ns, name) || d.getAttribute(ns + ":" + name) || d.getAttribute(name) || d[name];
2874     },
2875
2876     /**
2877     * Update the innerHTML of this element
2878     * @param {String} html The new HTML
2879     * @return {Ext.Element} this
2880      */
2881     update : function(html) {
2882         if (this.dom) {
2883             this.dom.innerHTML = html;
2884         }
2885         return this;
2886     }
2887 };
2888
2889 var ep = El.prototype;
2890
2891 El.addMethods = function(o){
2892    Ext.apply(ep, o);
2893 };
2894
2895 /**
2896  * Appends an event handler (shorthand for {@link #addListener}).
2897  * @param {String} eventName The name of event to handle.
2898  * @param {Function} fn The handler function the event invokes.
2899  * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function is executed.
2900  * @param {Object} options (optional) An object containing standard {@link #addListener} options
2901  * @member Ext.Element
2902  * @method on
2903  */
2904 ep.on = ep.addListener;
2905
2906 /**
2907  * Removes an event handler from this element (see {@link #removeListener} for additional notes).
2908  * @param {String} eventName The name of the event from which to remove the handler.
2909  * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
2910  * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
2911  * then this must refer to the same object.
2912  * @return {Ext.Element} this
2913  * @member Ext.Element
2914  * @method un
2915  */
2916 ep.un = ep.removeListener;
2917
2918 /**
2919  * true to automatically adjust width and height settings for box-model issues (default to true)
2920  */
2921 ep.autoBoxAdjust = true;
2922
2923 // private
2924 var unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i,
2925     docEl;
2926
2927 /**
2928  * @private
2929  */
2930
2931 /**
2932  * Retrieves Ext.Element objects.
2933  * <p><b>This method does not retrieve {@link Ext.Component Component}s.</b> This method
2934  * retrieves Ext.Element objects which encapsulate DOM elements. To retrieve a Component by
2935  * its ID, use {@link Ext.ComponentMgr#get}.</p>
2936  * <p>Uses simple caching to consistently return the same object. Automatically fixes if an
2937  * object was recreated with the same id via AJAX or DOM.</p>
2938  * @param {Mixed} el The id of the node, a DOM Node or an existing Element.
2939  * @return {Element} The Element object (or null if no matching element was found)
2940  * @static
2941  * @member Ext.Element
2942  * @method get
2943  */
2944 El.get = function(el){
2945     var ex,
2946         elm,
2947         id;
2948     if(!el){ return null; }
2949     if (typeof el == "string") { // element id
2950         if (!(elm = DOC.getElementById(el))) {
2951             return null;
2952         }
2953         if (EC[el] && EC[el].el) {
2954             ex = EC[el].el;
2955             ex.dom = elm;
2956         } else {
2957             ex = El.addToCache(new El(elm));
2958         }
2959         return ex;
2960     } else if (el.tagName) { // dom element
2961         if(!(id = el.id)){
2962             id = Ext.id(el);
2963         }
2964         if (EC[id] && EC[id].el) {
2965             ex = EC[id].el;
2966             ex.dom = el;
2967         } else {
2968             ex = El.addToCache(new El(el));
2969         }
2970         return ex;
2971     } else if (el instanceof El) {
2972         if(el != docEl){
2973             // refresh dom element in case no longer valid,
2974             // catch case where it hasn't been appended
2975
2976             // If an el instance is passed, don't pass to getElementById without some kind of id
2977             if (Ext.isIE && (el.id == undefined || el.id == '')) {
2978                 el.dom = el.dom;
2979             } else {
2980                 el.dom = DOC.getElementById(el.id) || el.dom;
2981             }
2982         }
2983         return el;
2984     } else if(el.isComposite) {
2985         return el;
2986     } else if(Ext.isArray(el)) {
2987         return El.select(el);
2988     } else if(el == DOC) {
2989         // create a bogus element object representing the document object
2990         if(!docEl){
2991             var f = function(){};
2992             f.prototype = El.prototype;
2993             docEl = new f();
2994             docEl.dom = DOC;
2995         }
2996         return docEl;
2997     }
2998     return null;
2999 };
3000
3001 El.addToCache = function(el, id){
3002     id = id || el.id;
3003     EC[id] = {
3004         el:  el,
3005         data: {},
3006         events: {}
3007     };
3008     return el;
3009 };
3010
3011 // private method for getting and setting element data
3012 El.data = function(el, key, value){
3013     el = El.get(el);
3014     if (!el) {
3015         return null;
3016     }
3017     var c = EC[el.id].data;
3018     if(arguments.length == 2){
3019         return c[key];
3020     }else{
3021         return (c[key] = value);
3022     }
3023 };
3024
3025 // private
3026 // Garbage collection - uncache elements/purge listeners on orphaned elements
3027 // so we don't hold a reference and cause the browser to retain them
3028 function garbageCollect(){
3029     if(!Ext.enableGarbageCollector){
3030         clearInterval(El.collectorThreadId);
3031     } else {
3032         var eid,
3033             el,
3034             d,
3035             o;
3036
3037         for(eid in EC){
3038             o = EC[eid];
3039             if(o.skipGC){
3040                 continue;
3041             }
3042             el = o.el;
3043             d = el.dom;
3044             // -------------------------------------------------------
3045             // Determining what is garbage:
3046             // -------------------------------------------------------
3047             // !d
3048             // dom node is null, definitely garbage
3049             // -------------------------------------------------------
3050             // !d.parentNode
3051             // no parentNode == direct orphan, definitely garbage
3052             // -------------------------------------------------------
3053             // !d.offsetParent && !document.getElementById(eid)
3054             // display none elements have no offsetParent so we will
3055             // also try to look it up by it's id. However, check
3056             // offsetParent first so we don't do unneeded lookups.
3057             // This enables collection of elements that are not orphans
3058             // directly, but somewhere up the line they have an orphan
3059             // parent.
3060             // -------------------------------------------------------
3061             if(!d || !d.parentNode || (!d.offsetParent && !DOC.getElementById(eid))){
3062                 if(Ext.enableListenerCollection){
3063                     Ext.EventManager.removeAll(d);
3064                 }
3065                 delete EC[eid];
3066             }
3067         }
3068         // Cleanup IE Object leaks
3069         if (Ext.isIE) {
3070             var t = {};
3071             for (eid in EC) {
3072                 t[eid] = EC[eid];
3073             }
3074             EC = Ext.elCache = t;
3075         }
3076     }
3077 }
3078 El.collectorThreadId = setInterval(garbageCollect, 30000);
3079
3080 var flyFn = function(){};
3081 flyFn.prototype = El.prototype;
3082
3083 // dom is optional
3084 El.Flyweight = function(dom){
3085     this.dom = dom;
3086 };
3087
3088 El.Flyweight.prototype = new flyFn();
3089 El.Flyweight.prototype.isFlyweight = true;
3090 El._flyweights = {};
3091
3092 /**
3093  * <p>Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
3094  * the dom node can be overwritten by other code. Shorthand of {@link Ext.Element#fly}</p>
3095  * <p>Use this to make one-time references to DOM elements which are not going to be accessed again either by
3096  * application code, or by Ext's classes. If accessing an element which will be processed regularly, then {@link Ext#get}
3097  * will be more appropriate to take advantage of the caching provided by the Ext.Element class.</p>
3098  * @param {String/HTMLElement} el The dom node or id
3099  * @param {String} named (optional) Allows for creation of named reusable flyweights to prevent conflicts
3100  * (e.g. internally Ext uses "_global")
3101  * @return {Element} The shared Element object (or null if no matching element was found)
3102  * @member Ext.Element
3103  * @method fly
3104  */
3105 El.fly = function(el, named){
3106     var ret = null;
3107     named = named || '_global';
3108
3109     if (el = Ext.getDom(el)) {
3110         (El._flyweights[named] = El._flyweights[named] || new El.Flyweight()).dom = el;
3111         ret = El._flyweights[named];
3112     }
3113     return ret;
3114 };
3115
3116 /**
3117  * Retrieves Ext.Element objects.
3118  * <p><b>This method does not retrieve {@link Ext.Component Component}s.</b> This method
3119  * retrieves Ext.Element objects which encapsulate DOM elements. To retrieve a Component by
3120  * its ID, use {@link Ext.ComponentMgr#get}.</p>
3121  * <p>Uses simple caching to consistently return the same object. Automatically fixes if an
3122  * object was recreated with the same id via AJAX or DOM.</p>
3123  * Shorthand of {@link Ext.Element#get}
3124  * @param {Mixed} el The id of the node, a DOM Node or an existing Element.
3125  * @return {Element} The Element object (or null if no matching element was found)
3126  * @member Ext
3127  * @method get
3128  */
3129 Ext.get = El.get;
3130
3131 /**
3132  * <p>Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
3133  * the dom node can be overwritten by other code. Shorthand of {@link Ext.Element#fly}</p>
3134  * <p>Use this to make one-time references to DOM elements which are not going to be accessed again either by
3135  * application code, or by Ext's classes. If accessing an element which will be processed regularly, then {@link Ext#get}
3136  * will be more appropriate to take advantage of the caching provided by the Ext.Element class.</p>
3137  * @param {String/HTMLElement} el The dom node or id
3138  * @param {String} named (optional) Allows for creation of named reusable flyweights to prevent conflicts
3139  * (e.g. internally Ext uses "_global")
3140  * @return {Element} The shared Element object (or null if no matching element was found)
3141  * @member Ext
3142  * @method fly
3143  */
3144 Ext.fly = El.fly;
3145
3146 // speedy lookup for elements never to box adjust
3147 var noBoxAdjust = Ext.isStrict ? {
3148     select:1
3149 } : {
3150     input:1, select:1, textarea:1
3151 };
3152 if(Ext.isIE || Ext.isGecko){
3153     noBoxAdjust['button'] = 1;
3154 }
3155
3156 })();
3157 /**
3158  * @class Ext.Element
3159  */
3160 Ext.Element.addMethods(function(){
3161         var PARENTNODE = 'parentNode',
3162                 NEXTSIBLING = 'nextSibling',
3163                 PREVIOUSSIBLING = 'previousSibling',
3164                 DQ = Ext.DomQuery,
3165                 GET = Ext.get;
3166         
3167         return {
3168                 /**
3169              * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
3170              * @param {String} selector The simple selector to test
3171              * @param {Number/Mixed} maxDepth (optional) The max depth to search as a number or element (defaults to 50 || document.body)
3172              * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node
3173              * @return {HTMLElement} The matching DOM node (or null if no match was found)
3174              */
3175             findParent : function(simpleSelector, maxDepth, returnEl){
3176                 var p = this.dom,
3177                         b = document.body, 
3178                         depth = 0,                      
3179                         stopEl;         
3180             if(Ext.isGecko && Object.prototype.toString.call(p) == '[object XULElement]') {
3181                 return null;
3182             }
3183                 maxDepth = maxDepth || 50;
3184                 if (isNaN(maxDepth)) {
3185                     stopEl = Ext.getDom(maxDepth);
3186                     maxDepth = Number.MAX_VALUE;
3187                 }
3188                 while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
3189                     if(DQ.is(p, simpleSelector)){
3190                         return returnEl ? GET(p) : p;
3191                     }
3192                     depth++;
3193                     p = p.parentNode;
3194                 }
3195                 return null;
3196             },
3197         
3198             /**
3199              * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
3200              * @param {String} selector The simple selector to test
3201              * @param {Number/Mixed} maxDepth (optional) The max depth to
3202                     search as a number or element (defaults to 10 || document.body)
3203              * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node
3204              * @return {HTMLElement} The matching DOM node (or null if no match was found)
3205              */
3206             findParentNode : function(simpleSelector, maxDepth, returnEl){
3207                 var p = Ext.fly(this.dom.parentNode, '_internal');
3208                 return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
3209             },
3210         
3211             /**
3212              * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
3213              * This is a shortcut for findParentNode() that always returns an Ext.Element.
3214              * @param {String} selector The simple selector to test
3215              * @param {Number/Mixed} maxDepth (optional) The max depth to
3216                     search as a number or element (defaults to 10 || document.body)
3217              * @return {Ext.Element} The matching DOM node (or null if no match was found)
3218              */
3219             up : function(simpleSelector, maxDepth){
3220                 return this.findParentNode(simpleSelector, maxDepth, true);
3221             },
3222         
3223             /**
3224              * Creates a {@link Ext.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
3225              * @param {String} selector The CSS selector
3226              * @return {CompositeElement/CompositeElementLite} The composite element
3227              */
3228             select : function(selector){
3229                 return Ext.Element.select(selector, this.dom);
3230             },
3231         
3232             /**
3233              * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
3234              * @param {String} selector The CSS selector
3235              * @return {Array} An array of the matched nodes
3236              */
3237             query : function(selector){
3238                 return DQ.select(selector, this.dom);
3239             },
3240         
3241             /**
3242              * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
3243              * @param {String} selector The CSS selector
3244              * @param {Boolean} returnDom (optional) True to return the DOM node instead of Ext.Element (defaults to false)
3245              * @return {HTMLElement/Ext.Element} The child Ext.Element (or DOM node if returnDom = true)
3246              */
3247             child : function(selector, returnDom){
3248                 var n = DQ.selectNode(selector, this.dom);
3249                 return returnDom ? n : GET(n);
3250             },
3251         
3252             /**
3253              * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
3254              * @param {String} selector The CSS selector
3255              * @param {Boolean} returnDom (optional) True to return the DOM node instead of Ext.Element (defaults to false)
3256              * @return {HTMLElement/Ext.Element} The child Ext.Element (or DOM node if returnDom = true)
3257              */
3258             down : function(selector, returnDom){
3259                 var n = DQ.selectNode(" > " + selector, this.dom);
3260                 return returnDom ? n : GET(n);
3261             },
3262         
3263                  /**
3264              * Gets the parent node for this element, optionally chaining up trying to match a selector
3265              * @param {String} selector (optional) Find a parent node that matches the passed simple selector
3266              * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
3267              * @return {Ext.Element/HTMLElement} The parent node or null
3268                  */
3269             parent : function(selector, returnDom){
3270                 return this.matchNode(PARENTNODE, PARENTNODE, selector, returnDom);
3271             },
3272         
3273              /**
3274              * Gets the next sibling, skipping text nodes
3275              * @param {String} selector (optional) Find the next sibling that matches the passed simple selector
3276              * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
3277              * @return {Ext.Element/HTMLElement} The next sibling or null
3278                  */
3279             next : function(selector, returnDom){
3280                 return this.matchNode(NEXTSIBLING, NEXTSIBLING, selector, returnDom);
3281             },
3282         
3283             /**
3284              * Gets the previous sibling, skipping text nodes
3285              * @param {String} selector (optional) Find the previous sibling that matches the passed simple selector
3286              * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
3287              * @return {Ext.Element/HTMLElement} The previous sibling or null
3288                  */
3289             prev : function(selector, returnDom){
3290                 return this.matchNode(PREVIOUSSIBLING, PREVIOUSSIBLING, selector, returnDom);
3291             },
3292         
3293         
3294             /**
3295              * Gets the first child, skipping text nodes
3296              * @param {String} selector (optional) Find the next sibling that matches the passed simple selector
3297              * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
3298              * @return {Ext.Element/HTMLElement} The first child or null
3299                  */
3300             first : function(selector, returnDom){
3301                 return this.matchNode(NEXTSIBLING, 'firstChild', selector, returnDom);
3302             },
3303         
3304             /**
3305              * Gets the last child, skipping text nodes
3306              * @param {String} selector (optional) Find the previous sibling that matches the passed simple selector
3307              * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
3308              * @return {Ext.Element/HTMLElement} The last child or null
3309                  */
3310             last : function(selector, returnDom){
3311                 return this.matchNode(PREVIOUSSIBLING, 'lastChild', selector, returnDom);
3312             },
3313             
3314             matchNode : function(dir, start, selector, returnDom){
3315                 var n = this.dom[start];
3316                 while(n){
3317                     if(n.nodeType == 1 && (!selector || DQ.is(n, selector))){
3318                         return !returnDom ? GET(n) : n;
3319                     }
3320                     n = n[dir];
3321                 }
3322                 return null;
3323             }   
3324     };
3325 }());/**
3326  * @class Ext.Element
3327  */
3328 Ext.Element.addMethods(
3329 function() {
3330         var GETDOM = Ext.getDom,
3331                 GET = Ext.get,
3332                 DH = Ext.DomHelper;
3333         
3334         return {
3335             /**
3336              * Appends the passed element(s) to this element
3337              * @param {String/HTMLElement/Array/Element/CompositeElement} el
3338              * @return {Ext.Element} this
3339              */
3340             appendChild: function(el){        
3341                 return GET(el).appendTo(this);        
3342             },
3343         
3344             /**
3345              * Appends this element to the passed element
3346              * @param {Mixed} el The new parent element
3347              * @return {Ext.Element} this
3348              */
3349             appendTo: function(el){        
3350                 GETDOM(el).appendChild(this.dom);        
3351                 return this;
3352             },
3353         
3354             /**
3355              * Inserts this element before the passed element in the DOM
3356              * @param {Mixed} el The element before which this element will be inserted
3357              * @return {Ext.Element} this
3358              */
3359             insertBefore: function(el){                   
3360                 (el = GETDOM(el)).parentNode.insertBefore(this.dom, el);
3361                 return this;
3362             },
3363         
3364             /**
3365              * Inserts this element after the passed element in the DOM
3366              * @param {Mixed} el The element to insert after
3367              * @return {Ext.Element} this
3368              */
3369             insertAfter: function(el){
3370                 (el = GETDOM(el)).parentNode.insertBefore(this.dom, el.nextSibling);
3371                 return this;
3372             },
3373         
3374             /**
3375              * Inserts (or creates) an element (or DomHelper config) as the first child of this element
3376              * @param {Mixed/Object} el The id or element to insert or a DomHelper config to create and insert
3377              * @return {Ext.Element} The new child
3378              */
3379             insertFirst: function(el, returnDom){
3380             el = el || {};
3381             if(el.nodeType || el.dom || typeof el == 'string'){ // element
3382                 el = GETDOM(el);
3383                 this.dom.insertBefore(el, this.dom.firstChild);
3384                 return !returnDom ? GET(el) : el;
3385             }else{ // dh config
3386                 return this.createChild(el, this.dom.firstChild, returnDom);
3387             }
3388         },
3389         
3390             /**
3391              * Replaces the passed element with this element
3392              * @param {Mixed} el The element to replace
3393              * @return {Ext.Element} this
3394              */
3395             replace: function(el){
3396                 el = GET(el);
3397                 this.insertBefore(el);
3398                 el.remove();
3399                 return this;
3400             },
3401         
3402             /**
3403              * Replaces this element with the passed element
3404              * @param {Mixed/Object} el The new element or a DomHelper config of an element to create
3405              * @return {Ext.Element} this
3406              */
3407             replaceWith: function(el){
3408                     var me = this;
3409                 
3410             if(el.nodeType || el.dom || typeof el == 'string'){
3411                 el = GETDOM(el);
3412                 me.dom.parentNode.insertBefore(el, me.dom);
3413             }else{
3414                 el = DH.insertBefore(me.dom, el);
3415             }
3416                 
3417                 delete Ext.elCache[me.id];
3418                 Ext.removeNode(me.dom);      
3419                 me.id = Ext.id(me.dom = el);
3420                 Ext.Element.addToCache(me.isFlyweight ? new Ext.Element(me.dom) : me);     
3421             return me;
3422             },
3423             
3424                 /**
3425                  * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
3426                  * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
3427                  * automatically generated with the specified attributes.
3428                  * @param {HTMLElement} insertBefore (optional) a child element of this element
3429                  * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
3430                  * @return {Ext.Element} The new child element
3431                  */
3432                 createChild: function(config, insertBefore, returnDom){
3433                     config = config || {tag:'div'};
3434                     return insertBefore ? 
3435                            DH.insertBefore(insertBefore, config, returnDom !== true) :  
3436                            DH[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
3437                 },
3438                 
3439                 /**
3440                  * Creates and wraps this element with another element
3441                  * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
3442                  * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Ext.Element
3443                  * @return {HTMLElement/Element} The newly created wrapper element
3444                  */
3445                 wrap: function(config, returnDom){        
3446                     var newEl = DH.insertBefore(this.dom, config || {tag: "div"}, !returnDom);
3447                     newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
3448                     return newEl;
3449                 },
3450                 
3451                 /**
3452                  * Inserts an html fragment into this element
3453                  * @param {String} where Where to insert the html in relation to this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
3454                  * @param {String} html The HTML fragment
3455                  * @param {Boolean} returnEl (optional) True to return an Ext.Element (defaults to false)
3456                  * @return {HTMLElement/Ext.Element} The inserted node (or nearest related if more than 1 inserted)
3457                  */
3458                 insertHtml : function(where, html, returnEl){
3459                     var el = DH.insertHtml(where, this.dom, html);
3460                     return returnEl ? Ext.get(el) : el;
3461                 }
3462         };
3463 }());/**
3464  * @class Ext.Element
3465  */
3466 Ext.Element.addMethods(function(){
3467     // local style camelizing for speed
3468     var supports = Ext.supports,
3469         propCache = {},
3470         camelRe = /(-[a-z])/gi,
3471         view = document.defaultView,
3472         opacityRe = /alpha\(opacity=(.*)\)/i,
3473         trimRe = /^\s+|\s+$/g,
3474         EL = Ext.Element,
3475         spacesRe = /\s+/,
3476         wordsRe = /\w/g,
3477         PADDING = "padding",
3478         MARGIN = "margin",
3479         BORDER = "border",
3480         LEFT = "-left",
3481         RIGHT = "-right",
3482         TOP = "-top",
3483         BOTTOM = "-bottom",
3484         WIDTH = "-width",
3485         MATH = Math,
3486         HIDDEN = 'hidden',
3487         ISCLIPPED = 'isClipped',
3488         OVERFLOW = 'overflow',
3489         OVERFLOWX = 'overflow-x',
3490         OVERFLOWY = 'overflow-y',
3491         ORIGINALCLIP = 'originalClip',
3492         // special markup used throughout Ext when box wrapping elements
3493         borders = {l: BORDER + LEFT + WIDTH, r: BORDER + RIGHT + WIDTH, t: BORDER + TOP + WIDTH, b: BORDER + BOTTOM + WIDTH},
3494         paddings = {l: PADDING + LEFT, r: PADDING + RIGHT, t: PADDING + TOP, b: PADDING + BOTTOM},
3495         margins = {l: MARGIN + LEFT, r: MARGIN + RIGHT, t: MARGIN + TOP, b: MARGIN + BOTTOM},
3496         data = Ext.Element.data;
3497
3498
3499     // private
3500     function camelFn(m, a) {
3501         return a.charAt(1).toUpperCase();
3502     }
3503
3504     function chkCache(prop) {
3505         return propCache[prop] || (propCache[prop] = prop == 'float' ? (supports.cssFloat ? 'cssFloat' : 'styleFloat') : prop.replace(camelRe, camelFn));
3506     }
3507
3508     return {
3509         // private  ==> used by Fx
3510         adjustWidth : function(width) {
3511             var me = this;
3512             var isNum = (typeof width == "number");
3513             if(isNum && me.autoBoxAdjust && !me.isBorderBox()){
3514                width -= (me.getBorderWidth("lr") + me.getPadding("lr"));
3515             }
3516             return (isNum && width < 0) ? 0 : width;
3517         },
3518
3519         // private   ==> used by Fx
3520         adjustHeight : function(height) {
3521             var me = this;
3522             var isNum = (typeof height == "number");
3523             if(isNum && me.autoBoxAdjust && !me.isBorderBox()){
3524                height -= (me.getBorderWidth("tb") + me.getPadding("tb"));
3525             }
3526             return (isNum && height < 0) ? 0 : height;
3527         },
3528
3529
3530         /**
3531          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
3532          * @param {String/Array} className The CSS class to add, or an array of classes
3533          * @return {Ext.Element} this
3534          */
3535         addClass : function(className){
3536             var me = this,
3537                 i,
3538                 len,
3539                 v,
3540                 cls = [];
3541             // Separate case is for speed
3542             if (!Ext.isArray(className)) {
3543                 if (typeof className == 'string' && !this.hasClass(className)) {
3544                     me.dom.className += " " + className;
3545                 }
3546             }
3547             else {
3548                 for (i = 0, len = className.length; i < len; i++) {
3549                     v = className[i];
3550                     if (typeof v == 'string' && (' ' + me.dom.className + ' ').indexOf(' ' + v + ' ') == -1) {
3551                         cls.push(v);
3552                     }
3553                 }
3554                 if (cls.length) {
3555                     me.dom.className += " " + cls.join(" ");
3556                 }
3557             }
3558             return me;
3559         },
3560
3561         /**
3562          * Removes one or more CSS classes from the element.
3563          * @param {String/Array} className The CSS class to remove, or an array of classes
3564          * @return {Ext.Element} this
3565          */
3566         removeClass : function(className){
3567             var me = this,
3568                 i,
3569                 idx,
3570                 len,
3571                 cls,
3572                 elClasses;
3573             if (!Ext.isArray(className)){
3574                 className = [className];
3575             }
3576             if (me.dom && me.dom.className) {
3577                 elClasses = me.dom.className.replace(trimRe, '').split(spacesRe);
3578                 for (i = 0, len = className.length; i < len; i++) {
3579                     cls = className[i];
3580                     if (typeof cls == 'string') {
3581                         cls = cls.replace(trimRe, '');
3582                         idx = elClasses.indexOf(cls);
3583                         if (idx != -1) {
3584                             elClasses.splice(idx, 1);
3585                         }
3586                     }
3587                 }
3588                 me.dom.className = elClasses.join(" ");
3589             }
3590             return me;
3591         },
3592
3593         /**
3594          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
3595          * @param {String/Array} className The CSS class to add, or an array of classes
3596          * @return {Ext.Element} this
3597          */
3598         radioClass : function(className){
3599             var cn = this.dom.parentNode.childNodes,
3600                 v,
3601                 i,
3602                 len;
3603             className = Ext.isArray(className) ? className : [className];
3604             for (i = 0, len = cn.length; i < len; i++) {
3605                 v = cn[i];
3606                 if (v && v.nodeType == 1) {
3607                     Ext.fly(v, '_internal').removeClass(className);
3608                 }
3609             };
3610             return this.addClass(className);
3611         },
3612
3613         /**
3614          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
3615          * @param {String} className The CSS class to toggle
3616          * @return {Ext.Element} this
3617          */
3618         toggleClass : function(className){
3619             return this.hasClass(className) ? this.removeClass(className) : this.addClass(className);
3620         },
3621
3622         /**
3623          * Checks if the specified CSS class exists on this element's DOM node.
3624          * @param {String} className The CSS class to check for
3625          * @return {Boolean} True if the class exists, else false
3626          */
3627         hasClass : function(className){
3628             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
3629         },
3630
3631         /**
3632          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
3633          * @param {String} oldClassName The CSS class to replace
3634          * @param {String} newClassName The replacement CSS class
3635          * @return {Ext.Element} this
3636          */
3637         replaceClass : function(oldClassName, newClassName){
3638             return this.removeClass(oldClassName).addClass(newClassName);
3639         },
3640
3641         isStyle : function(style, val) {
3642             return this.getStyle(style) == val;
3643         },
3644
3645         /**
3646          * Normalizes currentStyle and computedStyle.
3647          * @param {String} property The style property whose value is returned.
3648          * @return {String} The current value of the style property for this element.
3649          */
3650         getStyle : function(){
3651             return view && view.getComputedStyle ?
3652                 function(prop){
3653                     var el = this.dom,
3654                         v,
3655                         cs,
3656                         out,
3657                         display;
3658
3659                     if(el == document){
3660                         return null;
3661                     }
3662                     prop = chkCache(prop);
3663                     out = (v = el.style[prop]) ? v :
3664                            (cs = view.getComputedStyle(el, "")) ? cs[prop] : null;
3665                            
3666                     // Ignore cases when the margin is correctly reported as 0, the bug only shows
3667                     // numbers larger.
3668                     if(prop == 'marginRight' && out != '0px' && !supports.correctRightMargin){
3669                         display = el.style.display;
3670                         el.style.display = 'inline-block';
3671                         out = view.getComputedStyle(el, '').marginRight;
3672                         el.style.display = display;
3673                     }
3674                     
3675                     if(prop == 'backgroundColor' && out == 'rgba(0, 0, 0, 0)' && !supports.correctTransparentColor){
3676                         out = 'transparent';
3677                     }
3678                     return out;
3679                 } :
3680                 function(prop){
3681                     var el = this.dom,
3682                         m,
3683                         cs;
3684
3685                     if(el == document) return null;
3686                     if (prop == 'opacity') {
3687                         if (el.style.filter.match) {
3688                             if(m = el.style.filter.match(opacityRe)){
3689                                 var fv = parseFloat(m[1]);
3690                                 if(!isNaN(fv)){
3691                                     return fv ? fv / 100 : 0;
3692                                 }
3693                             }
3694                         }
3695                         return 1;
3696                     }
3697                     prop = chkCache(prop);
3698                     return el.style[prop] || ((cs = el.currentStyle) ? cs[prop] : null);
3699                 };
3700         }(),
3701
3702         /**
3703          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
3704          * are convert to standard 6 digit hex color.
3705          * @param {String} attr The css attribute
3706          * @param {String} defaultValue The default value to use when a valid color isn't found
3707          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
3708          * color anims.
3709          */
3710         getColor : function(attr, defaultValue, prefix){
3711             var v = this.getStyle(attr),
3712                 color = (typeof prefix != 'undefined') ? prefix : '#',
3713                 h;
3714
3715             if(!v || (/transparent|inherit/.test(v))) {
3716                 return defaultValue;
3717             }
3718             if(/^r/.test(v)){
3719                 Ext.each(v.slice(4, v.length -1).split(','), function(s){
3720                     h = parseInt(s, 10);
3721                     color += (h < 16 ? '0' : '') + h.toString(16);
3722                 });
3723             }else{
3724                 v = v.replace('#', '');
3725                 color += v.length == 3 ? v.replace(/^(\w)(\w)(\w)$/, '$1$1$2$2$3$3') : v;
3726             }
3727             return(color.length > 5 ? color.toLowerCase() : defaultValue);
3728         },
3729
3730         /**
3731          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
3732          * @param {String/Object} property The style property to be set, or an object of multiple styles.
3733          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
3734          * @return {Ext.Element} this
3735          */
3736         setStyle : function(prop, value){
3737             var tmp, style;
3738             
3739             if (typeof prop != 'object') {
3740                 tmp = {};
3741                 tmp[prop] = value;
3742                 prop = tmp;
3743             }
3744             for (style in prop) {
3745                 value = prop[style];
3746                 style == 'opacity' ?
3747                     this.setOpacity(value) :
3748                     this.dom.style[chkCache(style)] = value;
3749             }
3750             return this;
3751         },
3752
3753         /**
3754          * Set the opacity of the element
3755          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
3756          * @param {Boolean/Object} animate (optional) a standard Element animation config object or <tt>true</tt> for
3757          * the default animation (<tt>{duration: .35, easing: 'easeIn'}</tt>)
3758          * @return {Ext.Element} this
3759          */
3760          setOpacity : function(opacity, animate){
3761             var me = this,
3762                 s = me.dom.style;
3763
3764             if(!animate || !me.anim){
3765                 if(Ext.isIE){
3766                     var opac = opacity < 1 ? 'alpha(opacity=' + opacity * 100 + ')' : '',
3767                     val = s.filter.replace(opacityRe, '').replace(trimRe, '');
3768
3769                     s.zoom = 1;
3770                     s.filter = val + (val.length > 0 ? ' ' : '') + opac;
3771                 }else{
3772                     s.opacity = opacity;
3773                 }
3774             }else{
3775                 me.anim({opacity: {to: opacity}}, me.preanim(arguments, 1), null, .35, 'easeIn');
3776             }
3777             return me;
3778         },
3779
3780         /**
3781          * Clears any opacity settings from this element. Required in some cases for IE.
3782          * @return {Ext.Element} this
3783          */
3784         clearOpacity : function(){
3785             var style = this.dom.style;
3786             if(Ext.isIE){
3787                 if(!Ext.isEmpty(style.filter)){
3788                     style.filter = style.filter.replace(opacityRe, '').replace(trimRe, '');
3789                 }
3790             }else{
3791                 style.opacity = style['-moz-opacity'] = style['-khtml-opacity'] = '';
3792             }
3793             return this;
3794         },
3795
3796         /**
3797          * Returns the offset height of the element
3798          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
3799          * @return {Number} The element's height
3800          */
3801         getHeight : function(contentHeight){
3802             var me = this,
3803                 dom = me.dom,
3804                 hidden = Ext.isIE && me.isStyle('display', 'none'),
3805                 h = MATH.max(dom.offsetHeight, hidden ? 0 : dom.clientHeight) || 0;
3806
3807             h = !contentHeight ? h : h - me.getBorderWidth("tb") - me.getPadding("tb");
3808             return h < 0 ? 0 : h;
3809         },
3810
3811         /**
3812          * Returns the offset width of the element
3813          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
3814          * @return {Number} The element's width
3815          */
3816         getWidth : function(contentWidth){
3817             var me = this,
3818                 dom = me.dom,
3819                 hidden = Ext.isIE && me.isStyle('display', 'none'),
3820                 w = MATH.max(dom.offsetWidth, hidden ? 0 : dom.clientWidth) || 0;
3821             w = !contentWidth ? w : w - me.getBorderWidth("lr") - me.getPadding("lr");
3822             return w < 0 ? 0 : w;
3823         },
3824
3825         /**
3826          * Set the width of this Element.
3827          * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
3828          * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).</li>
3829          * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
3830          * </ul></div>
3831          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
3832          * @return {Ext.Element} this
3833          */
3834         setWidth : function(width, animate){
3835             var me = this;
3836             width = me.adjustWidth(width);
3837             !animate || !me.anim ?
3838                 me.dom.style.width = me.addUnits(width) :
3839                 me.anim({width : {to : width}}, me.preanim(arguments, 1));
3840             return me;
3841         },
3842
3843         /**
3844          * Set the height of this Element.
3845          * <pre><code>
3846 // change the height to 200px and animate with default configuration
3847 Ext.fly('elementId').setHeight(200, true);
3848
3849 // change the height to 150px and animate with a custom configuration
3850 Ext.fly('elId').setHeight(150, {
3851     duration : .5, // animation will have a duration of .5 seconds
3852     // will change the content to "finished"
3853     callback: function(){ this.{@link #update}("finished"); }
3854 });
3855          * </code></pre>
3856          * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
3857          * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels.)</li>
3858          * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
3859          * </ul></div>
3860          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
3861          * @return {Ext.Element} this
3862          */
3863          setHeight : function(height, animate){
3864             var me = this;
3865             height = me.adjustHeight(height);
3866             !animate || !me.anim ?
3867                 me.dom.style.height = me.addUnits(height) :
3868                 me.anim({height : {to : height}}, me.preanim(arguments, 1));
3869             return me;
3870         },
3871
3872         /**
3873          * Gets the width of the border(s) for the specified side(s)
3874          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
3875          * passing <tt>'lr'</tt> would get the border <b><u>l</u></b>eft width + the border <b><u>r</u></b>ight width.
3876          * @return {Number} The width of the sides passed added together
3877          */
3878         getBorderWidth : function(side){
3879             return this.addStyles(side, borders);
3880         },
3881
3882         /**
3883          * Gets the width of the padding(s) for the specified side(s)
3884          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
3885          * passing <tt>'lr'</tt> would get the padding <b><u>l</u></b>eft + the padding <b><u>r</u></b>ight.
3886          * @return {Number} The padding of the sides passed added together
3887          */
3888         getPadding : function(side){
3889             return this.addStyles(side, paddings);
3890         },
3891
3892         /**
3893          *  Store the current overflow setting and clip overflow on the element - use <tt>{@link #unclip}</tt> to remove
3894          * @return {Ext.Element} this
3895          */
3896         clip : function(){
3897             var me = this,
3898                 dom = me.dom;
3899
3900             if(!data(dom, ISCLIPPED)){
3901                 data(dom, ISCLIPPED, true);
3902                 data(dom, ORIGINALCLIP, {
3903                     o: me.getStyle(OVERFLOW),
3904                     x: me.getStyle(OVERFLOWX),
3905                     y: me.getStyle(OVERFLOWY)
3906                 });
3907                 me.setStyle(OVERFLOW, HIDDEN);
3908                 me.setStyle(OVERFLOWX, HIDDEN);
3909                 me.setStyle(OVERFLOWY, HIDDEN);
3910             }
3911             return me;
3912         },
3913
3914         /**
3915          *  Return clipping (overflow) to original clipping before <tt>{@link #clip}</tt> was called
3916          * @return {Ext.Element} this
3917          */
3918         unclip : function(){
3919             var me = this,
3920                 dom = me.dom;
3921
3922             if(data(dom, ISCLIPPED)){
3923                 data(dom, ISCLIPPED, false);
3924                 var o = data(dom, ORIGINALCLIP);
3925                 if(o.o){
3926                     me.setStyle(OVERFLOW, o.o);
3927                 }
3928                 if(o.x){
3929                     me.setStyle(OVERFLOWX, o.x);
3930                 }
3931                 if(o.y){
3932                     me.setStyle(OVERFLOWY, o.y);
3933                 }
3934             }
3935             return me;
3936         },
3937
3938         // private
3939         addStyles : function(sides, styles){
3940             var ttlSize = 0,
3941                 sidesArr = sides.match(wordsRe),
3942                 side,
3943                 size,
3944                 i,
3945                 len = sidesArr.length;
3946             for (i = 0; i < len; i++) {
3947                 side = sidesArr[i];
3948                 size = side && parseInt(this.getStyle(styles[side]), 10);
3949                 if (size) {
3950                     ttlSize += MATH.abs(size);
3951                 }
3952             }
3953             return ttlSize;
3954         },
3955
3956         margins : margins
3957     };
3958 }()
3959 );
3960 /**
3961  * @class Ext.Element
3962  */
3963 (function(){
3964 var D = Ext.lib.Dom,
3965         LEFT = "left",
3966         RIGHT = "right",
3967         TOP = "top",
3968         BOTTOM = "bottom",
3969         POSITION = "position",
3970         STATIC = "static",
3971         RELATIVE = "relative",
3972         AUTO = "auto",
3973         ZINDEX = "z-index";
3974
3975 Ext.Element.addMethods({
3976         /**
3977       * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
3978       * @return {Number} The X position of the element
3979       */
3980     getX : function(){
3981         return D.getX(this.dom);
3982     },
3983
3984     /**
3985       * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
3986       * @return {Number} The Y position of the element
3987       */
3988     getY : function(){
3989         return D.getY(this.dom);
3990     },
3991
3992     /**
3993       * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
3994       * @return {Array} The XY position of the element
3995       */
3996     getXY : function(){
3997         return D.getXY(this.dom);
3998     },
3999
4000     /**
4001       * Returns the offsets of this element from the passed element. Both element must be part of the DOM tree and not have display:none to have page coordinates.
4002       * @param {Mixed} element The element to get the offsets from.
4003       * @return {Array} The XY page offsets (e.g. [100, -200])
4004       */
4005     getOffsetsTo : function(el){
4006         var o = this.getXY(),
4007                 e = Ext.fly(el, '_internal').getXY();
4008         return [o[0]-e[0],o[1]-e[1]];
4009     },
4010
4011     /**
4012      * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
4013      * @param {Number} The X position of the element
4014      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
4015      * @return {Ext.Element} this
4016      */
4017     setX : function(x, animate){            
4018             return this.setXY([x, this.getY()], this.animTest(arguments, animate, 1));
4019     },
4020
4021     /**
4022      * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
4023      * @param {Number} The Y position of the element
4024      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
4025      * @return {Ext.Element} this
4026      */
4027     setY : function(y, animate){            
4028             return this.setXY([this.getX(), y], this.animTest(arguments, animate, 1));
4029     },
4030
4031     /**
4032      * Sets the element's left position directly using CSS style (instead of {@link #setX}).
4033      * @param {String} left The left CSS property value
4034      * @return {Ext.Element} this
4035      */
4036     setLeft : function(left){
4037         this.setStyle(LEFT, this.addUnits(left));
4038         return this;
4039     },
4040
4041     /**
4042      * Sets the element's top position directly using CSS style (instead of {@link #setY}).
4043      * @param {String} top The top CSS property value
4044      * @return {Ext.Element} this
4045      */
4046     setTop : function(top){
4047         this.setStyle(TOP, this.addUnits(top));
4048         return this;
4049     },
4050
4051     /**
4052      * Sets the element's CSS right style.
4053      * @param {String} right The right CSS property value
4054      * @return {Ext.Element} this
4055      */
4056     setRight : function(right){
4057         this.setStyle(RIGHT, this.addUnits(right));
4058         return this;
4059     },
4060
4061     /**
4062      * Sets the element's CSS bottom style.
4063      * @param {String} bottom The bottom CSS property value
4064      * @return {Ext.Element} this
4065      */
4066     setBottom : function(bottom){
4067         this.setStyle(BOTTOM, this.addUnits(bottom));
4068         return this;
4069     },
4070
4071     /**
4072      * Sets the position of the element in page coordinates, regardless of how the element is positioned.
4073      * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
4074      * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
4075      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
4076      * @return {Ext.Element} this
4077      */
4078     setXY : function(pos, animate){
4079             var me = this;
4080         if(!animate || !me.anim){
4081             D.setXY(me.dom, pos);
4082         }else{
4083             me.anim({points: {to: pos}}, me.preanim(arguments, 1), 'motion');
4084         }
4085         return me;
4086     },
4087
4088     /**
4089      * Sets the position of the element in page coordinates, regardless of how the element is positioned.
4090      * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
4091      * @param {Number} x X value for new position (coordinates are page-based)
4092      * @param {Number} y Y value for new position (coordinates are page-based)
4093      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
4094      * @return {Ext.Element} this
4095      */
4096     setLocation : function(x, y, animate){
4097         return this.setXY([x, y], this.animTest(arguments, animate, 2));
4098     },
4099
4100     /**
4101      * Sets the position of the element in page coordinates, regardless of how the element is positioned.
4102      * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
4103      * @param {Number} x X value for new position (coordinates are page-based)
4104      * @param {Number} y Y value for new position (coordinates are page-based)
4105      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
4106      * @return {Ext.Element} this
4107      */
4108     moveTo : function(x, y, animate){
4109         return this.setXY([x, y], this.animTest(arguments, animate, 2));        
4110     },    
4111     
4112     /**
4113      * Gets the left X coordinate
4114      * @param {Boolean} local True to get the local css position instead of page coordinate
4115      * @return {Number}
4116      */
4117     getLeft : function(local){
4118             return !local ? this.getX() : parseInt(this.getStyle(LEFT), 10) || 0;
4119     },
4120
4121     /**
4122      * Gets the right X coordinate of the element (element X position + element width)
4123      * @param {Boolean} local True to get the local css position instead of page coordinate
4124      * @return {Number}
4125      */
4126     getRight : function(local){
4127             var me = this;
4128             return !local ? me.getX() + me.getWidth() : (me.getLeft(true) + me.getWidth()) || 0;
4129     },
4130
4131     /**
4132      * Gets the top Y coordinate
4133      * @param {Boolean} local True to get the local css position instead of page coordinate
4134      * @return {Number}
4135      */
4136     getTop : function(local) {
4137             return !local ? this.getY() : parseInt(this.getStyle(TOP), 10) || 0;
4138     },
4139
4140     /**
4141      * Gets the bottom Y coordinate of the element (element Y position + element height)
4142      * @param {Boolean} local True to get the local css position instead of page coordinate
4143      * @return {Number}
4144      */
4145     getBottom : function(local){
4146             var me = this;
4147             return !local ? me.getY() + me.getHeight() : (me.getTop(true) + me.getHeight()) || 0;
4148     },
4149
4150     /**
4151     * Initializes positioning on this element. If a desired position is not passed, it will make the
4152     * the element positioned relative IF it is not already positioned.
4153     * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
4154     * @param {Number} zIndex (optional) The zIndex to apply
4155     * @param {Number} x (optional) Set the page X position
4156     * @param {Number} y (optional) Set the page Y position
4157     */
4158     position : function(pos, zIndex, x, y){
4159             var me = this;
4160             
4161         if(!pos && me.isStyle(POSITION, STATIC)){           
4162             me.setStyle(POSITION, RELATIVE);           
4163         } else if(pos) {
4164             me.setStyle(POSITION, pos);
4165         }
4166         if(zIndex){
4167             me.setStyle(ZINDEX, zIndex);
4168         }
4169         if(x || y) me.setXY([x || false, y || false]);
4170     },
4171
4172     /**
4173     * Clear positioning back to the default when the document was loaded
4174     * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
4175     * @return {Ext.Element} this
4176      */
4177     clearPositioning : function(value){
4178         value = value || '';
4179         this.setStyle({
4180             left : value,
4181             right : value,
4182             top : value,
4183             bottom : value,
4184             "z-index" : "",
4185             position : STATIC
4186         });
4187         return this;
4188     },
4189
4190     /**
4191     * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
4192     * snapshot before performing an update and then restoring the element.
4193     * @return {Object}
4194     */
4195     getPositioning : function(){
4196         var l = this.getStyle(LEFT);
4197         var t = this.getStyle(TOP);
4198         return {
4199             "position" : this.getStyle(POSITION),
4200             "left" : l,
4201             "right" : l ? "" : this.getStyle(RIGHT),
4202             "top" : t,
4203             "bottom" : t ? "" : this.getStyle(BOTTOM),
4204             "z-index" : this.getStyle(ZINDEX)
4205         };
4206     },
4207     
4208     /**
4209     * Set positioning with an object returned by getPositioning().
4210     * @param {Object} posCfg
4211     * @return {Ext.Element} this
4212      */
4213     setPositioning : function(pc){
4214             var me = this,
4215                 style = me.dom.style;
4216                 
4217         me.setStyle(pc);
4218         
4219         if(pc.right == AUTO){
4220             style.right = "";
4221         }
4222         if(pc.bottom == AUTO){
4223             style.bottom = "";
4224         }
4225         
4226         return me;
4227     },    
4228         
4229     /**
4230      * Translates the passed page coordinates into left/top css values for this element
4231      * @param {Number/Array} x The page x or an array containing [x, y]
4232      * @param {Number} y (optional) The page y, required if x is not an array
4233      * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
4234      */
4235     translatePoints : function(x, y){                
4236             y = isNaN(x[1]) ? y : x[1];
4237         x = isNaN(x[0]) ? x : x[0];
4238         var me = this,
4239                 relative = me.isStyle(POSITION, RELATIVE),
4240                 o = me.getXY(),
4241                 l = parseInt(me.getStyle(LEFT), 10),
4242                 t = parseInt(me.getStyle(TOP), 10);
4243         
4244         l = !isNaN(l) ? l : (relative ? 0 : me.dom.offsetLeft);
4245         t = !isNaN(t) ? t : (relative ? 0 : me.dom.offsetTop);        
4246
4247         return {left: (x - o[0] + l), top: (y - o[1] + t)}; 
4248     },
4249     
4250     animTest : function(args, animate, i) {
4251         return !!animate && this.preanim ? this.preanim(args, i) : false;
4252     }
4253 });
4254 })();/**
4255  * @class Ext.Element
4256  */
4257 Ext.Element.addMethods({
4258     /**
4259      * Returns true if this element is scrollable.
4260      * @return {Boolean}
4261      */
4262     isScrollable : function(){
4263         var dom = this.dom;
4264         return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
4265     },
4266
4267     /**
4268      * 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().
4269      * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
4270      * @param {Number} value The new scroll value.
4271      * @return {Element} this
4272      */
4273     scrollTo : function(side, value){
4274         this.dom["scroll" + (/top/i.test(side) ? "Top" : "Left")] = value;
4275         return this;
4276     },
4277
4278     /**
4279      * Returns the current scroll position of the element.
4280      * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
4281      */
4282     getScroll : function(){
4283         var d = this.dom, 
4284             doc = document,
4285             body = doc.body,
4286             docElement = doc.documentElement,
4287             l,
4288             t,
4289             ret;
4290
4291         if(d == doc || d == body){
4292             if(Ext.isIE && Ext.isStrict){
4293                 l = docElement.scrollLeft; 
4294                 t = docElement.scrollTop;
4295             }else{
4296                 l = window.pageXOffset;
4297                 t = window.pageYOffset;
4298             }
4299             ret = {left: l || (body ? body.scrollLeft : 0), top: t || (body ? body.scrollTop : 0)};
4300         }else{
4301             ret = {left: d.scrollLeft, top: d.scrollTop};
4302         }
4303         return ret;
4304     }
4305 });/**
4306  * @class Ext.Element
4307  */
4308 /**
4309  * Visibility mode constant for use with {@link #setVisibilityMode}. Use visibility to hide element
4310  * @static
4311  * @type Number
4312  */
4313 Ext.Element.VISIBILITY = 1;
4314 /**
4315  * Visibility mode constant for use with {@link #setVisibilityMode}. Use display to hide element
4316  * @static
4317  * @type Number
4318  */
4319 Ext.Element.DISPLAY = 2;
4320
4321 /**
4322  * Visibility mode constant for use with {@link #setVisibilityMode}. Use offsets (x and y positioning offscreen)
4323  * to hide element.
4324  * @static
4325  * @type Number
4326  */
4327 Ext.Element.OFFSETS = 3;
4328
4329
4330 Ext.Element.ASCLASS = 4;
4331
4332 /**
4333  * Defaults to 'x-hide-nosize'
4334  * @static
4335  * @type String
4336  */
4337 Ext.Element.visibilityCls = 'x-hide-nosize';
4338
4339 Ext.Element.addMethods(function(){
4340     var El = Ext.Element,
4341         OPACITY = "opacity",
4342         VISIBILITY = "visibility",
4343         DISPLAY = "display",
4344         HIDDEN = "hidden",
4345         OFFSETS = "offsets",
4346         ASCLASS = "asclass",
4347         NONE = "none",
4348         NOSIZE = 'nosize',
4349         ORIGINALDISPLAY = 'originalDisplay',
4350         VISMODE = 'visibilityMode',
4351         ISVISIBLE = 'isVisible',
4352         data = El.data,
4353         getDisplay = function(dom){
4354             var d = data(dom, ORIGINALDISPLAY);
4355             if(d === undefined){
4356                 data(dom, ORIGINALDISPLAY, d = '');
4357             }
4358             return d;
4359         },
4360         getVisMode = function(dom){
4361             var m = data(dom, VISMODE);
4362             if(m === undefined){
4363                 data(dom, VISMODE, m = 1);
4364             }
4365             return m;
4366         };
4367
4368     return {
4369         /**
4370          * The element's default display mode  (defaults to "")
4371          * @type String
4372          */
4373         originalDisplay : "",
4374         visibilityMode : 1,
4375
4376         /**
4377          * Sets the element's visibility mode. When setVisible() is called it
4378          * will use this to determine whether to set the visibility or the display property.
4379          * @param {Number} visMode Ext.Element.VISIBILITY or Ext.Element.DISPLAY
4380          * @return {Ext.Element} this
4381          */
4382         setVisibilityMode : function(visMode){
4383             data(this.dom, VISMODE, visMode);
4384             return this;
4385         },
4386
4387         /**
4388          * Perform custom animation on this element.
4389          * <div><ul class="mdetail-params">
4390          * <li><u>Animation Properties</u></li>
4391          *
4392          * <p>The Animation Control Object enables gradual transitions for any member of an
4393          * element's style object that takes a numeric value including but not limited to
4394          * these properties:</p><div><ul class="mdetail-params">
4395          * <li><tt>bottom, top, left, right</tt></li>
4396          * <li><tt>height, width</tt></li>
4397          * <li><tt>margin, padding</tt></li>
4398          * <li><tt>borderWidth</tt></li>
4399          * <li><tt>opacity</tt></li>
4400          * <li><tt>fontSize</tt></li>
4401          * <li><tt>lineHeight</tt></li>
4402          * </ul></div>
4403          *
4404          *
4405          * <li><u>Animation Property Attributes</u></li>
4406          *
4407          * <p>Each Animation Property is a config object with optional properties:</p>
4408          * <div><ul class="mdetail-params">
4409          * <li><tt>by</tt>*  : relative change - start at current value, change by this value</li>
4410          * <li><tt>from</tt> : ignore current value, start from this value</li>
4411          * <li><tt>to</tt>*  : start at current value, go to this value</li>
4412          * <li><tt>unit</tt> : any allowable unit specification</li>
4413          * <p>* do not specify both <tt>to</tt> and <tt>by</tt> for an animation property</p>
4414          * </ul></div>
4415          *
4416          * <li><u>Animation Types</u></li>
4417          *
4418          * <p>The supported animation types:</p><div><ul class="mdetail-params">
4419          * <li><tt>'run'</tt> : Default
4420          * <pre><code>
4421 var el = Ext.get('complexEl');
4422 el.animate(
4423     // animation control object
4424     {
4425         borderWidth: {to: 3, from: 0},
4426         opacity: {to: .3, from: 1},
4427         height: {to: 50, from: el.getHeight()},
4428         width: {to: 300, from: el.getWidth()},
4429         top  : {by: - 100, unit: 'px'},
4430     },
4431     0.35,      // animation duration
4432     null,      // callback
4433     'easeOut', // easing method
4434     'run'      // animation type ('run','color','motion','scroll')
4435 );
4436          * </code></pre>
4437          * </li>
4438          * <li><tt>'color'</tt>
4439          * <p>Animates transition of background, text, or border colors.</p>
4440          * <pre><code>
4441 el.animate(
4442     // animation control object
4443     {
4444         color: { to: '#06e' },
4445         backgroundColor: { to: '#e06' }
4446     },
4447     0.35,      // animation duration
4448     null,      // callback
4449     'easeOut', // easing method
4450     'color'    // animation type ('run','color','motion','scroll')
4451 );
4452          * </code></pre>
4453          * </li>
4454          *
4455          * <li><tt>'motion'</tt>
4456          * <p>Animates the motion of an element to/from specific points using optional bezier
4457          * way points during transit.</p>
4458          * <pre><code>
4459 el.animate(
4460     // animation control object
4461     {
4462         borderWidth: {to: 3, from: 0},
4463         opacity: {to: .3, from: 1},
4464         height: {to: 50, from: el.getHeight()},
4465         width: {to: 300, from: el.getWidth()},
4466         top  : {by: - 100, unit: 'px'},
4467         points: {
4468             to: [50, 100],  // go to this point
4469             control: [      // optional bezier way points
4470                 [ 600, 800],
4471                 [-100, 200]
4472             ]
4473         }
4474     },
4475     3000,      // animation duration (milliseconds!)
4476     null,      // callback
4477     'easeOut', // easing method
4478     'motion'   // animation type ('run','color','motion','scroll')
4479 );
4480          * </code></pre>
4481          * </li>
4482          * <li><tt>'scroll'</tt>
4483          * <p>Animate horizontal or vertical scrolling of an overflowing page element.</p>
4484          * <pre><code>
4485 el.animate(
4486     // animation control object
4487     {
4488         scroll: {to: [400, 300]}
4489     },
4490     0.35,      // animation duration
4491     null,      // callback
4492     'easeOut', // easing method
4493     'scroll'   // animation type ('run','color','motion','scroll')
4494 );
4495          * </code></pre>
4496          * </li>
4497          * </ul></div>
4498          *
4499          * </ul></div>
4500          *
4501          * @param {Object} args The animation control args
4502          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to <tt>.35</tt>)
4503          * @param {Function} onComplete (optional) Function to call when animation completes
4504          * @param {String} easing (optional) {@link Ext.Fx#easing} method to use (defaults to <tt>'easeOut'</tt>)
4505          * @param {String} animType (optional) <tt>'run'</tt> is the default. Can also be <tt>'color'</tt>,
4506          * <tt>'motion'</tt>, or <tt>'scroll'</tt>
4507          * @return {Ext.Element} this
4508          */
4509         animate : function(args, duration, onComplete, easing, animType){
4510             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
4511             return this;
4512         },
4513
4514         /*
4515          * @private Internal animation call
4516          */
4517         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
4518             animType = animType || 'run';
4519             opt = opt || {};
4520             var me = this,
4521                 anim = Ext.lib.Anim[animType](
4522                     me.dom,
4523                     args,
4524                     (opt.duration || defaultDur) || .35,
4525                     (opt.easing || defaultEase) || 'easeOut',
4526                     function(){
4527                         if(cb) cb.call(me);
4528                         if(opt.callback) opt.callback.call(opt.scope || me, me, opt);
4529                     },
4530                     me
4531                 );
4532             opt.anim = anim;
4533             return anim;
4534         },
4535
4536         // private legacy anim prep
4537         preanim : function(a, i){
4538             return !a[i] ? false : (typeof a[i] == 'object' ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
4539         },
4540
4541         /**
4542          * Checks whether the element is currently visible using both visibility and display properties.
4543          * @return {Boolean} True if the element is currently visible, else false
4544          */
4545         isVisible : function() {
4546             var me = this,
4547                 dom = me.dom,
4548                 visible = data(dom, ISVISIBLE);
4549
4550             if(typeof visible == 'boolean'){ //return the cached value if registered
4551                 return visible;
4552             }
4553             //Determine the current state based on display states
4554             visible = !me.isStyle(VISIBILITY, HIDDEN) &&
4555                       !me.isStyle(DISPLAY, NONE) &&
4556                       !((getVisMode(dom) == El.ASCLASS) && me.hasClass(me.visibilityCls || El.visibilityCls));
4557
4558             data(dom, ISVISIBLE, visible);
4559             return visible;
4560         },
4561
4562         /**
4563          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
4564          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
4565          * @param {Boolean} visible Whether the element is visible
4566          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
4567          * @return {Ext.Element} this
4568          */
4569         setVisible : function(visible, animate){
4570             var me = this, isDisplay, isVisibility, isOffsets, isNosize,
4571                 dom = me.dom,
4572                 visMode = getVisMode(dom);
4573
4574
4575             // hideMode string override
4576             if (typeof animate == 'string'){
4577                 switch (animate) {
4578                     case DISPLAY:
4579                         visMode = El.DISPLAY;
4580                         break;
4581                     case VISIBILITY:
4582                         visMode = El.VISIBILITY;
4583                         break;
4584                     case OFFSETS:
4585                         visMode = El.OFFSETS;
4586                         break;
4587                     case NOSIZE:
4588                     case ASCLASS:
4589                         visMode = El.ASCLASS;
4590                         break;
4591                 }
4592                 me.setVisibilityMode(visMode);
4593                 animate = false;
4594             }
4595
4596             if (!animate || !me.anim) {
4597                 if(visMode == El.ASCLASS ){
4598
4599                     me[visible?'removeClass':'addClass'](me.visibilityCls || El.visibilityCls);
4600
4601                 } else if (visMode == El.DISPLAY){
4602
4603                     return me.setDisplayed(visible);
4604
4605                 } else if (visMode == El.OFFSETS){
4606
4607                     if (!visible){
4608                         me.hideModeStyles = {
4609                             position: me.getStyle('position'),
4610                             top: me.getStyle('top'),
4611                             left: me.getStyle('left')
4612                         };
4613                         me.applyStyles({position: 'absolute', top: '-10000px', left: '-10000px'});
4614                     } else {
4615                         me.applyStyles(me.hideModeStyles || {position: '', top: '', left: ''});
4616                         delete me.hideModeStyles;
4617                     }
4618
4619                 }else{
4620                     me.fixDisplay();
4621                     dom.style.visibility = visible ? "visible" : HIDDEN;
4622                 }
4623             }else{
4624                 // closure for composites
4625                 if(visible){
4626                     me.setOpacity(.01);
4627                     me.setVisible(true);
4628                 }
4629                 me.anim({opacity: { to: (visible?1:0) }},
4630                         me.preanim(arguments, 1),
4631                         null,
4632                         .35,
4633                         'easeIn',
4634                         function(){
4635                             visible || me.setVisible(false).setOpacity(1);
4636                         });
4637             }
4638             data(dom, ISVISIBLE, visible);  //set logical visibility state
4639             return me;
4640         },
4641
4642
4643         /**
4644          * @private
4645          * Determine if the Element has a relevant height and width available based
4646          * upon current logical visibility state
4647          */
4648         hasMetrics  : function(){
4649             var dom = this.dom;
4650             return this.isVisible() || (getVisMode(dom) == El.VISIBILITY);
4651         },
4652
4653         /**
4654          * Toggles the element's visibility or display, depending on visibility mode.
4655          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
4656          * @return {Ext.Element} this
4657          */
4658         toggle : function(animate){
4659             var me = this;
4660             me.setVisible(!me.isVisible(), me.preanim(arguments, 0));
4661             return me;
4662         },
4663
4664         /**
4665          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
4666          * @param {Mixed} value Boolean value to display the element using its default display, or a string to set the display directly.
4667          * @return {Ext.Element} this
4668          */
4669         setDisplayed : function(value) {
4670             if(typeof value == "boolean"){
4671                value = value ? getDisplay(this.dom) : NONE;
4672             }
4673             this.setStyle(DISPLAY, value);
4674             return this;
4675         },
4676
4677         // private
4678         fixDisplay : function(){
4679             var me = this;
4680             if(me.isStyle(DISPLAY, NONE)){
4681                 me.setStyle(VISIBILITY, HIDDEN);
4682                 me.setStyle(DISPLAY, getDisplay(this.dom)); // first try reverting to default
4683                 if(me.isStyle(DISPLAY, NONE)){ // if that fails, default to block
4684                     me.setStyle(DISPLAY, "block");
4685                 }
4686             }
4687         },
4688
4689         /**
4690          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
4691          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
4692          * @return {Ext.Element} this
4693          */
4694         hide : function(animate){
4695             // hideMode override
4696             if (typeof animate == 'string'){
4697                 this.setVisible(false, animate);
4698                 return this;
4699             }
4700             this.setVisible(false, this.preanim(arguments, 0));
4701             return this;
4702         },
4703
4704         /**
4705         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
4706         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
4707          * @return {Ext.Element} this
4708          */
4709         show : function(animate){
4710             // hideMode override
4711             if (typeof animate == 'string'){
4712                 this.setVisible(true, animate);
4713                 return this;
4714             }
4715             this.setVisible(true, this.preanim(arguments, 0));
4716             return this;
4717         }
4718     };
4719 }());(function(){
4720     // contants
4721     var NULL = null,
4722         UNDEFINED = undefined,
4723         TRUE = true,
4724         FALSE = false,
4725         SETX = "setX",
4726         SETY = "setY",
4727         SETXY = "setXY",
4728         LEFT = "left",
4729         BOTTOM = "bottom",
4730         TOP = "top",
4731         RIGHT = "right",
4732         HEIGHT = "height",
4733         WIDTH = "width",
4734         POINTS = "points",
4735         HIDDEN = "hidden",
4736         ABSOLUTE = "absolute",
4737         VISIBLE = "visible",
4738         MOTION = "motion",
4739         POSITION = "position",
4740         EASEOUT = "easeOut",
4741         /*
4742          * Use a light flyweight here since we are using so many callbacks and are always assured a DOM element
4743          */
4744         flyEl = new Ext.Element.Flyweight(),
4745         queues = {},
4746         getObject = function(o){
4747             return o || {};
4748         },
4749         fly = function(dom){
4750             flyEl.dom = dom;
4751             flyEl.id = Ext.id(dom);
4752             return flyEl;
4753         },
4754         /*
4755          * Queueing now stored outside of the element due to closure issues
4756          */
4757         getQueue = function(id){
4758             if(!queues[id]){
4759                 queues[id] = [];
4760             }
4761             return queues[id];
4762         },
4763         setQueue = function(id, value){
4764             queues[id] = value;
4765         };
4766         
4767 //Notifies Element that fx methods are available
4768 Ext.enableFx = TRUE;
4769
4770 /**
4771  * @class Ext.Fx
4772  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
4773  * to the {@link Ext.Element} interface when included, so all effects calls should be performed via {@link Ext.Element}.
4774  * Conversely, since the effects are not actually defined in {@link Ext.Element}, Ext.Fx <b>must</b> be
4775  * {@link Ext#enableFx included} in order for the Element effects to work.</p><br/>
4776  * 
4777  * <p><b><u>Method Chaining</u></b></p>
4778  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
4779  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
4780  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
4781  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
4782  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
4783  * expected results and should be done with care.  Also see <tt>{@link #callback}</tt>.</p><br/>
4784  *
4785  * <p><b><u>Anchor Options for Motion Effects</u></b></p>
4786  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
4787  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
4788 <pre>
4789 Value  Description
4790 -----  -----------------------------
4791 tl     The top left corner
4792 t      The center of the top edge
4793 tr     The top right corner
4794 l      The center of the left edge
4795 r      The center of the right edge
4796 bl     The bottom left corner
4797 b      The center of the bottom edge
4798 br     The bottom right corner
4799 </pre>
4800  * <b>Note</b>: some Fx methods accept specific custom config parameters.  The options shown in the Config Options
4801  * section below are common options that can be passed to any Fx method unless otherwise noted.</b>
4802  * 
4803  * @cfg {Function} callback A function called when the effect is finished.  Note that effects are queued internally by the
4804  * Fx class, so a callback is not required to specify another effect -- effects can simply be chained together
4805  * and called in sequence (see note for <b><u>Method Chaining</u></b> above), for example:<pre><code>
4806  * el.slideIn().highlight();
4807  * </code></pre>
4808  * The callback is intended for any additional code that should run once a particular effect has completed. The Element
4809  * being operated upon is passed as the first parameter.
4810  * 
4811  * @cfg {Object} scope The scope (<code>this</code> reference) in which the <tt>{@link #callback}</tt> function is executed. Defaults to the browser window.
4812  * 
4813  * @cfg {String} easing A valid Ext.lib.Easing value for the effect:</p><div class="mdetail-params"><ul>
4814  * <li><b><tt>backBoth</tt></b></li>
4815  * <li><b><tt>backIn</tt></b></li>
4816  * <li><b><tt>backOut</tt></b></li>
4817  * <li><b><tt>bounceBoth</tt></b></li>
4818  * <li><b><tt>bounceIn</tt></b></li>
4819  * <li><b><tt>bounceOut</tt></b></li>
4820  * <li><b><tt>easeBoth</tt></b></li>
4821  * <li><b><tt>easeBothStrong</tt></b></li>
4822  * <li><b><tt>easeIn</tt></b></li>
4823  * <li><b><tt>easeInStrong</tt></b></li>
4824  * <li><b><tt>easeNone</tt></b></li>
4825  * <li><b><tt>easeOut</tt></b></li>
4826  * <li><b><tt>easeOutStrong</tt></b></li>
4827  * <li><b><tt>elasticBoth</tt></b></li>
4828  * <li><b><tt>elasticIn</tt></b></li>
4829  * <li><b><tt>elasticOut</tt></b></li>
4830  * </ul></div>
4831  *
4832  * @cfg {String} afterCls A css class to apply after the effect
4833  * @cfg {Number} duration The length of time (in seconds) that the effect should last
4834  * 
4835  * @cfg {Number} endOpacity Only applicable for {@link #fadeIn} or {@link #fadeOut}, a number between
4836  * <tt>0</tt> and <tt>1</tt> inclusive to configure the ending opacity value.
4837  *  
4838  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
4839  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
4840  * effects that end with the element being visually hidden, ignored otherwise)
4841  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. <tt>"width:100px"</tt>, or an object
4842  * in the form <tt>{width:"100px"}</tt>, or a function which returns such a specification that will be applied to the
4843  * Element after the effect finishes.
4844  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
4845  * @cfg {Boolean} concurrent Whether to allow subsequently-queued effects to run at the same time as the current effect, or to ensure that they run in sequence
4846  * @cfg {Boolean} stopFx Whether preceding effects should be stopped and removed before running current effect (only applies to non blocking effects)
4847  */
4848 Ext.Fx = {
4849     
4850     // private - calls the function taking arguments from the argHash based on the key.  Returns the return value of the function.
4851     //           this is useful for replacing switch statements (for example).
4852     switchStatements : function(key, fn, argHash){
4853         return fn.apply(this, argHash[key]);
4854     },
4855     
4856     /**
4857      * Slides the element into view.  An anchor point can be optionally passed to set the point of
4858      * origin for the slide effect.  This function automatically handles wrapping the element with
4859      * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
4860      * Usage:
4861      *<pre><code>
4862 // default: slide the element in from the top
4863 el.slideIn();
4864
4865 // custom: slide the element in from the right with a 2-second duration
4866 el.slideIn('r', { duration: 2 });
4867
4868 // common config options shown with default values
4869 el.slideIn('t', {
4870     easing: 'easeOut',
4871     duration: .5
4872 });
4873 </code></pre>
4874      * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
4875      * @param {Object} options (optional) Object literal with any of the Fx config options
4876      * @return {Ext.Element} The Element
4877      */
4878     slideIn : function(anchor, o){ 
4879         o = getObject(o);
4880         var me = this,
4881             dom = me.dom,
4882             st = dom.style,
4883             xy,
4884             r,
4885             b,              
4886             wrap,               
4887             after,
4888             st,
4889             args, 
4890             pt,
4891             bw,
4892             bh;
4893             
4894         anchor = anchor || "t";
4895
4896         me.queueFx(o, function(){            
4897             xy = fly(dom).getXY();
4898             // fix display to visibility
4899             fly(dom).fixDisplay();            
4900             
4901             // restore values after effect
4902             r = fly(dom).getFxRestore();      
4903             b = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: dom.offsetWidth, height: dom.offsetHeight};
4904             b.right = b.x + b.width;
4905             b.bottom = b.y + b.height;
4906             
4907             // fixed size for slide
4908             fly(dom).setWidth(b.width).setHeight(b.height);            
4909             
4910             // wrap if needed
4911             wrap = fly(dom).fxWrap(r.pos, o, HIDDEN);
4912             
4913             st.visibility = VISIBLE;
4914             st.position = ABSOLUTE;
4915             
4916             // clear out temp styles after slide and unwrap
4917             function after(){
4918                  fly(dom).fxUnwrap(wrap, r.pos, o);
4919                  st.width = r.width;
4920                  st.height = r.height;
4921                  fly(dom).afterFx(o);
4922             }
4923             
4924             // time to calculate the positions        
4925             pt = {to: [b.x, b.y]}; 
4926             bw = {to: b.width};
4927             bh = {to: b.height};
4928                 
4929             function argCalc(wrap, style, ww, wh, sXY, sXYval, s1, s2, w, h, p){                    
4930                 var ret = {};
4931                 fly(wrap).setWidth(ww).setHeight(wh);
4932                 if(fly(wrap)[sXY]){
4933                     fly(wrap)[sXY](sXYval);                  
4934                 }
4935                 style[s1] = style[s2] = "0";                    
4936                 if(w){
4937                     ret.width = w;
4938                 }
4939                 if(h){
4940                     ret.height = h;
4941                 }
4942                 if(p){
4943                     ret.points = p;
4944                 }
4945                 return ret;
4946             };
4947
4948             args = fly(dom).switchStatements(anchor.toLowerCase(), argCalc, {
4949                     t  : [wrap, st, b.width, 0, NULL, NULL, LEFT, BOTTOM, NULL, bh, NULL],
4950                     l  : [wrap, st, 0, b.height, NULL, NULL, RIGHT, TOP, bw, NULL, NULL],
4951                     r  : [wrap, st, b.width, b.height, SETX, b.right, LEFT, TOP, NULL, NULL, pt],
4952                     b  : [wrap, st, b.width, b.height, SETY, b.bottom, LEFT, TOP, NULL, bh, pt],
4953                     tl : [wrap, st, 0, 0, NULL, NULL, RIGHT, BOTTOM, bw, bh, pt],
4954                     bl : [wrap, st, 0, 0, SETY, b.y + b.height, RIGHT, TOP, bw, bh, pt],
4955                     br : [wrap, st, 0, 0, SETXY, [b.right, b.bottom], LEFT, TOP, bw, bh, pt],
4956                     tr : [wrap, st, 0, 0, SETX, b.x + b.width, LEFT, BOTTOM, bw, bh, pt]
4957                 });
4958             
4959             st.visibility = VISIBLE;
4960             fly(wrap).show();
4961
4962             arguments.callee.anim = fly(wrap).fxanim(args,
4963                 o,
4964                 MOTION,
4965                 .5,
4966                 EASEOUT, 
4967                 after);
4968         });
4969         return me;
4970     },
4971     
4972     /**
4973      * Slides the element out of view.  An anchor point can be optionally passed to set the end point
4974      * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
4975      * 'hidden') but block elements will still take up space in the document.  The element must be removed
4976      * from the DOM using the 'remove' config option if desired.  This function automatically handles 
4977      * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
4978      * Usage:
4979      *<pre><code>
4980 // default: slide the element out to the top
4981 el.slideOut();
4982
4983 // custom: slide the element out to the right with a 2-second duration
4984 el.slideOut('r', { duration: 2 });
4985
4986 // common config options shown with default values
4987 el.slideOut('t', {
4988     easing: 'easeOut',
4989     duration: .5,
4990     remove: false,
4991     useDisplay: false
4992 });
4993 </code></pre>
4994      * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
4995      * @param {Object} options (optional) Object literal with any of the Fx config options
4996      * @return {Ext.Element} The Element
4997      */
4998     slideOut : function(anchor, o){
4999         o = getObject(o);
5000         var me = this,
5001             dom = me.dom,
5002             st = dom.style,
5003             xy = me.getXY(),
5004             wrap,
5005             r,
5006             b,
5007             a,
5008             zero = {to: 0}; 
5009                     
5010         anchor = anchor || "t";
5011
5012         me.queueFx(o, function(){
5013             
5014             // restore values after effect
5015             r = fly(dom).getFxRestore(); 
5016             b = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: dom.offsetWidth, height: dom.offsetHeight};
5017             b.right = b.x + b.width;
5018             b.bottom = b.y + b.height;
5019                 
5020             // fixed size for slide   
5021             fly(dom).setWidth(b.width).setHeight(b.height);
5022
5023             // wrap if needed
5024             wrap = fly(dom).fxWrap(r.pos, o, VISIBLE);
5025                 
5026             st.visibility = VISIBLE;
5027             st.position = ABSOLUTE;
5028             fly(wrap).setWidth(b.width).setHeight(b.height);            
5029
5030             function after(){
5031                 o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();                
5032                 fly(dom).fxUnwrap(wrap, r.pos, o);
5033                 st.width = r.width;
5034                 st.height = r.height;
5035                 fly(dom).afterFx(o);
5036             }            
5037             
5038             function argCalc(style, s1, s2, p1, v1, p2, v2, p3, v3){                    
5039                 var ret = {};
5040                 
5041                 style[s1] = style[s2] = "0";
5042                 ret[p1] = v1;               
5043                 if(p2){
5044                     ret[p2] = v2;               
5045                 }
5046                 if(p3){
5047                     ret[p3] = v3;
5048                 }
5049                 
5050                 return ret;
5051             };
5052             
5053             a = fly(dom).switchStatements(anchor.toLowerCase(), argCalc, {
5054                 t  : [st, LEFT, BOTTOM, HEIGHT, zero],
5055                 l  : [st, RIGHT, TOP, WIDTH, zero],
5056                 r  : [st, LEFT, TOP, WIDTH, zero, POINTS, {to : [b.right, b.y]}],
5057                 b  : [st, LEFT, TOP, HEIGHT, zero, POINTS, {to : [b.x, b.bottom]}],
5058                 tl : [st, RIGHT, BOTTOM, WIDTH, zero, HEIGHT, zero],
5059                 bl : [st, RIGHT, TOP, WIDTH, zero, HEIGHT, zero, POINTS, {to : [b.x, b.bottom]}],
5060                 br : [st, LEFT, TOP, WIDTH, zero, HEIGHT, zero, POINTS, {to : [b.x + b.width, b.bottom]}],
5061                 tr : [st, LEFT, BOTTOM, WIDTH, zero, HEIGHT, zero, POINTS, {to : [b.right, b.y]}]
5062             });
5063             
5064             arguments.callee.anim = fly(wrap).fxanim(a,
5065                 o,
5066                 MOTION,
5067                 .5,
5068                 EASEOUT, 
5069                 after);
5070         });
5071         return me;
5072     },
5073
5074     /**
5075      * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
5076      * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
5077      * The element must be removed from the DOM using the 'remove' config option if desired.
5078      * Usage:
5079      *<pre><code>
5080 // default
5081 el.puff();
5082
5083 // common config options shown with default values
5084 el.puff({
5085     easing: 'easeOut',
5086     duration: .5,
5087     remove: false,
5088     useDisplay: false
5089 });
5090 </code></pre>
5091      * @param {Object} options (optional) Object literal with any of the Fx config options
5092      * @return {Ext.Element} The Element
5093      */
5094     puff : function(o){
5095         o = getObject(o);
5096         var me = this,
5097             dom = me.dom,
5098             st = dom.style,
5099             width,
5100             height,
5101             r;
5102
5103         me.queueFx(o, function(){
5104             width = fly(dom).getWidth();
5105             height = fly(dom).getHeight();
5106             fly(dom).clearOpacity();
5107             fly(dom).show();
5108
5109             // restore values after effect
5110             r = fly(dom).getFxRestore();                   
5111             
5112             function after(){
5113                 o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();                  
5114                 fly(dom).clearOpacity();  
5115                 fly(dom).setPositioning(r.pos);
5116                 st.width = r.width;
5117                 st.height = r.height;
5118                 st.fontSize = '';
5119                 fly(dom).afterFx(o);
5120             }   
5121
5122             arguments.callee.anim = fly(dom).fxanim({
5123                     width : {to : fly(dom).adjustWidth(width * 2)},
5124                     height : {to : fly(dom).adjustHeight(height * 2)},
5125                     points : {by : [-width * .5, -height * .5]},
5126                     opacity : {to : 0},
5127                     fontSize: {to : 200, unit: "%"}
5128                 },
5129                 o,
5130                 MOTION,
5131                 .5,
5132                 EASEOUT,
5133                  after);
5134         });
5135         return me;
5136     },
5137
5138     /**
5139      * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
5140      * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
5141      * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
5142      * Usage:
5143      *<pre><code>
5144 // default
5145 el.switchOff();
5146
5147 // all config options shown with default values
5148 el.switchOff({
5149     easing: 'easeIn',
5150     duration: .3,
5151     remove: false,
5152     useDisplay: false
5153 });
5154 </code></pre>
5155      * @param {Object} options (optional) Object literal with any of the Fx config options
5156      * @return {Ext.Element} The Element
5157      */
5158     switchOff : function(o){
5159         o = getObject(o);
5160         var me = this,
5161             dom = me.dom,
5162             st = dom.style,
5163             r;
5164
5165         me.queueFx(o, function(){
5166             fly(dom).clearOpacity();
5167             fly(dom).clip();
5168
5169             // restore values after effect
5170             r = fly(dom).getFxRestore();
5171                 
5172             function after(){
5173                 o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();  
5174                 fly(dom).clearOpacity();
5175                 fly(dom).setPositioning(r.pos);
5176                 st.width = r.width;
5177                 st.height = r.height;   
5178                 fly(dom).afterFx(o);
5179             };
5180
5181             fly(dom).fxanim({opacity : {to : 0.3}}, 
5182                 NULL, 
5183                 NULL, 
5184                 .1, 
5185                 NULL, 
5186                 function(){                                 
5187                     fly(dom).clearOpacity();
5188                         (function(){                            
5189                             fly(dom).fxanim({
5190                                 height : {to : 1},
5191                                 points : {by : [0, fly(dom).getHeight() * .5]}
5192                             }, 
5193                             o, 
5194                             MOTION, 
5195                             0.3, 
5196                             'easeIn', 
5197                             after);
5198                         }).defer(100);
5199                 });
5200         });
5201         return me;
5202     },
5203
5204     /**
5205      * Highlights the Element by setting a color (applies to the background-color by default, but can be
5206      * changed using the "attr" config option) and then fading back to the original color. If no original
5207      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
5208      * Usage:
5209 <pre><code>
5210 // default: highlight background to yellow
5211 el.highlight();
5212
5213 // custom: highlight foreground text to blue for 2 seconds
5214 el.highlight("0000ff", { attr: 'color', duration: 2 });
5215
5216 // common config options shown with default values
5217 el.highlight("ffff9c", {
5218     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
5219     endColor: (current color) or "ffffff",
5220     easing: 'easeIn',
5221     duration: 1
5222 });
5223 </code></pre>
5224      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
5225      * @param {Object} options (optional) Object literal with any of the Fx config options
5226      * @return {Ext.Element} The Element
5227      */ 
5228     highlight : function(color, o){
5229         o = getObject(o);
5230         var me = this,
5231             dom = me.dom,
5232             attr = o.attr || "backgroundColor",
5233             a = {},
5234             restore;
5235
5236         me.queueFx(o, function(){
5237             fly(dom).clearOpacity();
5238             fly(dom).show();
5239
5240             function after(){
5241                 dom.style[attr] = restore;
5242                 fly(dom).afterFx(o);
5243             }            
5244             restore = dom.style[attr];
5245             a[attr] = {from: color || "ffff9c", to: o.endColor || fly(dom).getColor(attr) || "ffffff"};
5246             arguments.callee.anim = fly(dom).fxanim(a,
5247                 o,
5248                 'color',
5249                 1,
5250                 'easeIn', 
5251                 after);
5252         });
5253         return me;
5254     },
5255
5256    /**
5257     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
5258     * Usage:
5259 <pre><code>
5260 // default: a single light blue ripple
5261 el.frame();
5262
5263 // custom: 3 red ripples lasting 3 seconds total
5264 el.frame("ff0000", 3, { duration: 3 });
5265
5266 // common config options shown with default values
5267 el.frame("C3DAF9", 1, {
5268     duration: 1 //duration of each individual ripple.
5269     // Note: Easing is not configurable and will be ignored if included
5270 });
5271 </code></pre>
5272     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
5273     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
5274     * @param {Object} options (optional) Object literal with any of the Fx config options
5275     * @return {Ext.Element} The Element
5276     */
5277     frame : function(color, count, o){
5278         o = getObject(o);
5279         var me = this,
5280             dom = me.dom,
5281             proxy,
5282             active;
5283
5284         me.queueFx(o, function(){
5285             color = color || '#C3DAF9';
5286             if(color.length == 6){
5287                 color = '#' + color;
5288             }            
5289             count = count || 1;
5290             fly(dom).show();
5291
5292             var xy = fly(dom).getXY(),
5293                 b = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: dom.offsetWidth, height: dom.offsetHeight},
5294                 queue = function(){
5295                     proxy = fly(document.body || document.documentElement).createChild({
5296                         style:{
5297                             position : ABSOLUTE,
5298                             'z-index': 35000, // yee haw
5299                             border : '0px solid ' + color
5300                         }
5301                     });
5302                     return proxy.queueFx({}, animFn);
5303                 };
5304             
5305             
5306             arguments.callee.anim = {
5307                 isAnimated: true,
5308                 stop: function() {
5309                     count = 0;
5310                     proxy.stopFx();
5311                 }
5312             };
5313             
5314             function animFn(){
5315                 var scale = Ext.isBorderBox ? 2 : 1;
5316                 active = proxy.anim({
5317                     top : {from : b.y, to : b.y - 20},
5318                     left : {from : b.x, to : b.x - 20},
5319                     borderWidth : {from : 0, to : 10},
5320                     opacity : {from : 1, to : 0},
5321                     height : {from : b.height, to : b.height + 20 * scale},
5322                     width : {from : b.width, to : b.width + 20 * scale}
5323                 },{
5324                     duration: o.duration || 1,
5325                     callback: function() {
5326                         proxy.remove();
5327                         --count > 0 ? queue() : fly(dom).afterFx(o);
5328                     }
5329                 });
5330                 arguments.callee.anim = {
5331                     isAnimated: true,
5332                     stop: function(){
5333                         active.stop();
5334                     }
5335                 };
5336             };
5337             queue();
5338         });
5339         return me;
5340     },
5341
5342    /**
5343     * Creates a pause before any subsequent queued effects begin.  If there are
5344     * no effects queued after the pause it will have no effect.
5345     * Usage:
5346 <pre><code>
5347 el.pause(1);
5348 </code></pre>
5349     * @param {Number} seconds The length of time to pause (in seconds)
5350     * @return {Ext.Element} The Element
5351     */
5352     pause : function(seconds){        
5353         var dom = this.dom,
5354             t;
5355
5356         this.queueFx({}, function(){
5357             t = setTimeout(function(){
5358                 fly(dom).afterFx({});
5359             }, seconds * 1000);
5360             arguments.callee.anim = {
5361                 isAnimated: true,
5362                 stop: function(){
5363                     clearTimeout(t);
5364                     fly(dom).afterFx({});
5365                 }
5366             };
5367         });
5368         return this;
5369     },
5370
5371    /**
5372     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
5373     * using the <tt>{@link #endOpacity}</tt> config option.
5374     * Usage:
5375 <pre><code>
5376 // default: fade in from opacity 0 to 100%
5377 el.fadeIn();
5378
5379 // custom: fade in from opacity 0 to 75% over 2 seconds
5380 el.fadeIn({ endOpacity: .75, duration: 2});
5381
5382 // common config options shown with default values
5383 el.fadeIn({
5384     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
5385     easing: 'easeOut',
5386     duration: .5
5387 });
5388 </code></pre>
5389     * @param {Object} options (optional) Object literal with any of the Fx config options
5390     * @return {Ext.Element} The Element
5391     */
5392     fadeIn : function(o){
5393         o = getObject(o);
5394         var me = this,
5395             dom = me.dom,
5396             to = o.endOpacity || 1;
5397         
5398         me.queueFx(o, function(){
5399             fly(dom).setOpacity(0);
5400             fly(dom).fixDisplay();
5401             dom.style.visibility = VISIBLE;
5402             arguments.callee.anim = fly(dom).fxanim({opacity:{to:to}},
5403                 o, NULL, .5, EASEOUT, function(){
5404                 if(to == 1){
5405                     fly(dom).clearOpacity();
5406                 }
5407                 fly(dom).afterFx(o);
5408             });
5409         });
5410         return me;
5411     },
5412
5413    /**
5414     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
5415     * using the <tt>{@link #endOpacity}</tt> config option.  Note that IE may require
5416     * <tt>{@link #useDisplay}:true</tt> in order to redisplay correctly.
5417     * Usage:
5418 <pre><code>
5419 // default: fade out from the element's current opacity to 0
5420 el.fadeOut();
5421
5422 // custom: fade out from the element's current opacity to 25% over 2 seconds
5423 el.fadeOut({ endOpacity: .25, duration: 2});
5424
5425 // common config options shown with default values
5426 el.fadeOut({
5427     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
5428     easing: 'easeOut',
5429     duration: .5,
5430     remove: false,
5431     useDisplay: false
5432 });
5433 </code></pre>
5434     * @param {Object} options (optional) Object literal with any of the Fx config options
5435     * @return {Ext.Element} The Element
5436     */
5437     fadeOut : function(o){
5438         o = getObject(o);
5439         var me = this,
5440             dom = me.dom,
5441             style = dom.style,
5442             to = o.endOpacity || 0;         
5443         
5444         me.queueFx(o, function(){  
5445             arguments.callee.anim = fly(dom).fxanim({ 
5446                 opacity : {to : to}},
5447                 o, 
5448                 NULL, 
5449                 .5, 
5450                 EASEOUT, 
5451                 function(){
5452                     if(to == 0){
5453                         Ext.Element.data(dom, 'visibilityMode') == Ext.Element.DISPLAY || o.useDisplay ? 
5454                             style.display = "none" :
5455                             style.visibility = HIDDEN;
5456                             
5457                         fly(dom).clearOpacity();
5458                     }
5459                     fly(dom).afterFx(o);
5460             });
5461         });
5462         return me;
5463     },
5464
5465    /**
5466     * Animates the transition of an element's dimensions from a starting height/width
5467     * to an ending height/width.  This method is a convenience implementation of {@link shift}.
5468     * Usage:
5469 <pre><code>
5470 // change height and width to 100x100 pixels
5471 el.scale(100, 100);
5472
5473 // common config options shown with default values.  The height and width will default to
5474 // the element&#39;s existing values if passed as null.
5475 el.scale(
5476     [element&#39;s width],
5477     [element&#39;s height], {
5478         easing: 'easeOut',
5479         duration: .35
5480     }
5481 );
5482 </code></pre>
5483     * @param {Number} width  The new width (pass undefined to keep the original width)
5484     * @param {Number} height  The new height (pass undefined to keep the original height)
5485     * @param {Object} options (optional) Object literal with any of the Fx config options
5486     * @return {Ext.Element} The Element
5487     */
5488     scale : function(w, h, o){
5489         this.shift(Ext.apply({}, o, {
5490             width: w,
5491             height: h
5492         }));
5493         return this;
5494     },
5495
5496    /**
5497     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
5498     * Any of these properties not specified in the config object will not be changed.  This effect 
5499     * requires that at least one new dimension, position or opacity setting must be passed in on
5500     * the config object in order for the function to have any effect.
5501     * Usage:
5502 <pre><code>
5503 // slide the element horizontally to x position 200 while changing the height and opacity
5504 el.shift({ x: 200, height: 50, opacity: .8 });
5505
5506 // common config options shown with default values.
5507 el.shift({
5508     width: [element&#39;s width],
5509     height: [element&#39;s height],
5510     x: [element&#39;s x position],
5511     y: [element&#39;s y position],
5512     opacity: [element&#39;s opacity],
5513     easing: 'easeOut',
5514     duration: .35
5515 });
5516 </code></pre>
5517     * @param {Object} options  Object literal with any of the Fx config options
5518     * @return {Ext.Element} The Element
5519     */
5520     shift : function(o){
5521         o = getObject(o);
5522         var dom = this.dom,
5523             a = {};
5524                 
5525         this.queueFx(o, function(){
5526             for (var prop in o) {
5527                 if (o[prop] != UNDEFINED) {                                                 
5528                     a[prop] = {to : o[prop]};                   
5529                 }
5530             } 
5531             
5532             a.width ? a.width.to = fly(dom).adjustWidth(o.width) : a;
5533             a.height ? a.height.to = fly(dom).adjustWidth(o.height) : a;   
5534             
5535             if (a.x || a.y || a.xy) {
5536                 a.points = a.xy || 
5537                            {to : [ a.x ? a.x.to : fly(dom).getX(),
5538                                    a.y ? a.y.to : fly(dom).getY()]};                  
5539             }
5540
5541             arguments.callee.anim = fly(dom).fxanim(a,
5542                 o, 
5543                 MOTION, 
5544                 .35, 
5545                 EASEOUT, 
5546                 function(){
5547                     fly(dom).afterFx(o);
5548                 });
5549         });
5550         return this;
5551     },
5552
5553     /**
5554      * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
5555      * ending point of the effect.
5556      * Usage:
5557      *<pre><code>
5558 // default: slide the element downward while fading out
5559 el.ghost();
5560
5561 // custom: slide the element out to the right with a 2-second duration
5562 el.ghost('r', { duration: 2 });
5563
5564 // common config options shown with default values
5565 el.ghost('b', {
5566     easing: 'easeOut',
5567     duration: .5,
5568     remove: false,
5569     useDisplay: false
5570 });
5571 </code></pre>
5572      * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
5573      * @param {Object} options (optional) Object literal with any of the Fx config options
5574      * @return {Ext.Element} The Element
5575      */
5576     ghost : function(anchor, o){
5577         o = getObject(o);
5578         var me = this,
5579             dom = me.dom,
5580             st = dom.style,
5581             a = {opacity: {to: 0}, points: {}},
5582             pt = a.points,
5583             r,
5584             w,
5585             h;
5586             
5587         anchor = anchor || "b";
5588
5589         me.queueFx(o, function(){
5590             // restore values after effect
5591             r = fly(dom).getFxRestore();
5592             w = fly(dom).getWidth();
5593             h = fly(dom).getHeight();
5594             
5595             function after(){
5596                 o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();   
5597                 fly(dom).clearOpacity();
5598                 fly(dom).setPositioning(r.pos);
5599                 st.width = r.width;
5600                 st.height = r.height;
5601                 fly(dom).afterFx(o);
5602             }
5603                 
5604             pt.by = fly(dom).switchStatements(anchor.toLowerCase(), function(v1,v2){ return [v1, v2];}, {
5605                t  : [0, -h],
5606                l  : [-w, 0],
5607                r  : [w, 0],
5608                b  : [0, h],
5609                tl : [-w, -h],
5610                bl : [-w, h],
5611                br : [w, h],
5612                tr : [w, -h] 
5613             });
5614                 
5615             arguments.callee.anim = fly(dom).fxanim(a,
5616                 o,
5617                 MOTION,
5618                 .5,
5619                 EASEOUT, after);
5620         });
5621         return me;
5622     },
5623
5624     /**
5625      * Ensures that all effects queued after syncFx is called on the element are
5626      * run concurrently.  This is the opposite of {@link #sequenceFx}.
5627      * @return {Ext.Element} The Element
5628      */
5629     syncFx : function(){
5630         var me = this;
5631         me.fxDefaults = Ext.apply(me.fxDefaults || {}, {
5632             block : FALSE,
5633             concurrent : TRUE,
5634             stopFx : FALSE
5635         });
5636         return me;
5637     },
5638
5639     /**
5640      * Ensures that all effects queued after sequenceFx is called on the element are
5641      * run in sequence.  This is the opposite of {@link #syncFx}.
5642      * @return {Ext.Element} The Element
5643      */
5644     sequenceFx : function(){
5645         var me = this;
5646         me.fxDefaults = Ext.apply(me.fxDefaults || {}, {
5647             block : FALSE,
5648             concurrent : FALSE,
5649             stopFx : FALSE
5650         });
5651         return me;
5652     },
5653
5654     /* @private */
5655     nextFx : function(){        
5656         var ef = getQueue(this.dom.id)[0];
5657         if(ef){
5658             ef.call(this);
5659         }
5660     },
5661
5662     /**
5663      * Returns true if the element has any effects actively running or queued, else returns false.
5664      * @return {Boolean} True if element has active effects, else false
5665      */
5666     hasActiveFx : function(){
5667         return getQueue(this.dom.id)[0];
5668     },
5669
5670     /**
5671      * Stops any running effects and clears the element's internal effects queue if it contains
5672      * any additional effects that haven't started yet.
5673      * @return {Ext.Element} The Element
5674      */
5675     stopFx : function(finish){
5676         var me = this,
5677             id = me.dom.id;
5678         if(me.hasActiveFx()){
5679             var cur = getQueue(id)[0];
5680             if(cur && cur.anim){
5681                 if(cur.anim.isAnimated){
5682                     setQueue(id, [cur]); //clear
5683                     cur.anim.stop(finish !== undefined ? finish : TRUE);
5684                 }else{
5685                     setQueue(id, []);
5686                 }
5687             }
5688         }
5689         return me;
5690     },
5691
5692     /* @private */
5693     beforeFx : function(o){
5694         if(this.hasActiveFx() && !o.concurrent){
5695            if(o.stopFx){
5696                this.stopFx();
5697                return TRUE;
5698            }
5699            return FALSE;
5700         }
5701         return TRUE;
5702     },
5703
5704     /**
5705      * Returns true if the element is currently blocking so that no other effect can be queued
5706      * until this effect is finished, else returns false if blocking is not set.  This is commonly
5707      * used to ensure that an effect initiated by a user action runs to completion prior to the
5708      * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
5709      * @return {Boolean} True if blocking, else false
5710      */
5711     hasFxBlock : function(){
5712         var q = getQueue(this.dom.id);
5713         return q && q[0] && q[0].block;
5714     },
5715
5716     /* @private */
5717     queueFx : function(o, fn){
5718         var me = fly(this.dom);
5719         if(!me.hasFxBlock()){
5720             Ext.applyIf(o, me.fxDefaults);
5721             if(!o.concurrent){
5722                 var run = me.beforeFx(o);
5723                 fn.block = o.block;
5724                 getQueue(me.dom.id).push(fn);
5725                 if(run){
5726                     me.nextFx();
5727                 }
5728             }else{
5729                 fn.call(me);
5730             }
5731         }
5732         return me;
5733     },
5734
5735     /* @private */
5736     fxWrap : function(pos, o, vis){ 
5737         var dom = this.dom,
5738             wrap,
5739             wrapXY;
5740         if(!o.wrap || !(wrap = Ext.getDom(o.wrap))){            
5741             if(o.fixPosition){
5742                 wrapXY = fly(dom).getXY();
5743             }
5744             var div = document.createElement("div");
5745             div.style.visibility = vis;
5746             wrap = dom.parentNode.insertBefore(div, dom);
5747             fly(wrap).setPositioning(pos);
5748             if(fly(wrap).isStyle(POSITION, "static")){
5749                 fly(wrap).position("relative");
5750             }
5751             fly(dom).clearPositioning('auto');
5752             fly(wrap).clip();
5753             wrap.appendChild(dom);
5754             if(wrapXY){
5755                 fly(wrap).setXY(wrapXY);
5756             }
5757         }
5758         return wrap;
5759     },
5760
5761     /* @private */
5762     fxUnwrap : function(wrap, pos, o){      
5763         var dom = this.dom;
5764         fly(dom).clearPositioning();
5765         fly(dom).setPositioning(pos);
5766         if(!o.wrap){
5767             var pn = fly(wrap).dom.parentNode;
5768             pn.insertBefore(dom, wrap); 
5769             fly(wrap).remove();
5770         }
5771     },
5772
5773     /* @private */
5774     getFxRestore : function(){
5775         var st = this.dom.style;
5776         return {pos: this.getPositioning(), width: st.width, height : st.height};
5777     },
5778
5779     /* @private */
5780     afterFx : function(o){
5781         var dom = this.dom,
5782             id = dom.id;
5783         if(o.afterStyle){
5784             fly(dom).setStyle(o.afterStyle);            
5785         }
5786         if(o.afterCls){
5787             fly(dom).addClass(o.afterCls);
5788         }
5789         if(o.remove == TRUE){
5790             fly(dom).remove();
5791         }
5792         if(o.callback){
5793             o.callback.call(o.scope, fly(dom));
5794         }
5795         if(!o.concurrent){
5796             getQueue(id).shift();
5797             fly(dom).nextFx();
5798         }
5799     },
5800
5801     /* @private */
5802     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
5803         animType = animType || 'run';
5804         opt = opt || {};
5805         var anim = Ext.lib.Anim[animType](
5806                 this.dom, 
5807                 args,
5808                 (opt.duration || defaultDur) || .35,
5809                 (opt.easing || defaultEase) || EASEOUT,
5810                 cb,            
5811                 this
5812             );
5813         opt.anim = anim;
5814         return anim;
5815     }
5816 };
5817
5818 // backwards compat
5819 Ext.Fx.resize = Ext.Fx.scale;
5820
5821 //When included, Ext.Fx is automatically applied to Element so that all basic
5822 //effects are available directly via the Element API
5823 Ext.Element.addMethods(Ext.Fx);
5824 })();
5825 /**
5826  * @class Ext.CompositeElementLite
5827  * <p>This class encapsulates a <i>collection</i> of DOM elements, providing methods to filter
5828  * members, or to perform collective actions upon the whole set.</p>
5829  * <p>Although they are not listed, this class supports all of the methods of {@link Ext.Element} and
5830  * {@link Ext.Fx}. The methods from these classes will be performed on all the elements in this collection.</p>
5831  * Example:<pre><code>
5832 var els = Ext.select("#some-el div.some-class");
5833 // or select directly from an existing element
5834 var el = Ext.get('some-el');
5835 el.select('div.some-class');
5836
5837 els.setWidth(100); // all elements become 100 width
5838 els.hide(true); // all elements fade out and hide
5839 // or
5840 els.setWidth(100).hide(true);
5841 </code>
5842  */
5843 Ext.CompositeElementLite = function(els, root){
5844     /**
5845      * <p>The Array of DOM elements which this CompositeElement encapsulates. Read-only.</p>
5846      * <p>This will not <i>usually</i> be accessed in developers' code, but developers wishing
5847      * to augment the capabilities of the CompositeElementLite class may use it when adding
5848      * methods to the class.</p>
5849      * <p>For example to add the <code>nextAll</code> method to the class to <b>add</b> all
5850      * following siblings of selected elements, the code would be</p><code><pre>
5851 Ext.override(Ext.CompositeElementLite, {
5852     nextAll: function() {
5853         var els = this.elements, i, l = els.length, n, r = [], ri = -1;
5854
5855 //      Loop through all elements in this Composite, accumulating
5856 //      an Array of all siblings.
5857         for (i = 0; i < l; i++) {
5858             for (n = els[i].nextSibling; n; n = n.nextSibling) {
5859                 r[++ri] = n;
5860             }
5861         }
5862
5863 //      Add all found siblings to this Composite
5864         return this.add(r);
5865     }
5866 });</pre></code>
5867      * @type Array
5868      * @property elements
5869      */
5870     this.elements = [];
5871     this.add(els, root);
5872     this.el = new Ext.Element.Flyweight();
5873 };
5874
5875 Ext.CompositeElementLite.prototype = {
5876     isComposite: true,
5877
5878     // private
5879     getElement : function(el){
5880         // Set the shared flyweight dom property to the current element
5881         var e = this.el;
5882         e.dom = el;
5883         e.id = el.id;
5884         return e;
5885     },
5886
5887     // private
5888     transformElement : function(el){
5889         return Ext.getDom(el);
5890     },
5891
5892     /**
5893      * Returns the number of elements in this Composite.
5894      * @return Number
5895      */
5896     getCount : function(){
5897         return this.elements.length;
5898     },
5899     /**
5900      * Adds elements to this Composite object.
5901      * @param {Mixed} els Either an Array of DOM elements to add, or another Composite object who's elements should be added.
5902      * @return {CompositeElement} This Composite object.
5903      */
5904     add : function(els, root){
5905         var me = this,
5906             elements = me.elements;
5907         if(!els){
5908             return this;
5909         }
5910         if(typeof els == "string"){
5911             els = Ext.Element.selectorFunction(els, root);
5912         }else if(els.isComposite){
5913             els = els.elements;
5914         }else if(!Ext.isIterable(els)){
5915             els = [els];
5916         }
5917
5918         for(var i = 0, len = els.length; i < len; ++i){
5919             elements.push(me.transformElement(els[i]));
5920         }
5921         return me;
5922     },
5923
5924     invoke : function(fn, args){
5925         var me = this,
5926             els = me.elements,
5927             len = els.length,
5928             e,
5929             i;
5930
5931         for(i = 0; i < len; i++) {
5932             e = els[i];
5933             if(e){
5934                 Ext.Element.prototype[fn].apply(me.getElement(e), args);
5935             }
5936         }
5937         return me;
5938     },
5939     /**
5940      * Returns a flyweight Element of the dom element object at the specified index
5941      * @param {Number} index
5942      * @return {Ext.Element}
5943      */
5944     item : function(index){
5945         var me = this,
5946             el = me.elements[index],
5947             out = null;
5948
5949         if(el){
5950             out = me.getElement(el);
5951         }
5952         return out;
5953     },
5954
5955     // fixes scope with flyweight
5956     addListener : function(eventName, handler, scope, opt){
5957         var els = this.elements,
5958             len = els.length,
5959             i, e;
5960
5961         for(i = 0; i<len; i++) {
5962             e = els[i];
5963             if(e) {
5964                 Ext.EventManager.on(e, eventName, handler, scope || e, opt);
5965             }
5966         }
5967         return this;
5968     },
5969     /**
5970      * <p>Calls the passed function for each element in this composite.</p>
5971      * @param {Function} fn The function to call. The function is passed the following parameters:<ul>
5972      * <li><b>el</b> : Element<div class="sub-desc">The current Element in the iteration.
5973      * <b>This is the flyweight (shared) Ext.Element instance, so if you require a
5974      * a reference to the dom node, use el.dom.</b></div></li>
5975      * <li><b>c</b> : Composite<div class="sub-desc">This Composite object.</div></li>
5976      * <li><b>idx</b> : Number<div class="sub-desc">The zero-based index in the iteration.</div></li>
5977      * </ul>
5978      * @param {Object} scope (optional) The scope (<i>this</i> reference) in which the function is executed. (defaults to the Element)
5979      * @return {CompositeElement} this
5980      */
5981     each : function(fn, scope){
5982         var me = this,
5983             els = me.elements,
5984             len = els.length,
5985             i, e;
5986
5987         for(i = 0; i<len; i++) {
5988             e = els[i];
5989             if(e){
5990                 e = this.getElement(e);
5991                 if(fn.call(scope || e, e, me, i) === false){
5992                     break;
5993                 }
5994             }
5995         }
5996         return me;
5997     },
5998
5999     /**
6000     * Clears this Composite and adds the elements passed.
6001     * @param {Mixed} els Either an array of DOM elements, or another Composite from which to fill this Composite.
6002     * @return {CompositeElement} this
6003     */
6004     fill : function(els){
6005         var me = this;
6006         me.elements = [];
6007         me.add(els);
6008         return me;
6009     },
6010
6011     /**
6012      * Filters this composite to only elements that match the passed selector.
6013      * @param {String/Function} selector A string CSS selector or a comparison function.
6014      * The comparison function will be called with the following arguments:<ul>
6015      * <li><code>el</code> : Ext.Element<div class="sub-desc">The current DOM element.</div></li>
6016      * <li><code>index</code> : Number<div class="sub-desc">The current index within the collection.</div></li>
6017      * </ul>
6018      * @return {CompositeElement} this
6019      */
6020     filter : function(selector){
6021         var els = [],
6022             me = this,
6023             fn = Ext.isFunction(selector) ? selector
6024                 : function(el){
6025                     return el.is(selector);
6026                 };
6027
6028         me.each(function(el, self, i) {
6029             if (fn(el, i) !== false) {
6030                 els[els.length] = me.transformElement(el);
6031             }
6032         });
6033         
6034         me.elements = els;
6035         return me;
6036     },
6037
6038     /**
6039      * Find the index of the passed element within the composite collection.
6040      * @param el {Mixed} The id of an element, or an Ext.Element, or an HtmlElement to find within the composite collection.
6041      * @return Number The index of the passed Ext.Element in the composite collection, or -1 if not found.
6042      */
6043     indexOf : function(el){
6044         return this.elements.indexOf(this.transformElement(el));
6045     },
6046
6047     /**
6048     * Replaces the specified element with the passed element.
6049     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
6050     * to replace.
6051     * @param {Mixed} replacement The id of an element or the Element itself.
6052     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
6053     * @return {CompositeElement} this
6054     */
6055     replaceElement : function(el, replacement, domReplace){
6056         var index = !isNaN(el) ? el : this.indexOf(el),
6057             d;
6058         if(index > -1){
6059             replacement = Ext.getDom(replacement);
6060             if(domReplace){
6061                 d = this.elements[index];
6062                 d.parentNode.insertBefore(replacement, d);
6063                 Ext.removeNode(d);
6064             }
6065             this.elements.splice(index, 1, replacement);
6066         }
6067         return this;
6068     },
6069
6070     /**
6071      * Removes all elements.
6072      */
6073     clear : function(){
6074         this.elements = [];
6075     }
6076 };
6077
6078 Ext.CompositeElementLite.prototype.on = Ext.CompositeElementLite.prototype.addListener;
6079
6080 /**
6081  * @private
6082  * Copies all of the functions from Ext.Element's prototype onto CompositeElementLite's prototype.
6083  * This is called twice - once immediately below, and once again after additional Ext.Element
6084  * are added in Ext JS
6085  */
6086 Ext.CompositeElementLite.importElementMethods = function() {
6087     var fnName,
6088         ElProto = Ext.Element.prototype,
6089         CelProto = Ext.CompositeElementLite.prototype;
6090
6091     for (fnName in ElProto) {
6092         if (typeof ElProto[fnName] == 'function'){
6093             (function(fnName) {
6094                 CelProto[fnName] = CelProto[fnName] || function() {
6095                     return this.invoke(fnName, arguments);
6096                 };
6097             }).call(CelProto, fnName);
6098
6099         }
6100     }
6101 };
6102
6103 Ext.CompositeElementLite.importElementMethods();
6104
6105 if(Ext.DomQuery){
6106     Ext.Element.selectorFunction = Ext.DomQuery.select;
6107 }
6108
6109 /**
6110  * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods
6111  * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
6112  * {@link Ext.CompositeElementLite CompositeElementLite} object.
6113  * @param {String/Array} selector The CSS selector or an array of elements
6114  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
6115  * @return {CompositeElementLite/CompositeElement}
6116  * @member Ext.Element
6117  * @method select
6118  */
6119 Ext.Element.select = function(selector, root){
6120     var els;
6121     if(typeof selector == "string"){
6122         els = Ext.Element.selectorFunction(selector, root);
6123     }else if(selector.length !== undefined){
6124         els = selector;
6125     }else{
6126         throw "Invalid selector";
6127     }
6128     return new Ext.CompositeElementLite(els);
6129 };
6130 /**
6131  * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods
6132  * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
6133  * {@link Ext.CompositeElementLite CompositeElementLite} object.
6134  * @param {String/Array} selector The CSS selector or an array of elements
6135  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
6136  * @return {CompositeElementLite/CompositeElement}
6137  * @member Ext
6138  * @method select
6139  */
6140 Ext.select = Ext.Element.select;
6141 (function(){
6142     var BEFOREREQUEST = "beforerequest",
6143         REQUESTCOMPLETE = "requestcomplete",
6144         REQUESTEXCEPTION = "requestexception",
6145         UNDEFINED = undefined,
6146         LOAD = 'load',
6147         POST = 'POST',
6148         GET = 'GET',
6149         WINDOW = window;
6150
6151     /**
6152      * @class Ext.data.Connection
6153      * @extends Ext.util.Observable
6154      * <p>The class encapsulates a connection to the page's originating domain, allowing requests to be made
6155      * either to a configured URL, or to a URL specified at request time.</p>
6156      * <p>Requests made by this class are asynchronous, and will return immediately. No data from
6157      * the server will be available to the statement immediately following the {@link #request} call.
6158      * To process returned data, use a
6159      * <a href="#request-option-success" ext:member="request-option-success" ext:cls="Ext.data.Connection">success callback</a>
6160      * in the request options object,
6161      * or an {@link #requestcomplete event listener}.</p>
6162      * <p><h3>File Uploads</h3><a href="#request-option-isUpload" ext:member="request-option-isUpload" ext:cls="Ext.data.Connection">File uploads</a> are not performed using normal "Ajax" techniques, that
6163      * is they are <b>not</b> performed using XMLHttpRequests. Instead the form is submitted in the standard
6164      * manner with the DOM <tt>&lt;form></tt> element temporarily modified to have its
6165      * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
6166      * to a dynamically generated, hidden <tt>&lt;iframe></tt> which is inserted into the document
6167      * but removed after the return data has been gathered.</p>
6168      * <p>The server response is parsed by the browser to create the document for the IFRAME. If the
6169      * server is using JSON to send the return object, then the
6170      * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17">Content-Type</a> header
6171      * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.</p>
6172      * <p>Characters which are significant to an HTML parser must be sent as HTML entities, so encode
6173      * "&lt;" as "&amp;lt;", "&amp;" as "&amp;amp;" etc.</p>
6174      * <p>The response text is retrieved from the document, and a fake XMLHttpRequest object
6175      * is created containing a <tt>responseText</tt> property in order to conform to the
6176      * requirements of event handlers and callbacks.</p>
6177      * <p>Be aware that file upload packets are sent with the content type <a href="http://www.faqs.org/rfcs/rfc2388.html">multipart/form</a>
6178      * and some server technologies (notably JEE) may require some custom processing in order to
6179      * retrieve parameter names and parameter values from the packet content.</p>
6180      * <p>Also note that it's not possible to check the response code of the hidden iframe, so the success handler will ALWAYS fire.</p>
6181      * @constructor
6182      * @param {Object} config a configuration object.
6183      */
6184     Ext.data.Connection = function(config){
6185         Ext.apply(this, config);
6186         this.addEvents(
6187             /**
6188              * @event beforerequest
6189              * Fires before a network request is made to retrieve a data object.
6190              * @param {Connection} conn This Connection object.
6191              * @param {Object} options The options config object passed to the {@link #request} method.
6192              */
6193             BEFOREREQUEST,
6194             /**
6195              * @event requestcomplete
6196              * Fires if the request was successfully completed.
6197              * @param {Connection} conn This Connection object.
6198              * @param {Object} response The XHR object containing the response data.
6199              * See <a href="http://www.w3.org/TR/XMLHttpRequest/">The XMLHttpRequest Object</a>
6200              * for details.
6201              * @param {Object} options The options config object passed to the {@link #request} method.
6202              */
6203             REQUESTCOMPLETE,
6204             /**
6205              * @event requestexception
6206              * Fires if an error HTTP status was returned from the server.
6207              * See <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html">HTTP Status Code Definitions</a>
6208              * for details of HTTP status codes.
6209              * @param {Connection} conn This Connection object.
6210              * @param {Object} response The XHR object containing the response data.
6211              * See <a href="http://www.w3.org/TR/XMLHttpRequest/">The XMLHttpRequest Object</a>
6212              * for details.
6213              * @param {Object} options The options config object passed to the {@link #request} method.
6214              */
6215             REQUESTEXCEPTION
6216         );
6217         Ext.data.Connection.superclass.constructor.call(this);
6218     };
6219
6220     Ext.extend(Ext.data.Connection, Ext.util.Observable, {
6221         /**
6222          * @cfg {String} url (Optional) <p>The default URL to be used for requests to the server. Defaults to undefined.</p>
6223          * <p>The <code>url</code> config may be a function which <i>returns</i> the URL to use for the Ajax request. The scope
6224          * (<code><b>this</b></code> reference) of the function is the <code>scope</code> option passed to the {@link #request} method.</p>
6225          */
6226         /**
6227          * @cfg {Object} extraParams (Optional) An object containing properties which are used as
6228          * extra parameters to each request made by this object. (defaults to undefined)
6229          */
6230         /**
6231          * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
6232          *  to each request made by this object. (defaults to undefined)
6233          */
6234         /**
6235          * @cfg {String} method (Optional) The default HTTP method to be used for requests.
6236          * (defaults to undefined; if not set, but {@link #request} params are present, POST will be used;
6237          * otherwise, GET will be used.)
6238          */
6239         /**
6240          * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
6241          */
6242         timeout : 30000,
6243         /**
6244          * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
6245          * @type Boolean
6246          */
6247         autoAbort:false,
6248
6249         /**
6250          * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
6251          * @type Boolean
6252          */
6253         disableCaching: true,
6254
6255         /**
6256          * @cfg {String} disableCachingParam (Optional) Change the parameter which is sent went disabling caching
6257          * through a cache buster. Defaults to '_dc'
6258          * @type String
6259          */
6260         disableCachingParam: '_dc',
6261
6262         /**
6263          * <p>Sends an HTTP request to a remote server.</p>
6264          * <p><b>Important:</b> Ajax server requests are asynchronous, and this call will
6265          * return before the response has been received. Process any returned data
6266          * in a callback function.</p>
6267          * <pre><code>
6268 Ext.Ajax.request({
6269    url: 'ajax_demo/sample.json',
6270    success: function(response, opts) {
6271       var obj = Ext.decode(response.responseText);
6272       console.dir(obj);
6273    },
6274    failure: function(response, opts) {
6275       console.log('server-side failure with status code ' + response.status);
6276    }
6277 });
6278          * </code></pre>
6279          * <p>To execute a callback function in the correct scope, use the <tt>scope</tt> option.</p>
6280          * @param {Object} options An object which may contain the following properties:<ul>
6281          * <li><b>url</b> : String/Function (Optional)<div class="sub-desc">The URL to
6282          * which to send the request, or a function to call which returns a URL string. The scope of the
6283          * function is specified by the <tt>scope</tt> option. Defaults to the configured
6284          * <tt>{@link #url}</tt>.</div></li>
6285          * <li><b>params</b> : Object/String/Function (Optional)<div class="sub-desc">
6286          * An object containing properties which are used as parameters to the
6287          * request, a url encoded string or a function to call to get either. The scope of the function
6288          * is specified by the <tt>scope</tt> option.</div></li>
6289          * <li><b>method</b> : String (Optional)<div class="sub-desc">The HTTP method to use
6290          * for the request. Defaults to the configured method, or if no method was configured,
6291          * "GET" if no parameters are being sent, and "POST" if parameters are being sent.  Note that
6292          * the method name is case-sensitive and should be all caps.</div></li>
6293          * <li><b>callback</b> : Function (Optional)<div class="sub-desc">The
6294          * function to be called upon receipt of the HTTP response. The callback is
6295          * called regardless of success or failure and is passed the following
6296          * parameters:<ul>
6297          * <li><b>options</b> : Object<div class="sub-desc">The parameter to the request call.</div></li>
6298          * <li><b>success</b> : Boolean<div class="sub-desc">True if the request succeeded.</div></li>
6299          * <li><b>response</b> : Object<div class="sub-desc">The XMLHttpRequest object containing the response data.
6300          * See <a href="http://www.w3.org/TR/XMLHttpRequest/">http://www.w3.org/TR/XMLHttpRequest/</a> for details about
6301          * accessing elements of the response.</div></li>
6302          * </ul></div></li>
6303          * <li><a id="request-option-success"></a><b>success</b> : Function (Optional)<div class="sub-desc">The function
6304          * to be called upon success of the request. The callback is passed the following
6305          * parameters:<ul>
6306          * <li><b>response</b> : Object<div class="sub-desc">The XMLHttpRequest object containing the response data.</div></li>
6307          * <li><b>options</b> : Object<div class="sub-desc">The parameter to the request call.</div></li>
6308          * </ul></div></li>
6309          * <li><b>failure</b> : Function (Optional)<div class="sub-desc">The function
6310          * to be called upon failure of the request. The callback is passed the
6311          * following parameters:<ul>
6312          * <li><b>response</b> : Object<div class="sub-desc">The XMLHttpRequest object containing the response data.</div></li>
6313          * <li><b>options</b> : Object<div class="sub-desc">The parameter to the request call.</div></li>
6314          * </ul></div></li>
6315          * <li><b>scope</b> : Object (Optional)<div class="sub-desc">The scope in
6316          * which to execute the callbacks: The "this" object for the callback function. If the <tt>url</tt>, or <tt>params</tt> options were
6317          * specified as functions from which to draw values, then this also serves as the scope for those function calls.
6318          * Defaults to the browser window.</div></li>
6319          * <li><b>timeout</b> : Number (Optional)<div class="sub-desc">The timeout in milliseconds to be used for this request. Defaults to 30 seconds.</div></li>
6320          * <li><b>form</b> : Element/HTMLElement/String (Optional)<div class="sub-desc">The <tt>&lt;form&gt;</tt>
6321          * Element or the id of the <tt>&lt;form&gt;</tt> to pull parameters from.</div></li>
6322          * <li><a id="request-option-isUpload"></a><b>isUpload</b> : Boolean (Optional)<div class="sub-desc"><b>Only meaningful when used
6323          * with the <tt>form</tt> option</b>.
6324          * <p>True if the form object is a file upload (will be set automatically if the form was
6325          * configured with <b><tt>enctype</tt></b> "multipart/form-data").</p>
6326          * <p>File uploads are not performed using normal "Ajax" techniques, that is they are <b>not</b>
6327          * performed using XMLHttpRequests. Instead the form is submitted in the standard manner with the
6328          * DOM <tt>&lt;form></tt> element temporarily modified to have its
6329          * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
6330          * to a dynamically generated, hidden <tt>&lt;iframe></tt> which is inserted into the document
6331          * but removed after the return data has been gathered.</p>
6332          * <p>The server response is parsed by the browser to create the document for the IFRAME. If the
6333          * server is using JSON to send the return object, then the
6334          * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17">Content-Type</a> header
6335          * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.</p>
6336          * <p>The response text is retrieved from the document, and a fake XMLHttpRequest object
6337          * is created containing a <tt>responseText</tt> property in order to conform to the
6338          * requirements of event handlers and callbacks.</p>
6339          * <p>Be aware that file upload packets are sent with the content type <a href="http://www.faqs.org/rfcs/rfc2388.html">multipart/form</a>
6340          * and some server technologies (notably JEE) may require some custom processing in order to
6341          * retrieve parameter names and parameter values from the packet content.</p>
6342          * </div></li>
6343          * <li><b>headers</b> : Object (Optional)<div class="sub-desc">Request
6344          * headers to set for the request.</div></li>
6345          * <li><b>xmlData</b> : Object (Optional)<div class="sub-desc">XML document
6346          * to use for the post. Note: This will be used instead of params for the post
6347          * data. Any params will be appended to the URL.</div></li>
6348          * <li><b>jsonData</b> : Object/String (Optional)<div class="sub-desc">JSON
6349          * data to use as the post. Note: This will be used instead of params for the post
6350          * data. Any params will be appended to the URL.</div></li>
6351          * <li><b>disableCaching</b> : Boolean (Optional)<div class="sub-desc">True
6352          * to add a unique cache-buster param to GET requests.</div></li>
6353          * </ul></p>
6354          * <p>The options object may also contain any other property which might be needed to perform
6355          * postprocessing in a callback because it is passed to callback functions.</p>
6356          * @return {Number} transactionId The id of the server transaction. This may be used
6357          * to cancel the request.
6358          */
6359         request : function(o){
6360             var me = this;
6361             if(me.fireEvent(BEFOREREQUEST, me, o)){
6362                 if (o.el) {
6363                     if(!Ext.isEmpty(o.indicatorText)){
6364                         me.indicatorText = '<div class="loading-indicator">'+o.indicatorText+"</div>";
6365                     }
6366                     if(me.indicatorText) {
6367                         Ext.getDom(o.el).innerHTML = me.indicatorText;
6368                     }
6369                     o.success = (Ext.isFunction(o.success) ? o.success : function(){}).createInterceptor(function(response) {
6370                         Ext.getDom(o.el).innerHTML = response.responseText;
6371                     });
6372                 }
6373
6374                 var p = o.params,
6375                     url = o.url || me.url,
6376                     method,
6377                     cb = {success: me.handleResponse,
6378                           failure: me.handleFailure,
6379                           scope: me,
6380                           argument: {options: o},
6381                           timeout : Ext.num(o.timeout, me.timeout)
6382                     },
6383                     form,
6384                     serForm;
6385
6386
6387                 if (Ext.isFunction(p)) {
6388                     p = p.call(o.scope||WINDOW, o);
6389                 }
6390
6391                 p = Ext.urlEncode(me.extraParams, Ext.isObject(p) ? Ext.urlEncode(p) : p);
6392
6393                 if (Ext.isFunction(url)) {
6394                     url = url.call(o.scope || WINDOW, o);
6395                 }
6396
6397                 if((form = Ext.getDom(o.form))){
6398                     url = url || form.action;
6399                      if(o.isUpload || (/multipart\/form-data/i.test(form.getAttribute("enctype")))) {
6400                          return me.doFormUpload.call(me, o, p, url);
6401                      }
6402                     serForm = Ext.lib.Ajax.serializeForm(form);
6403                     p = p ? (p + '&' + serForm) : serForm;
6404                 }
6405
6406                 method = o.method || me.method || ((p || o.xmlData || o.jsonData) ? POST : GET);
6407
6408                 if(method === GET && (me.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
6409                     var dcp = o.disableCachingParam || me.disableCachingParam;
6410                     url = Ext.urlAppend(url, dcp + '=' + (new Date().getTime()));
6411                 }
6412
6413                 o.headers = Ext.apply(o.headers || {}, me.defaultHeaders || {});
6414
6415                 if(o.autoAbort === true || me.autoAbort) {
6416                     me.abort();
6417                 }
6418
6419                 if((method == GET || o.xmlData || o.jsonData) && p){
6420                     url = Ext.urlAppend(url, p);
6421                     p = '';
6422                 }
6423                 return (me.transId = Ext.lib.Ajax.request(method, url, cb, p, o));
6424             }else{
6425                 return o.callback ? o.callback.apply(o.scope, [o,UNDEFINED,UNDEFINED]) : null;
6426             }
6427         },
6428
6429         /**
6430          * Determine whether this object has a request outstanding.
6431          * @param {Number} transactionId (Optional) defaults to the last transaction
6432          * @return {Boolean} True if there is an outstanding request.
6433          */
6434         isLoading : function(transId){
6435             return transId ? Ext.lib.Ajax.isCallInProgress(transId) : !! this.transId;
6436         },
6437
6438         /**
6439          * Aborts any outstanding request.
6440          * @param {Number} transactionId (Optional) defaults to the last transaction
6441          */
6442         abort : function(transId){
6443             if(transId || this.isLoading()){
6444                 Ext.lib.Ajax.abort(transId || this.transId);
6445             }
6446         },
6447
6448         // private
6449         handleResponse : function(response){
6450             this.transId = false;
6451             var options = response.argument.options;
6452             response.argument = options ? options.argument : null;
6453             this.fireEvent(REQUESTCOMPLETE, this, response, options);
6454             if(options.success){
6455                 options.success.call(options.scope, response, options);
6456             }
6457             if(options.callback){
6458                 options.callback.call(options.scope, options, true, response);
6459             }
6460         },
6461
6462         // private
6463         handleFailure : function(response, e){
6464             this.transId = false;
6465             var options = response.argument.options;
6466             response.argument = options ? options.argument : null;
6467             this.fireEvent(REQUESTEXCEPTION, this, response, options, e);
6468             if(options.failure){
6469                 options.failure.call(options.scope, response, options);
6470             }
6471             if(options.callback){
6472                 options.callback.call(options.scope, options, false, response);
6473             }
6474         },
6475
6476         // private
6477         doFormUpload : function(o, ps, url){
6478             var id = Ext.id(),
6479                 doc = document,
6480                 frame = doc.createElement('iframe'),
6481                 form = Ext.getDom(o.form),
6482                 hiddens = [],
6483                 hd,
6484                 encoding = 'multipart/form-data',
6485                 buf = {
6486                     target: form.target,
6487                     method: form.method,
6488                     encoding: form.encoding,
6489                     enctype: form.enctype,
6490                     action: form.action
6491                 };
6492
6493             /*
6494              * Originally this behaviour was modified for Opera 10 to apply the secure URL after
6495              * the frame had been added to the document. It seems this has since been corrected in
6496              * Opera so the behaviour has been reverted, the URL will be set before being added.
6497              */
6498             Ext.fly(frame).set({
6499                 id: id,
6500                 name: id,
6501                 cls: 'x-hidden',
6502                 src: Ext.SSL_SECURE_URL
6503             }); 
6504
6505             doc.body.appendChild(frame);
6506
6507             // This is required so that IE doesn't pop the response up in a new window.
6508             if(Ext.isIE){
6509                document.frames[id].name = id;
6510             }
6511
6512
6513             Ext.fly(form).set({
6514                 target: id,
6515                 method: POST,
6516                 enctype: encoding,
6517                 encoding: encoding,
6518                 action: url || buf.action
6519             });
6520
6521             // add dynamic params
6522             Ext.iterate(Ext.urlDecode(ps, false), function(k, v){
6523                 hd = doc.createElement('input');
6524                 Ext.fly(hd).set({
6525                     type: 'hidden',
6526                     value: v,
6527                     name: k
6528                 });
6529                 form.appendChild(hd);
6530                 hiddens.push(hd);
6531             });
6532
6533             function cb(){
6534                 var me = this,
6535                     // bogus response object
6536                     r = {responseText : '',
6537                          responseXML : null,
6538                          argument : o.argument},
6539                     doc,
6540                     firstChild;
6541
6542                 try{
6543                     doc = frame.contentWindow.document || frame.contentDocument || WINDOW.frames[id].document;
6544                     if(doc){
6545                         if(doc.body){
6546                             if(/textarea/i.test((firstChild = doc.body.firstChild || {}).tagName)){ // json response wrapped in textarea
6547                                 r.responseText = firstChild.value;
6548                             }else{
6549                                 r.responseText = doc.body.innerHTML;
6550                             }
6551                         }
6552                         //in IE the document may still have a body even if returns XML.
6553                         r.responseXML = doc.XMLDocument || doc;
6554                     }
6555                 }
6556                 catch(e) {}
6557
6558                 Ext.EventManager.removeListener(frame, LOAD, cb, me);
6559
6560                 me.fireEvent(REQUESTCOMPLETE, me, r, o);
6561
6562                 function runCallback(fn, scope, args){
6563                     if(Ext.isFunction(fn)){
6564                         fn.apply(scope, args);
6565                     }
6566                 }
6567
6568                 runCallback(o.success, o.scope, [r, o]);
6569                 runCallback(o.callback, o.scope, [o, true, r]);
6570
6571                 if(!me.debugUploads){
6572                     setTimeout(function(){Ext.removeNode(frame);}, 100);
6573                 }
6574             }
6575
6576             Ext.EventManager.on(frame, LOAD, cb, this);
6577             form.submit();
6578
6579             Ext.fly(form).set(buf);
6580             Ext.each(hiddens, function(h) {
6581                 Ext.removeNode(h);
6582             });
6583         }
6584     });
6585 })();
6586
6587 /**
6588  * @class Ext.Ajax
6589  * @extends Ext.data.Connection
6590  * <p>The global Ajax request class that provides a simple way to make Ajax requests
6591  * with maximum flexibility.</p>
6592  * <p>Since Ext.Ajax is a singleton, you can set common properties/events for it once
6593  * and override them at the request function level only if necessary.</p>
6594  * <p>Common <b>Properties</b> you may want to set are:<div class="mdetail-params"><ul>
6595  * <li><b><tt>{@link #method}</tt></b><p class="sub-desc"></p></li>
6596  * <li><b><tt>{@link #extraParams}</tt></b><p class="sub-desc"></p></li>
6597  * <li><b><tt>{@link #url}</tt></b><p class="sub-desc"></p></li>
6598  * </ul></div>
6599  * <pre><code>
6600 // Default headers to pass in every request
6601 Ext.Ajax.defaultHeaders = {
6602     'Powered-By': 'Ext'
6603 };
6604  * </code></pre>
6605  * </p>
6606  * <p>Common <b>Events</b> you may want to set are:<div class="mdetail-params"><ul>
6607  * <li><b><tt>{@link Ext.data.Connection#beforerequest beforerequest}</tt></b><p class="sub-desc"></p></li>
6608  * <li><b><tt>{@link Ext.data.Connection#requestcomplete requestcomplete}</tt></b><p class="sub-desc"></p></li>
6609  * <li><b><tt>{@link Ext.data.Connection#requestexception requestexception}</tt></b><p class="sub-desc"></p></li>
6610  * </ul></div>
6611  * <pre><code>
6612 // Example: show a spinner during all Ajax requests
6613 Ext.Ajax.on('beforerequest', this.showSpinner, this);
6614 Ext.Ajax.on('requestcomplete', this.hideSpinner, this);
6615 Ext.Ajax.on('requestexception', this.hideSpinner, this);
6616  * </code></pre>
6617  * </p>
6618  * <p>An example request:</p>
6619  * <pre><code>
6620 // Basic request
6621 Ext.Ajax.{@link Ext.data.Connection#request request}({
6622    url: 'foo.php',
6623    success: someFn,
6624    failure: otherFn,
6625    headers: {
6626        'my-header': 'foo'
6627    },
6628    params: { foo: 'bar' }
6629 });
6630
6631 // Simple ajax form submission
6632 Ext.Ajax.{@link Ext.data.Connection#request request}({
6633     form: 'some-form',
6634     params: 'foo=bar'
6635 });
6636  * </code></pre>
6637  * </p>
6638  * @singleton
6639  */
6640 Ext.Ajax = new Ext.data.Connection({
6641     /**
6642      * @cfg {String} url @hide
6643      */
6644     /**
6645      * @cfg {Object} extraParams @hide
6646      */
6647     /**
6648      * @cfg {Object} defaultHeaders @hide
6649      */
6650     /**
6651      * @cfg {String} method (Optional) @hide
6652      */
6653     /**
6654      * @cfg {Number} timeout (Optional) @hide
6655      */
6656     /**
6657      * @cfg {Boolean} autoAbort (Optional) @hide
6658      */
6659
6660     /**
6661      * @cfg {Boolean} disableCaching (Optional) @hide
6662      */
6663
6664     /**
6665      * @property  disableCaching
6666      * True to add a unique cache-buster param to GET requests. (defaults to true)
6667      * @type Boolean
6668      */
6669     /**
6670      * @property  url
6671      * The default URL to be used for requests to the server. (defaults to undefined)
6672      * If the server receives all requests through one URL, setting this once is easier than
6673      * entering it on every request.
6674      * @type String
6675      */
6676     /**
6677      * @property  extraParams
6678      * An object containing properties which are used as extra parameters to each request made
6679      * by this object (defaults to undefined). Session information and other data that you need
6680      * to pass with each request are commonly put here.
6681      * @type Object
6682      */
6683     /**
6684      * @property  defaultHeaders
6685      * An object containing request headers which are added to each request made by this object
6686      * (defaults to undefined).
6687      * @type Object
6688      */
6689     /**
6690      * @property  method
6691      * The default HTTP method to be used for requests. Note that this is case-sensitive and
6692      * should be all caps (defaults to undefined; if not set but params are present will use
6693      * <tt>"POST"</tt>, otherwise will use <tt>"GET"</tt>.)
6694      * @type String
6695      */
6696     /**
6697      * @property  timeout
6698      * The timeout in milliseconds to be used for requests. (defaults to 30000)
6699      * @type Number
6700      */
6701
6702     /**
6703      * @property  autoAbort
6704      * Whether a new request should abort any pending requests. (defaults to false)
6705      * @type Boolean
6706      */
6707     autoAbort : false,
6708
6709     /**
6710      * Serialize the passed form into a url encoded string
6711      * @param {String/HTMLElement} form
6712      * @return {String}
6713      */
6714     serializeForm : function(form){
6715         return Ext.lib.Ajax.serializeForm(form);
6716     }
6717 });
6718 /**
6719  * @class Ext.util.JSON
6720  * Modified version of Douglas Crockford"s json.js that doesn"t
6721  * mess with the Object prototype
6722  * http://www.json.org/js.html
6723  * @singleton
6724  */
6725 Ext.util.JSON = new (function(){
6726     var useHasOwn = !!{}.hasOwnProperty,
6727         isNative = function() {
6728             var useNative = null;
6729
6730             return function() {
6731                 if (useNative === null) {
6732                     useNative = Ext.USE_NATIVE_JSON && window.JSON && JSON.toString() == '[object JSON]';
6733                 }
6734         
6735                 return useNative;
6736             };
6737         }(),
6738         pad = function(n) {
6739             return n < 10 ? "0" + n : n;
6740         },
6741         doDecode = function(json){
6742             return eval("(" + json + ")");    
6743         },
6744         doEncode = function(o){
6745             if(!Ext.isDefined(o) || o === null){
6746                 return "null";
6747             }else if(Ext.isArray(o)){
6748                 return encodeArray(o);
6749             }else if(Ext.isDate(o)){
6750                 return Ext.util.JSON.encodeDate(o);
6751             }else if(Ext.isString(o)){
6752                 return encodeString(o);
6753             }else if(typeof o == "number"){
6754                 //don't use isNumber here, since finite checks happen inside isNumber
6755                 return isFinite(o) ? String(o) : "null";
6756             }else if(Ext.isBoolean(o)){
6757                 return String(o);
6758             }else {
6759                 var a = ["{"], b, i, v;
6760                 for (i in o) {
6761                     // don't encode DOM objects
6762                     if(!o.getElementsByTagName){
6763                         if(!useHasOwn || o.hasOwnProperty(i)) {
6764                             v = o[i];
6765                             switch (typeof v) {
6766                             case "undefined":
6767                             case "function":
6768                             case "unknown":
6769                                 break;
6770                             default:
6771                                 if(b){
6772                                     a.push(',');
6773                                 }
6774                                 a.push(doEncode(i), ":",
6775                                         v === null ? "null" : doEncode(v));
6776                                 b = true;
6777                             }
6778                         }
6779                     }
6780                 }
6781                 a.push("}");
6782                 return a.join("");
6783             }    
6784         },
6785         m = {
6786             "\b": '\\b',
6787             "\t": '\\t',
6788             "\n": '\\n',
6789             "\f": '\\f',
6790             "\r": '\\r',
6791             '"' : '\\"',
6792             "\\": '\\\\'
6793         },
6794         encodeString = function(s){
6795             if (/["\\\x00-\x1f]/.test(s)) {
6796                 return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
6797                     var c = m[b];
6798                     if(c){
6799                         return c;
6800                     }
6801                     c = b.charCodeAt();
6802                     return "\\u00" +
6803                         Math.floor(c / 16).toString(16) +
6804                         (c % 16).toString(16);
6805                 }) + '"';
6806             }
6807             return '"' + s + '"';
6808         },
6809         encodeArray = function(o){
6810             var a = ["["], b, i, l = o.length, v;
6811                 for (i = 0; i < l; i += 1) {
6812                     v = o[i];
6813                     switch (typeof v) {
6814                         case "undefined":
6815                         case "function":
6816                         case "unknown":
6817                             break;
6818                         default:
6819                             if (b) {
6820                                 a.push(',');
6821                             }
6822                             a.push(v === null ? "null" : Ext.util.JSON.encode(v));
6823                             b = true;
6824                     }
6825                 }
6826                 a.push("]");
6827                 return a.join("");
6828         };
6829
6830     /**
6831      * <p>Encodes a Date. This returns the actual string which is inserted into the JSON string as the literal expression.
6832      * <b>The returned value includes enclosing double quotation marks.</b></p>
6833      * <p>The default return format is "yyyy-mm-ddThh:mm:ss".</p>
6834      * <p>To override this:</p><pre><code>
6835 Ext.util.JSON.encodeDate = function(d) {
6836     return d.format('"Y-m-d"');
6837 };
6838 </code></pre>
6839      * @param {Date} d The Date to encode
6840      * @return {String} The string literal to use in a JSON string.
6841      */
6842     this.encodeDate = function(o){
6843         return '"' + o.getFullYear() + "-" +
6844                 pad(o.getMonth() + 1) + "-" +
6845                 pad(o.getDate()) + "T" +
6846                 pad(o.getHours()) + ":" +
6847                 pad(o.getMinutes()) + ":" +
6848                 pad(o.getSeconds()) + '"';
6849     };
6850
6851     /**
6852      * Encodes an Object, Array or other value
6853      * @param {Mixed} o The variable to encode
6854      * @return {String} The JSON string
6855      */
6856     this.encode = function() {
6857         var ec;
6858         return function(o) {
6859             if (!ec) {
6860                 // setup encoding function on first access
6861                 ec = isNative() ? JSON.stringify : doEncode;
6862             }
6863             return ec(o);
6864         };
6865     }();
6866
6867
6868     /**
6869      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError unless the safe option is set.
6870      * @param {String} json The JSON string
6871      * @return {Object} The resulting object
6872      */
6873     this.decode = function() {
6874         var dc;
6875         return function(json) {
6876             if (!dc) {
6877                 // setup decoding function on first access
6878                 dc = isNative() ? JSON.parse : doDecode;
6879             }
6880             return dc(json);
6881         };
6882     }();
6883
6884 })();
6885 /**
6886  * Shorthand for {@link Ext.util.JSON#encode}
6887  * @param {Mixed} o The variable to encode
6888  * @return {String} The JSON string
6889  * @member Ext
6890  * @method encode
6891  */
6892 Ext.encode = Ext.util.JSON.encode;
6893 /**
6894  * Shorthand for {@link Ext.util.JSON#decode}
6895  * @param {String} json The JSON string
6896  * @param {Boolean} safe (optional) Whether to return null or throw an exception if the JSON is invalid.
6897  * @return {Object} The resulting object
6898  * @member Ext
6899  * @method decode
6900  */
6901 Ext.decode = Ext.util.JSON.decode;
6902 /**
6903  * @class Ext.EventManager
6904  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides
6905  * several useful events directly.
6906  * See {@link Ext.EventObject} for more details on normalized event objects.
6907  * @singleton
6908  */
6909 Ext.EventManager = function(){
6910     var docReadyEvent,
6911         docReadyProcId,
6912         docReadyState = false,
6913         DETECT_NATIVE = Ext.isGecko || Ext.isWebKit || Ext.isSafari,
6914         E = Ext.lib.Event,
6915         D = Ext.lib.Dom,
6916         DOC = document,
6917         WINDOW = window,
6918         DOMCONTENTLOADED = "DOMContentLoaded",
6919         COMPLETE = 'complete',
6920         propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/,
6921         /*
6922          * This cache is used to hold special js objects, the document and window, that don't have an id. We need to keep
6923          * a reference to them so we can look them up at a later point.
6924          */
6925         specialElCache = [];
6926
6927      function getId(el){
6928         var id = false,
6929             i = 0,
6930             len = specialElCache.length,
6931             skip = false,
6932             o;
6933             
6934         if (el) {
6935             if (el.getElementById || el.navigator) {
6936                 // look up the id
6937                 for(; i < len; ++i){
6938                     o = specialElCache[i];
6939                     if(o.el === el){
6940                         id = o.id;
6941                         break;
6942                     }
6943                 }
6944                 if(!id){
6945                     // for browsers that support it, ensure that give the el the same id
6946                     id = Ext.id(el);
6947                     specialElCache.push({
6948                         id: id,
6949                         el: el
6950                     });
6951                     skip = true;
6952                 }
6953             }else{
6954                 id = Ext.id(el);
6955             }
6956             if(!Ext.elCache[id]){
6957                 Ext.Element.addToCache(new Ext.Element(el), id);
6958                 if(skip){
6959                     Ext.elCache[id].skipGC = true;
6960                 }
6961             }
6962         }
6963         return id;
6964      }
6965
6966     /// There is some jquery work around stuff here that isn't needed in Ext Core.
6967     function addListener(el, ename, fn, task, wrap, scope){
6968         el = Ext.getDom(el);
6969         var id = getId(el),
6970             es = Ext.elCache[id].events,
6971             wfn;
6972
6973         wfn = E.on(el, ename, wrap);
6974         es[ename] = es[ename] || [];
6975
6976         /* 0 = Original Function,
6977            1 = Event Manager Wrapped Function,
6978            2 = Scope,
6979            3 = Adapter Wrapped Function,
6980            4 = Buffered Task
6981         */
6982         es[ename].push([fn, wrap, scope, wfn, task]);
6983
6984         // this is a workaround for jQuery and should somehow be removed from Ext Core in the future
6985         // without breaking ExtJS.
6986
6987         // workaround for jQuery
6988         if(el.addEventListener && ename == "mousewheel"){
6989             var args = ["DOMMouseScroll", wrap, false];
6990             el.addEventListener.apply(el, args);
6991             Ext.EventManager.addListener(WINDOW, 'unload', function(){
6992                 el.removeEventListener.apply(el, args);
6993             });
6994         }
6995
6996         // fix stopped mousedowns on the document
6997         if(el == DOC && ename == "mousedown"){
6998             Ext.EventManager.stoppedMouseDownEvent.addListener(wrap);
6999         }
7000     }
7001
7002     function doScrollChk(){
7003         /* Notes:
7004              'doScroll' will NOT work in a IFRAME/FRAMESET.
7005              The method succeeds but, a DOM query done immediately after -- FAILS.
7006           */
7007         if(window != top){
7008             return false;
7009         }
7010
7011         try{
7012             DOC.documentElement.doScroll('left');
7013         }catch(e){
7014              return false;
7015         }
7016
7017         fireDocReady();
7018         return true;
7019     }
7020     /**
7021      * @return {Boolean} True if the document is in a 'complete' state (or was determined to
7022      * be true by other means). If false, the state is evaluated again until canceled.
7023      */
7024     function checkReadyState(e){
7025
7026         if(Ext.isIE && doScrollChk()){
7027             return true;
7028         }
7029         if(DOC.readyState == COMPLETE){
7030             fireDocReady();
7031             return true;
7032         }
7033         docReadyState || (docReadyProcId = setTimeout(arguments.callee, 2));
7034         return false;
7035     }
7036
7037     var styles;
7038     function checkStyleSheets(e){
7039         styles || (styles = Ext.query('style, link[rel=stylesheet]'));
7040         if(styles.length == DOC.styleSheets.length){
7041             fireDocReady();
7042             return true;
7043         }
7044         docReadyState || (docReadyProcId = setTimeout(arguments.callee, 2));
7045         return false;
7046     }
7047
7048     function OperaDOMContentLoaded(e){
7049         DOC.removeEventListener(DOMCONTENTLOADED, arguments.callee, false);
7050         checkStyleSheets();
7051     }
7052
7053     function fireDocReady(e){
7054         if(!docReadyState){
7055             docReadyState = true; //only attempt listener removal once
7056
7057             if(docReadyProcId){
7058                 clearTimeout(docReadyProcId);
7059             }
7060             if(DETECT_NATIVE) {
7061                 DOC.removeEventListener(DOMCONTENTLOADED, fireDocReady, false);
7062             }
7063             if(Ext.isIE && checkReadyState.bindIE){  //was this was actually set ??
7064                 DOC.detachEvent('onreadystatechange', checkReadyState);
7065             }
7066             E.un(WINDOW, "load", arguments.callee);
7067         }
7068         if(docReadyEvent && !Ext.isReady){
7069             Ext.isReady = true;
7070             docReadyEvent.fire();
7071             docReadyEvent.listeners = [];
7072         }
7073
7074     }
7075
7076     function initDocReady(){
7077         docReadyEvent || (docReadyEvent = new Ext.util.Event());
7078         if (DETECT_NATIVE) {
7079             DOC.addEventListener(DOMCONTENTLOADED, fireDocReady, false);
7080         }
7081         /*
7082          * Handle additional (exceptional) detection strategies here
7083          */
7084         if (Ext.isIE){
7085             //Use readystatechange as a backup AND primary detection mechanism for a FRAME/IFRAME
7086             //See if page is already loaded
7087             if(!checkReadyState()){
7088                 checkReadyState.bindIE = true;
7089                 DOC.attachEvent('onreadystatechange', checkReadyState);
7090             }
7091
7092         }else if(Ext.isOpera ){
7093             /* Notes:
7094                Opera needs special treatment needed here because CSS rules are NOT QUITE
7095                available after DOMContentLoaded is raised.
7096             */
7097
7098             //See if page is already loaded and all styleSheets are in place
7099             (DOC.readyState == COMPLETE && checkStyleSheets()) ||
7100                 DOC.addEventListener(DOMCONTENTLOADED, OperaDOMContentLoaded, false);
7101
7102         }else if (Ext.isWebKit){
7103             //Fallback for older Webkits without DOMCONTENTLOADED support
7104             checkReadyState();
7105         }
7106         // no matter what, make sure it fires on load
7107         E.on(WINDOW, "load", fireDocReady);
7108     }
7109
7110     function createTargeted(h, o){
7111         return function(){
7112             var args = Ext.toArray(arguments);
7113             if(o.target == Ext.EventObject.setEvent(args[0]).target){
7114                 h.apply(this, args);
7115             }
7116         };
7117     }
7118
7119     function createBuffered(h, o, task){
7120         return function(e){
7121             // create new event object impl so new events don't wipe out properties
7122             task.delay(o.buffer, h, null, [new Ext.EventObjectImpl(e)]);
7123         };
7124     }
7125
7126     function createSingle(h, el, ename, fn, scope){
7127         return function(e){
7128             Ext.EventManager.removeListener(el, ename, fn, scope);
7129             h(e);
7130         };
7131     }
7132
7133     function createDelayed(h, o, fn){
7134         return function(e){
7135             var task = new Ext.util.DelayedTask(h);
7136             if(!fn.tasks) {
7137                 fn.tasks = [];
7138             }
7139             fn.tasks.push(task);
7140             task.delay(o.delay || 10, h, null, [new Ext.EventObjectImpl(e)]);
7141         };
7142     }
7143
7144     function listen(element, ename, opt, fn, scope){
7145         var o = (!opt || typeof opt == "boolean") ? {} : opt,
7146             el = Ext.getDom(element), task;
7147
7148         fn = fn || o.fn;
7149         scope = scope || o.scope;
7150
7151         if(!el){
7152             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
7153         }
7154         function h(e){
7155             // prevent errors while unload occurring
7156             if(!Ext){// !window[xname]){  ==> can't we do this?
7157                 return;
7158             }
7159             e = Ext.EventObject.setEvent(e);
7160             var t;
7161             if (o.delegate) {
7162                 if(!(t = e.getTarget(o.delegate, el))){
7163                     return;
7164                 }
7165             } else {
7166                 t = e.target;
7167             }
7168             if (o.stopEvent) {
7169                 e.stopEvent();
7170             }
7171             if (o.preventDefault) {
7172                e.preventDefault();
7173             }
7174             if (o.stopPropagation) {
7175                 e.stopPropagation();
7176             }
7177             if (o.normalized === false) {
7178                 e = e.browserEvent;
7179             }
7180
7181             fn.call(scope || el, e, t, o);
7182         }
7183         if(o.target){
7184             h = createTargeted(h, o);
7185         }
7186         if(o.delay){
7187             h = createDelayed(h, o, fn);
7188         }
7189         if(o.single){
7190             h = createSingle(h, el, ename, fn, scope);
7191         }
7192         if(o.buffer){
7193             task = new Ext.util.DelayedTask(h);
7194             h = createBuffered(h, o, task);
7195         }
7196
7197         addListener(el, ename, fn, task, h, scope);
7198         return h;
7199     }
7200
7201     var pub = {
7202         /**
7203          * Appends an event handler to an element.  The shorthand version {@link #on} is equivalent.  Typically you will
7204          * use {@link Ext.Element#addListener} directly on an Element in favor of calling this version.
7205          * @param {String/HTMLElement} el The html element or id to assign the event handler to.
7206          * @param {String} eventName The name of the event to listen for.
7207          * @param {Function} handler The handler function the event invokes. This function is passed
7208          * the following parameters:<ul>
7209          * <li>evt : EventObject<div class="sub-desc">The {@link Ext.EventObject EventObject} describing the event.</div></li>
7210          * <li>t : Element<div class="sub-desc">The {@link Ext.Element Element} which was the target of the event.
7211          * Note that this may be filtered by using the <tt>delegate</tt> option.</div></li>
7212          * <li>o : Object<div class="sub-desc">The options object from the addListener call.</div></li>
7213          * </ul>
7214          * @param {Object} scope (optional) The scope (<b><code>this</code></b> reference) in which the handler function is executed. <b>Defaults to the Element</b>.
7215          * @param {Object} options (optional) An object containing handler configuration properties.
7216          * This may contain any of the following properties:<ul>
7217          * <li>scope : Object<div class="sub-desc">The scope (<b><code>this</code></b> reference) in which the handler function is executed. <b>Defaults to the Element</b>.</div></li>
7218          * <li>delegate : String<div class="sub-desc">A simple selector to filter the target or look for a descendant of the target</div></li>
7219          * <li>stopEvent : Boolean<div class="sub-desc">True to stop the event. That is stop propagation, and prevent the default action.</div></li>
7220          * <li>preventDefault : Boolean<div class="sub-desc">True to prevent the default action</div></li>
7221          * <li>stopPropagation : Boolean<div class="sub-desc">True to prevent event propagation</div></li>
7222          * <li>normalized : Boolean<div class="sub-desc">False to pass a browser event to the handler function instead of an Ext.EventObject</div></li>
7223          * <li>delay : Number<div class="sub-desc">The number of milliseconds to delay the invocation of the handler after te event fires.</div></li>
7224          * <li>single : Boolean<div class="sub-desc">True to add a handler to handle just the next firing of the event, and then remove itself.</div></li>
7225          * <li>buffer : Number<div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
7226          * by the specified number of milliseconds. If the event fires again within that time, the original
7227          * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
7228          * <li>target : Element<div class="sub-desc">Only call the handler if the event was fired on the target Element, <i>not</i> if the event was bubbled up from a child node.</div></li>
7229          * </ul><br>
7230          * <p>See {@link Ext.Element#addListener} for examples of how to use these options.</p>
7231          */
7232         addListener : function(element, eventName, fn, scope, options){
7233             if(typeof eventName == 'object'){
7234                 var o = eventName, e, val;
7235                 for(e in o){
7236                     val = o[e];
7237                     if(!propRe.test(e)){
7238                         if(Ext.isFunction(val)){
7239                             // shared options
7240                             listen(element, e, o, val, o.scope);
7241                         }else{
7242                             // individual options
7243                             listen(element, e, val);
7244                         }
7245                     }
7246                 }
7247             } else {
7248                 listen(element, eventName, options, fn, scope);
7249             }
7250         },
7251
7252         /**
7253          * Removes an event handler from an element.  The shorthand version {@link #un} is equivalent.  Typically
7254          * you will use {@link Ext.Element#removeListener} directly on an Element in favor of calling this version.
7255          * @param {String/HTMLElement} el The id or html element from which to remove the listener.
7256          * @param {String} eventName The name of the event.
7257          * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
7258          * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
7259          * then this must refer to the same object.
7260          */
7261         removeListener : function(el, eventName, fn, scope){
7262             el = Ext.getDom(el);
7263             var id = getId(el),
7264                 f = el && (Ext.elCache[id].events)[eventName] || [],
7265                 wrap, i, l, k, len, fnc;
7266
7267             for (i = 0, len = f.length; i < len; i++) {
7268
7269                 /* 0 = Original Function,
7270                    1 = Event Manager Wrapped Function,
7271                    2 = Scope,
7272                    3 = Adapter Wrapped Function,
7273                    4 = Buffered Task
7274                 */
7275                 if (Ext.isArray(fnc = f[i]) && fnc[0] == fn && (!scope || fnc[2] == scope)) {
7276                     if(fnc[4]) {
7277                         fnc[4].cancel();
7278                     }
7279                     k = fn.tasks && fn.tasks.length;
7280                     if(k) {
7281                         while(k--) {
7282                             fn.tasks[k].cancel();
7283                         }
7284                         delete fn.tasks;
7285                     }
7286                     wrap = fnc[1];
7287                     E.un(el, eventName, E.extAdapter ? fnc[3] : wrap);
7288
7289                     // jQuery workaround that should be removed from Ext Core
7290                     if(wrap && el.addEventListener && eventName == "mousewheel"){
7291                         el.removeEventListener("DOMMouseScroll", wrap, false);
7292                     }
7293
7294                     // fix stopped mousedowns on the document
7295                     if(wrap && el == DOC && eventName == "mousedown"){
7296                         Ext.EventManager.stoppedMouseDownEvent.removeListener(wrap);
7297                     }
7298
7299                     f.splice(i, 1);
7300                     if (f.length === 0) {
7301                         delete Ext.elCache[id].events[eventName];
7302                     }
7303                     for (k in Ext.elCache[id].events) {
7304                         return false;
7305                     }
7306                     Ext.elCache[id].events = {};
7307                     return false;
7308                 }
7309             }
7310         },
7311
7312         /**
7313          * Removes all event handers from an element.  Typically you will use {@link Ext.Element#removeAllListeners}
7314          * directly on an Element in favor of calling this version.
7315          * @param {String/HTMLElement} el The id or html element from which to remove all event handlers.
7316          */
7317         removeAll : function(el){
7318             el = Ext.getDom(el);
7319             var id = getId(el),
7320                 ec = Ext.elCache[id] || {},
7321                 es = ec.events || {},
7322                 f, i, len, ename, fn, k, wrap;
7323
7324             for(ename in es){
7325                 if(es.hasOwnProperty(ename)){
7326                     f = es[ename];
7327                     /* 0 = Original Function,
7328                        1 = Event Manager Wrapped Function,
7329                        2 = Scope,
7330                        3 = Adapter Wrapped Function,
7331                        4 = Buffered Task
7332                     */
7333                     for (i = 0, len = f.length; i < len; i++) {
7334                         fn = f[i];
7335                         if(fn[4]) {
7336                             fn[4].cancel();
7337                         }
7338                         if(fn[0].tasks && (k = fn[0].tasks.length)) {
7339                             while(k--) {
7340                                 fn[0].tasks[k].cancel();
7341                             }
7342                             delete fn.tasks;
7343                         }
7344                         wrap =  fn[1];
7345                         E.un(el, ename, E.extAdapter ? fn[3] : wrap);
7346
7347                         // jQuery workaround that should be removed from Ext Core
7348                         if(el.addEventListener && wrap && ename == "mousewheel"){
7349                             el.removeEventListener("DOMMouseScroll", wrap, false);
7350                         }
7351
7352                         // fix stopped mousedowns on the document
7353                         if(wrap && el == DOC &&  ename == "mousedown"){
7354                             Ext.EventManager.stoppedMouseDownEvent.removeListener(wrap);
7355                         }
7356                     }
7357                 }
7358             }
7359             if (Ext.elCache[id]) {
7360                 Ext.elCache[id].events = {};
7361             }
7362         },
7363
7364         getListeners : function(el, eventName) {
7365             el = Ext.getDom(el);
7366             var id = getId(el),
7367                 ec = Ext.elCache[id] || {},
7368                 es = ec.events || {},
7369                 results = [];
7370             if (es && es[eventName]) {
7371                 return es[eventName];
7372             } else {
7373                 return null;
7374             }
7375         },
7376
7377         purgeElement : function(el, recurse, eventName) {
7378             el = Ext.getDom(el);
7379             var id = getId(el),
7380                 ec = Ext.elCache[id] || {},
7381                 es = ec.events || {},
7382                 i, f, len;
7383             if (eventName) {
7384                 if (es && es.hasOwnProperty(eventName)) {
7385                     f = es[eventName];
7386                     for (i = 0, len = f.length; i < len; i++) {
7387                         Ext.EventManager.removeListener(el, eventName, f[i][0]);
7388                     }
7389                 }
7390             } else {
7391                 Ext.EventManager.removeAll(el);
7392             }
7393             if (recurse && el && el.childNodes) {
7394                 for (i = 0, len = el.childNodes.length; i < len; i++) {
7395                     Ext.EventManager.purgeElement(el.childNodes[i], recurse, eventName);
7396                 }
7397             }
7398         },
7399
7400         _unload : function() {
7401             var el;
7402             for (el in Ext.elCache) {
7403                 Ext.EventManager.removeAll(el);
7404             }
7405             delete Ext.elCache;
7406             delete Ext.Element._flyweights;
7407
7408             // Abort any outstanding Ajax requests
7409             var c,
7410                 conn,
7411                 tid,
7412                 ajax = Ext.lib.Ajax;
7413             (typeof ajax.conn == 'object') ? conn = ajax.conn : conn = {};
7414             for (tid in conn) {
7415                 c = conn[tid];
7416                 if (c) {
7417                     ajax.abort({conn: c, tId: tid});
7418                 }
7419             }
7420         },
7421         /**
7422          * Adds a listener to be notified when the document is ready (before onload and before images are loaded). Can be
7423          * accessed shorthanded as Ext.onReady().
7424          * @param {Function} fn The method the event invokes.
7425          * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
7426          * @param {boolean} options (optional) Options object as passed to {@link Ext.Element#addListener}. It is recommended that the options
7427          * <code>{single: true}</code> be used so that the handler is removed on first invocation.
7428          */
7429         onDocumentReady : function(fn, scope, options){
7430             if (Ext.isReady) { // if it already fired or document.body is present
7431                 docReadyEvent || (docReadyEvent = new Ext.util.Event());
7432                 docReadyEvent.addListener(fn, scope, options);
7433                 docReadyEvent.fire();
7434                 docReadyEvent.listeners = [];
7435             } else {
7436                 if (!docReadyEvent) {
7437                     initDocReady();
7438                 }
7439                 options = options || {};
7440                 options.delay = options.delay || 1;
7441                 docReadyEvent.addListener(fn, scope, options);
7442             }
7443         },
7444
7445         /**
7446          * Forces a document ready state transition for the framework.  Used when Ext is loaded
7447          * into a DOM structure AFTER initial page load (Google API or other dynamic load scenario.
7448          * Any pending 'onDocumentReady' handlers will be fired (if not already handled).
7449          */
7450         fireDocReady  : fireDocReady
7451     };
7452      /**
7453      * Appends an event handler to an element.  Shorthand for {@link #addListener}.
7454      * @param {String/HTMLElement} el The html element or id to assign the event handler to
7455      * @param {String} eventName The name of the event to listen for.
7456      * @param {Function} handler The handler function the event invokes.
7457      * @param {Object} scope (optional) (<code>this</code> reference) in which the handler function executes. <b>Defaults to the Element</b>.
7458      * @param {Object} options (optional) An object containing standard {@link #addListener} options
7459      * @member Ext.EventManager
7460      * @method on
7461      */
7462     pub.on = pub.addListener;
7463     /**
7464      * Removes an event handler from an element.  Shorthand for {@link #removeListener}.
7465      * @param {String/HTMLElement} el The id or html element from which to remove the listener.
7466      * @param {String} eventName The name of the event.
7467      * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #on} call.</b>
7468      * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
7469      * then this must refer to the same object.
7470      * @member Ext.EventManager
7471      * @method un
7472      */
7473     pub.un = pub.removeListener;
7474
7475     pub.stoppedMouseDownEvent = new Ext.util.Event();
7476     return pub;
7477 }();
7478 /**
7479   * Adds a listener to be notified when the document is ready (before onload and before images are loaded). Shorthand of {@link Ext.EventManager#onDocumentReady}.
7480   * @param {Function} fn The method the event invokes.
7481   * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
7482   * @param {boolean} options (optional) Options object as passed to {@link Ext.Element#addListener}. It is recommended that the options
7483   * <code>{single: true}</code> be used so that the handler is removed on first invocation.
7484   * @member Ext
7485   * @method onReady
7486  */
7487 Ext.onReady = Ext.EventManager.onDocumentReady;
7488
7489
7490 //Initialize doc classes
7491 (function(){
7492     var initExtCss = function() {
7493         // find the body element
7494         var bd = document.body || document.getElementsByTagName('body')[0];
7495         if (!bd) {
7496             return false;
7497         }
7498         
7499         var cls = [' ',
7500                 Ext.isIE ? "ext-ie " + (Ext.isIE6 ? 'ext-ie6' : (Ext.isIE7 ? 'ext-ie7' : 'ext-ie8'))
7501                 : Ext.isGecko ? "ext-gecko " + (Ext.isGecko2 ? 'ext-gecko2' : 'ext-gecko3')
7502                 : Ext.isOpera ? "ext-opera"
7503                 : Ext.isWebKit ? "ext-webkit" : ""];
7504
7505         if (Ext.isSafari) {
7506             cls.push("ext-safari " + (Ext.isSafari2 ? 'ext-safari2' : (Ext.isSafari3 ? 'ext-safari3' : 'ext-safari4')));
7507         } else if(Ext.isChrome) {
7508             cls.push("ext-chrome");
7509         }
7510
7511         if (Ext.isMac) {
7512             cls.push("ext-mac");
7513         }
7514         if (Ext.isLinux) {
7515             cls.push("ext-linux");
7516         }
7517
7518         // add to the parent to allow for selectors like ".ext-strict .ext-ie"
7519         if (Ext.isStrict || Ext.isBorderBox) {
7520             var p = bd.parentNode;
7521             if (p) {
7522                 Ext.fly(p, '_internal').addClass(((Ext.isStrict && Ext.isIE ) || (!Ext.enableForcedBoxModel && !Ext.isIE)) ? ' ext-strict' : ' ext-border-box');
7523             }
7524         }
7525         // Forced border box model class applied to all elements. Bypassing javascript based box model adjustments
7526         // in favor of css.  This is for non-IE browsers.
7527         if (Ext.enableForcedBoxModel && !Ext.isIE) {
7528             Ext.isForcedBorderBox = true;
7529             cls.push("ext-forced-border-box");
7530         }
7531         
7532         Ext.fly(bd, '_internal').addClass(cls);
7533         return true;
7534     };
7535     
7536     if (!initExtCss()) {
7537         Ext.onReady(initExtCss);
7538     }
7539 })();
7540
7541 /**
7542  * Code used to detect certain browser feature/quirks/bugs at startup.
7543  */
7544 (function(){
7545     var supports = Ext.apply(Ext.supports, {
7546         /**
7547          * In Webkit, there is an issue with getting the margin right property, see
7548          * https://bugs.webkit.org/show_bug.cgi?id=13343
7549          */
7550         correctRightMargin: true,
7551         
7552         /**
7553          * Webkit browsers return rgba(0, 0, 0) when a transparent color is used
7554          */
7555         correctTransparentColor: true,
7556         
7557         /**
7558          * IE uses styleFloat, not cssFloat for the float property.
7559          */
7560         cssFloat: true
7561     });
7562     
7563     var supportTests = function(){
7564             var div = document.createElement('div'),
7565                 doc = document,
7566                 view,
7567                 last;
7568                 
7569             div.innerHTML = '<div style="height:30px;width:50px;"><div style="height:20px;width:20px;"></div></div><div style="float:left;background-color:transparent;">';
7570             doc.body.appendChild(div);
7571             last = div.lastChild;
7572             
7573             if((view = doc.defaultView)){
7574                 if(view.getComputedStyle(div.firstChild.firstChild, null).marginRight != '0px'){
7575                     supports.correctRightMargin = false;
7576                 }
7577                 if(view.getComputedStyle(last, null).backgroundColor != 'transparent'){
7578                     supports.correctTransparentColor = false;
7579                 }
7580             }
7581             supports.cssFloat = !!last.style.cssFloat;
7582             doc.body.removeChild(div);
7583     };
7584     
7585     if (Ext.isReady) {
7586         supportTests();    
7587     } else {
7588         Ext.onReady(supportTests);
7589     }
7590 })();
7591
7592
7593 /**
7594  * @class Ext.EventObject
7595  * Just as {@link Ext.Element} wraps around a native DOM node, Ext.EventObject
7596  * wraps the browser's native event-object normalizing cross-browser differences,
7597  * such as which mouse button is clicked, keys pressed, mechanisms to stop
7598  * event-propagation along with a method to prevent default actions from taking place.
7599  * <p>For example:</p>
7600  * <pre><code>
7601 function handleClick(e, t){ // e is not a standard event object, it is a Ext.EventObject
7602     e.preventDefault();
7603     var target = e.getTarget(); // same as t (the target HTMLElement)
7604     ...
7605 }
7606 var myDiv = {@link Ext#get Ext.get}("myDiv");  // get reference to an {@link Ext.Element}
7607 myDiv.on(         // 'on' is shorthand for addListener
7608     "click",      // perform an action on click of myDiv
7609     handleClick   // reference to the action handler
7610 );
7611 // other methods to do the same:
7612 Ext.EventManager.on("myDiv", 'click', handleClick);
7613 Ext.EventManager.addListener("myDiv", 'click', handleClick);
7614  </code></pre>
7615  * @singleton
7616  */
7617 Ext.EventObject = function(){
7618     var E = Ext.lib.Event,
7619         clickRe = /(dbl)?click/,
7620         // safari keypress events for special keys return bad keycodes
7621         safariKeys = {
7622             3 : 13, // enter
7623             63234 : 37, // left
7624             63235 : 39, // right
7625             63232 : 38, // up
7626             63233 : 40, // down
7627             63276 : 33, // page up
7628             63277 : 34, // page down
7629             63272 : 46, // delete
7630             63273 : 36, // home
7631             63275 : 35  // end
7632         },
7633         // normalize button clicks
7634         btnMap = Ext.isIE ? {1:0,4:1,2:2} : {0:0,1:1,2:2};
7635
7636     Ext.EventObjectImpl = function(e){
7637         if(e){
7638             this.setEvent(e.browserEvent || e);
7639         }
7640     };
7641
7642     Ext.EventObjectImpl.prototype = {
7643            /** @private */
7644         setEvent : function(e){
7645             var me = this;
7646             if(e == me || (e && e.browserEvent)){ // already wrapped
7647                 return e;
7648             }
7649             me.browserEvent = e;
7650             if(e){
7651                 // normalize buttons
7652                 me.button = e.button ? btnMap[e.button] : (e.which ? e.which - 1 : -1);
7653                 if(clickRe.test(e.type) && me.button == -1){
7654                     me.button = 0;
7655                 }
7656                 me.type = e.type;
7657                 me.shiftKey = e.shiftKey;
7658                 // mac metaKey behaves like ctrlKey
7659                 me.ctrlKey = e.ctrlKey || e.metaKey || false;
7660                 me.altKey = e.altKey;
7661                 // in getKey these will be normalized for the mac
7662                 me.keyCode = e.keyCode;
7663                 me.charCode = e.charCode;
7664                 // cache the target for the delayed and or buffered events
7665                 me.target = E.getTarget(e);
7666                 // same for XY
7667                 me.xy = E.getXY(e);
7668             }else{
7669                 me.button = -1;
7670                 me.shiftKey = false;
7671                 me.ctrlKey = false;
7672                 me.altKey = false;
7673                 me.keyCode = 0;
7674                 me.charCode = 0;
7675                 me.target = null;
7676                 me.xy = [0, 0];
7677             }
7678             return me;
7679         },
7680
7681         /**
7682          * Stop the event (preventDefault and stopPropagation)
7683          */
7684         stopEvent : function(){
7685             var me = this;
7686             if(me.browserEvent){
7687                 if(me.browserEvent.type == 'mousedown'){
7688                     Ext.EventManager.stoppedMouseDownEvent.fire(me);
7689                 }
7690                 E.stopEvent(me.browserEvent);
7691             }
7692         },
7693
7694         /**
7695          * Prevents the browsers default handling of the event.
7696          */
7697         preventDefault : function(){
7698             if(this.browserEvent){
7699                 E.preventDefault(this.browserEvent);
7700             }
7701         },
7702
7703         /**
7704          * Cancels bubbling of the event.
7705          */
7706         stopPropagation : function(){
7707             var me = this;
7708             if(me.browserEvent){
7709                 if(me.browserEvent.type == 'mousedown'){
7710                     Ext.EventManager.stoppedMouseDownEvent.fire(me);
7711                 }
7712                 E.stopPropagation(me.browserEvent);
7713             }
7714         },
7715
7716         /**
7717          * Gets the character code for the event.
7718          * @return {Number}
7719          */
7720         getCharCode : function(){
7721             return this.charCode || this.keyCode;
7722         },
7723
7724         /**
7725          * Returns a normalized keyCode for the event.
7726          * @return {Number} The key code
7727          */
7728         getKey : function(){
7729             return this.normalizeKey(this.keyCode || this.charCode);
7730         },
7731
7732         // private
7733         normalizeKey: function(k){
7734             return Ext.isSafari ? (safariKeys[k] || k) : k;
7735         },
7736
7737         /**
7738          * Gets the x coordinate of the event.
7739          * @return {Number}
7740          */
7741         getPageX : function(){
7742             return this.xy[0];
7743         },
7744
7745         /**
7746          * Gets the y coordinate of the event.
7747          * @return {Number}
7748          */
7749         getPageY : function(){
7750             return this.xy[1];
7751         },
7752
7753         /**
7754          * Gets the page coordinates of the event.
7755          * @return {Array} The xy values like [x, y]
7756          */
7757         getXY : function(){
7758             return this.xy;
7759         },
7760
7761         /**
7762          * Gets the target for the event.
7763          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
7764          * @param {Number/Mixed} maxDepth (optional) The max depth to
7765                 search as a number or element (defaults to 10 || document.body)
7766          * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node
7767          * @return {HTMLelement}
7768          */
7769         getTarget : function(selector, maxDepth, returnEl){
7770             return selector ? Ext.fly(this.target).findParent(selector, maxDepth, returnEl) : (returnEl ? Ext.get(this.target) : this.target);
7771         },
7772
7773         /**
7774          * Gets the related target.
7775          * @return {HTMLElement}
7776          */
7777         getRelatedTarget : function(){
7778             return this.browserEvent ? E.getRelatedTarget(this.browserEvent) : null;
7779         },
7780
7781         /**
7782          * Normalizes mouse wheel delta across browsers
7783          * @return {Number} The delta
7784          */
7785         getWheelDelta : function(){
7786             var e = this.browserEvent;
7787             var delta = 0;
7788             if(e.wheelDelta){ /* IE/Opera. */
7789                 delta = e.wheelDelta/120;
7790             }else if(e.detail){ /* Mozilla case. */
7791                 delta = -e.detail/3;
7792             }
7793             return delta;
7794         },
7795
7796         /**
7797         * Returns true if the target of this event is a child of el.  Unless the allowEl parameter is set, it will return false if if the target is el.
7798         * Example usage:<pre><code>
7799         // Handle click on any child of an element
7800         Ext.getBody().on('click', function(e){
7801             if(e.within('some-el')){
7802                 alert('Clicked on a child of some-el!');
7803             }
7804         });
7805
7806         // Handle click directly on an element, ignoring clicks on child nodes
7807         Ext.getBody().on('click', function(e,t){
7808             if((t.id == 'some-el') && !e.within(t, true)){
7809                 alert('Clicked directly on some-el!');
7810             }
7811         });
7812         </code></pre>
7813          * @param {Mixed} el The id, DOM element or Ext.Element to check
7814          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
7815          * @param {Boolean} allowEl {optional} true to also check if the passed element is the target or related target
7816          * @return {Boolean}
7817          */
7818         within : function(el, related, allowEl){
7819             if(el){
7820                 var t = this[related ? "getRelatedTarget" : "getTarget"]();
7821                 return t && ((allowEl ? (t == Ext.getDom(el)) : false) || Ext.fly(el).contains(t));
7822             }
7823             return false;
7824         }
7825      };
7826
7827     return new Ext.EventObjectImpl();
7828 }();
7829 /**
7830  * @class Ext.Loader
7831  * @singleton
7832  * Simple class to help load JavaScript files on demand
7833  */
7834 Ext.Loader = Ext.apply({}, {
7835     /**
7836      * Loads a given set of .js files. Calls the callback function when all files have been loaded
7837      * Set preserveOrder to true to ensure non-parallel loading of files if load order is important
7838      * @param {Array} fileList Array of all files to load
7839      * @param {Function} callback Callback to call after all files have been loaded
7840      * @param {Object} scope The scope to call the callback in
7841      * @param {Boolean} preserveOrder True to make files load in serial, one after the other (defaults to false)
7842      */
7843     load: function(fileList, callback, scope, preserveOrder) {
7844         var scope       = scope || this,
7845             head        = document.getElementsByTagName("head")[0],
7846             fragment    = document.createDocumentFragment(),
7847             numFiles    = fileList.length,
7848             loadedFiles = 0,
7849             me          = this;
7850         
7851         /**
7852          * Loads a particular file from the fileList by index. This is used when preserving order
7853          */
7854         var loadFileIndex = function(index) {
7855             head.appendChild(
7856                 me.buildScriptTag(fileList[index], onFileLoaded)
7857             );
7858         };
7859         
7860         /**
7861          * Callback function which is called after each file has been loaded. This calls the callback
7862          * passed to load once the final file in the fileList has been loaded
7863          */
7864         var onFileLoaded = function() {
7865             loadedFiles ++;
7866             
7867             //if this was the last file, call the callback, otherwise load the next file
7868             if (numFiles == loadedFiles && typeof callback == 'function') {
7869                 callback.call(scope);
7870             } else {
7871                 if (preserveOrder === true) {
7872                     loadFileIndex(loadedFiles);
7873                 }
7874             }
7875         };
7876         
7877         if (preserveOrder === true) {
7878             loadFileIndex.call(this, 0);
7879         } else {
7880             //load each file (most browsers will do this in parallel)
7881             Ext.each(fileList, function(file, index) {
7882                 fragment.appendChild(
7883                     this.buildScriptTag(file, onFileLoaded)
7884                 );  
7885             }, this);
7886             
7887             head.appendChild(fragment);
7888         }
7889     },
7890     
7891     /**
7892      * @private
7893      * Creates and returns a script tag, but does not place it into the document. If a callback function
7894      * is passed, this is called when the script has been loaded
7895      * @param {String} filename The name of the file to create a script tag for
7896      * @param {Function} callback Optional callback, which is called when the script has been loaded
7897      * @return {Element} The new script ta
7898      */
7899     buildScriptTag: function(filename, callback) {
7900         var script  = document.createElement('script');
7901         script.type = "text/javascript";
7902         script.src  = filename;
7903         
7904         //IE has a different way of handling &lt;script&gt; loads, so we need to check for it here
7905         if (script.readyState) {
7906             script.onreadystatechange = function() {
7907                 if (script.readyState == "loaded" || script.readyState == "complete") {
7908                     script.onreadystatechange = null;
7909                     callback();
7910                 }
7911             };
7912         } else {
7913             script.onload = callback;
7914         }    
7915         
7916         return script;
7917     }
7918 });
7919 /**
7920  * @class Ext
7921  */
7922
7923 Ext.ns("Ext.grid", "Ext.list", "Ext.dd", "Ext.tree", "Ext.form", "Ext.menu",
7924        "Ext.state", "Ext.layout", "Ext.app", "Ext.ux", "Ext.chart", "Ext.direct");
7925     /**
7926      * Namespace alloted for extensions to the framework.
7927      * @property ux
7928      * @type Object
7929      */
7930
7931 Ext.apply(Ext, function(){
7932     var E = Ext,
7933         idSeed = 0,
7934         scrollWidth = null;
7935
7936     return {
7937         /**
7938         * A reusable empty function
7939         * @property
7940         * @type Function
7941         */
7942         emptyFn : function(){},
7943
7944         /**
7945          * URL to a 1x1 transparent gif image used by Ext to create inline icons with CSS background images.
7946          * In older versions of IE, this defaults to "http://extjs.com/s.gif" and you should change this to a URL on your server.
7947          * For other browsers it uses an inline data URL.
7948          * @type String
7949          */
7950         BLANK_IMAGE_URL : Ext.isIE6 || Ext.isIE7 || Ext.isAir ?
7951                             'http:/' + '/www.extjs.com/s.gif' :
7952                             'data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==',
7953
7954         extendX : function(supr, fn){
7955             return Ext.extend(supr, fn(supr.prototype));
7956         },
7957
7958         /**
7959          * Returns the current HTML document object as an {@link Ext.Element}.
7960          * @return Ext.Element The document
7961          */
7962         getDoc : function(){
7963             return Ext.get(document);
7964         },
7965
7966         /**
7967          * Utility method for validating that a value is numeric, returning the specified default value if it is not.
7968          * @param {Mixed} value Should be a number, but any type will be handled appropriately
7969          * @param {Number} defaultValue The value to return if the original value is non-numeric
7970          * @return {Number} Value, if numeric, else defaultValue
7971          */
7972         num : function(v, defaultValue){
7973             v = Number(Ext.isEmpty(v) || Ext.isArray(v) || typeof v == 'boolean' || (typeof v == 'string' && v.trim().length == 0) ? NaN : v);
7974             return isNaN(v) ? defaultValue : v;
7975         },
7976
7977         /**
7978          * <p>Utility method for returning a default value if the passed value is empty.</p>
7979          * <p>The value is deemed to be empty if it is<div class="mdetail-params"><ul>
7980          * <li>null</li>
7981          * <li>undefined</li>
7982          * <li>an empty array</li>
7983          * <li>a zero length string (Unless the <tt>allowBlank</tt> parameter is <tt>true</tt>)</li>
7984          * </ul></div>
7985          * @param {Mixed} value The value to test
7986          * @param {Mixed} defaultValue The value to return if the original value is empty
7987          * @param {Boolean} allowBlank (optional) true to allow zero length strings to qualify as non-empty (defaults to false)
7988          * @return {Mixed} value, if non-empty, else defaultValue
7989          */
7990         value : function(v, defaultValue, allowBlank){
7991             return Ext.isEmpty(v, allowBlank) ? defaultValue : v;
7992         },
7993
7994         /**
7995          * Escapes the passed string for use in a regular expression
7996          * @param {String} str
7997          * @return {String}
7998          */
7999         escapeRe : function(s) {
8000             return s.replace(/([-.*+?^${}()|[\]\/\\])/g, "\\$1");
8001         },
8002
8003         sequence : function(o, name, fn, scope){
8004             o[name] = o[name].createSequence(fn, scope);
8005         },
8006
8007         /**
8008          * Applies event listeners to elements by selectors when the document is ready.
8009          * The event name is specified with an <tt>&#64;</tt> suffix.
8010          * <pre><code>
8011 Ext.addBehaviors({
8012     // add a listener for click on all anchors in element with id foo
8013     '#foo a&#64;click' : function(e, t){
8014         // do something
8015     },
8016
8017     // add the same listener to multiple selectors (separated by comma BEFORE the &#64;)
8018     '#foo a, #bar span.some-class&#64;mouseover' : function(){
8019         // do something
8020     }
8021 });
8022          * </code></pre>
8023          * @param {Object} obj The list of behaviors to apply
8024          */
8025         addBehaviors : function(o){
8026             if(!Ext.isReady){
8027                 Ext.onReady(function(){
8028                     Ext.addBehaviors(o);
8029                 });
8030             } else {
8031                 var cache = {}, // simple cache for applying multiple behaviors to same selector does query multiple times
8032                     parts,
8033                     b,
8034                     s;
8035                 for (b in o) {
8036                     if ((parts = b.split('@'))[1]) { // for Object prototype breakers
8037                         s = parts[0];
8038                         if(!cache[s]){
8039                             cache[s] = Ext.select(s);
8040                         }
8041                         cache[s].on(parts[1], o[b]);
8042                     }
8043                 }
8044                 cache = null;
8045             }
8046         },
8047
8048         /**
8049          * Utility method for getting the width of the browser scrollbar. This can differ depending on
8050          * operating system settings, such as the theme or font size.
8051          * @param {Boolean} force (optional) true to force a recalculation of the value.
8052          * @return {Number} The width of the scrollbar.
8053          */
8054         getScrollBarWidth: function(force){
8055             if(!Ext.isReady){
8056                 return 0;
8057             }
8058
8059             if(force === true || scrollWidth === null){
8060                     // Append our div, do our calculation and then remove it
8061                 var div = Ext.getBody().createChild('<div class="x-hide-offsets" style="width:100px;height:50px;overflow:hidden;"><div style="height:200px;"></div></div>'),
8062                     child = div.child('div', true);
8063                 var w1 = child.offsetWidth;
8064                 div.setStyle('overflow', (Ext.isWebKit || Ext.isGecko) ? 'auto' : 'scroll');
8065                 var w2 = child.offsetWidth;
8066                 div.remove();
8067                 // Need to add 2 to ensure we leave enough space
8068                 scrollWidth = w1 - w2 + 2;
8069             }
8070             return scrollWidth;
8071         },
8072
8073
8074         // deprecated
8075         combine : function(){
8076             var as = arguments, l = as.length, r = [];
8077             for(var i = 0; i < l; i++){
8078                 var a = as[i];
8079                 if(Ext.isArray(a)){
8080                     r = r.concat(a);
8081                 }else if(a.length !== undefined && !a.substr){
8082                     r = r.concat(Array.prototype.slice.call(a, 0));
8083                 }else{
8084                     r.push(a);
8085                 }
8086             }
8087             return r;
8088         },
8089
8090         /**
8091          * Copies a set of named properties fom the source object to the destination object.
8092          * <p>example:<pre><code>
8093 ImageComponent = Ext.extend(Ext.BoxComponent, {
8094     initComponent: function() {
8095         this.autoEl = { tag: 'img' };
8096         MyComponent.superclass.initComponent.apply(this, arguments);
8097         this.initialBox = Ext.copyTo({}, this.initialConfig, 'x,y,width,height');
8098     }
8099 });
8100          * </code></pre>
8101          * @param {Object} dest The destination object.
8102          * @param {Object} source The source object.
8103          * @param {Array/String} names Either an Array of property names, or a comma-delimited list
8104          * of property names to copy.
8105          * @return {Object} The modified object.
8106         */
8107         copyTo : function(dest, source, names){
8108             if(typeof names == 'string'){
8109                 names = names.split(/[,;\s]/);
8110             }
8111             Ext.each(names, function(name){
8112                 if(source.hasOwnProperty(name)){
8113                     dest[name] = source[name];
8114                 }
8115             }, this);
8116             return dest;
8117         },
8118
8119         /**
8120          * Attempts to destroy any objects passed to it by removing all event listeners, removing them from the
8121          * DOM (if applicable) and calling their destroy functions (if available).  This method is primarily
8122          * intended for arguments of type {@link Ext.Element} and {@link Ext.Component}, but any subclass of
8123          * {@link Ext.util.Observable} can be passed in.  Any number of elements and/or components can be
8124          * passed into this function in a single call as separate arguments.
8125          * @param {Mixed} arg1 An {@link Ext.Element}, {@link Ext.Component}, or an Array of either of these to destroy
8126          * @param {Mixed} arg2 (optional)
8127          * @param {Mixed} etc... (optional)
8128          */
8129         destroy : function(){
8130             Ext.each(arguments, function(arg){
8131                 if(arg){
8132                     if(Ext.isArray(arg)){
8133                         this.destroy.apply(this, arg);
8134                     }else if(typeof arg.destroy == 'function'){
8135                         arg.destroy();
8136                     }else if(arg.dom){
8137                         arg.remove();
8138                     }
8139                 }
8140             }, this);
8141         },
8142
8143         /**
8144          * Attempts to destroy and then remove a set of named properties of the passed object.
8145          * @param {Object} o The object (most likely a Component) who's properties you wish to destroy.
8146          * @param {Mixed} arg1 The name of the property to destroy and remove from the object.
8147          * @param {Mixed} etc... More property names to destroy and remove.
8148          */
8149         destroyMembers : function(o, arg1, arg2, etc){
8150             for(var i = 1, a = arguments, len = a.length; i < len; i++) {
8151                 Ext.destroy(o[a[i]]);
8152                 delete o[a[i]];
8153             }
8154         },
8155
8156         /**
8157          * Creates a copy of the passed Array with falsy values removed.
8158          * @param {Array/NodeList} arr The Array from which to remove falsy values.
8159          * @return {Array} The new, compressed Array.
8160          */
8161         clean : function(arr){
8162             var ret = [];
8163             Ext.each(arr, function(v){
8164                 if(!!v){
8165                     ret.push(v);
8166                 }
8167             });
8168             return ret;
8169         },
8170
8171         /**
8172          * Creates a copy of the passed Array, filtered to contain only unique values.
8173          * @param {Array} arr The Array to filter
8174          * @return {Array} The new Array containing unique values.
8175          */
8176         unique : function(arr){
8177             var ret = [],
8178                 collect = {};
8179
8180             Ext.each(arr, function(v) {
8181                 if(!collect[v]){
8182                     ret.push(v);
8183                 }
8184                 collect[v] = true;
8185             });
8186             return ret;
8187         },
8188
8189         /**
8190          * Recursively flattens into 1-d Array. Injects Arrays inline.
8191          * @param {Array} arr The array to flatten
8192          * @return {Array} The new, flattened array.
8193          */
8194         flatten : function(arr){
8195             var worker = [];
8196             function rFlatten(a) {
8197                 Ext.each(a, function(v) {
8198                     if(Ext.isArray(v)){
8199                         rFlatten(v);
8200                     }else{
8201                         worker.push(v);
8202                     }
8203                 });
8204                 return worker;
8205             }
8206             return rFlatten(arr);
8207         },
8208
8209         /**
8210          * Returns the minimum value in the Array.
8211          * @param {Array|NodeList} arr The Array from which to select the minimum value.
8212          * @param {Function} comp (optional) a function to perform the comparision which determines minimization.
8213          *                   If omitted the "<" operator will be used. Note: gt = 1; eq = 0; lt = -1
8214          * @return {Object} The minimum value in the Array.
8215          */
8216         min : function(arr, comp){
8217             var ret = arr[0];
8218             comp = comp || function(a,b){ return a < b ? -1 : 1; };
8219             Ext.each(arr, function(v) {
8220                 ret = comp(ret, v) == -1 ? ret : v;
8221             });
8222             return ret;
8223         },
8224
8225         /**
8226          * Returns the maximum value in the Array
8227          * @param {Array|NodeList} arr The Array from which to select the maximum value.
8228          * @param {Function} comp (optional) a function to perform the comparision which determines maximization.
8229          *                   If omitted the ">" operator will be used. Note: gt = 1; eq = 0; lt = -1
8230          * @return {Object} The maximum value in the Array.
8231          */
8232         max : function(arr, comp){
8233             var ret = arr[0];
8234             comp = comp || function(a,b){ return a > b ? 1 : -1; };
8235             Ext.each(arr, function(v) {
8236                 ret = comp(ret, v) == 1 ? ret : v;
8237             });
8238             return ret;
8239         },
8240
8241         /**
8242          * Calculates the mean of the Array
8243          * @param {Array} arr The Array to calculate the mean value of.
8244          * @return {Number} The mean.
8245          */
8246         mean : function(arr){
8247            return arr.length > 0 ? Ext.sum(arr) / arr.length : undefined;
8248         },
8249
8250         /**
8251          * Calculates the sum of the Array
8252          * @param {Array} arr The Array to calculate the sum value of.
8253          * @return {Number} The sum.
8254          */
8255         sum : function(arr){
8256            var ret = 0;
8257            Ext.each(arr, function(v) {
8258                ret += v;
8259            });
8260            return ret;
8261         },
8262
8263         /**
8264          * Partitions the set into two sets: a true set and a false set.
8265          * Example:
8266          * Example2:
8267          * <pre><code>
8268 // Example 1:
8269 Ext.partition([true, false, true, true, false]); // [[true, true, true], [false, false]]
8270
8271 // Example 2:
8272 Ext.partition(
8273     Ext.query("p"),
8274     function(val){
8275         return val.className == "class1"
8276     }
8277 );
8278 // true are those paragraph elements with a className of "class1",
8279 // false set are those that do not have that className.
8280          * </code></pre>
8281          * @param {Array|NodeList} arr The array to partition
8282          * @param {Function} truth (optional) a function to determine truth.  If this is omitted the element
8283          *                   itself must be able to be evaluated for its truthfulness.
8284          * @return {Array} [true<Array>,false<Array>]
8285          */
8286         partition : function(arr, truth){
8287             var ret = [[],[]];
8288             Ext.each(arr, function(v, i, a) {
8289                 ret[ (truth && truth(v, i, a)) || (!truth && v) ? 0 : 1].push(v);
8290             });
8291             return ret;
8292         },
8293
8294         /**
8295          * Invokes a method on each item in an Array.
8296          * <pre><code>
8297 // Example:
8298 Ext.invoke(Ext.query("p"), "getAttribute", "id");
8299 // [el1.getAttribute("id"), el2.getAttribute("id"), ..., elN.getAttribute("id")]
8300          * </code></pre>
8301          * @param {Array|NodeList} arr The Array of items to invoke the method on.
8302          * @param {String} methodName The method name to invoke.
8303          * @param {...*} args Arguments to send into the method invocation.
8304          * @return {Array} The results of invoking the method on each item in the array.
8305          */
8306         invoke : function(arr, methodName){
8307             var ret = [],
8308                 args = Array.prototype.slice.call(arguments, 2);
8309             Ext.each(arr, function(v,i) {
8310                 if (v && typeof v[methodName] == 'function') {
8311                     ret.push(v[methodName].apply(v, args));
8312                 } else {
8313                     ret.push(undefined);
8314                 }
8315             });
8316             return ret;
8317         },
8318
8319         /**
8320          * Plucks the value of a property from each item in the Array
8321          * <pre><code>
8322 // Example:
8323 Ext.pluck(Ext.query("p"), "className"); // [el1.className, el2.className, ..., elN.className]
8324          * </code></pre>
8325          * @param {Array|NodeList} arr The Array of items to pluck the value from.
8326          * @param {String} prop The property name to pluck from each element.
8327          * @return {Array} The value from each item in the Array.
8328          */
8329         pluck : function(arr, prop){
8330             var ret = [];
8331             Ext.each(arr, function(v) {
8332                 ret.push( v[prop] );
8333             });
8334             return ret;
8335         },
8336
8337         /**
8338          * <p>Zips N sets together.</p>
8339          * <pre><code>
8340 // Example 1:
8341 Ext.zip([1,2,3],[4,5,6]); // [[1,4],[2,5],[3,6]]
8342 // Example 2:
8343 Ext.zip(
8344     [ "+", "-", "+"],
8345     [  12,  10,  22],
8346     [  43,  15,  96],
8347     function(a, b, c){
8348         return "$" + a + "" + b + "." + c
8349     }
8350 ); // ["$+12.43", "$-10.15", "$+22.96"]
8351          * </code></pre>
8352          * @param {Arrays|NodeLists} arr This argument may be repeated. Array(s) to contribute values.
8353          * @param {Function} zipper (optional) The last item in the argument list. This will drive how the items are zipped together.
8354          * @return {Array} The zipped set.
8355          */
8356         zip : function(){
8357             var parts = Ext.partition(arguments, function( val ){ return typeof val != 'function'; }),
8358                 arrs = parts[0],
8359                 fn = parts[1][0],
8360                 len = Ext.max(Ext.pluck(arrs, "length")),
8361                 ret = [];
8362
8363             for (var i = 0; i < len; i++) {
8364                 ret[i] = [];
8365                 if(fn){
8366                     ret[i] = fn.apply(fn, Ext.pluck(arrs, i));
8367                 }else{
8368                     for (var j = 0, aLen = arrs.length; j < aLen; j++){
8369                         ret[i].push( arrs[j][i] );
8370                     }
8371                 }
8372             }
8373             return ret;
8374         },
8375
8376         /**
8377          * This is shorthand reference to {@link Ext.ComponentMgr#get}.
8378          * Looks up an existing {@link Ext.Component Component} by {@link Ext.Component#id id}
8379          * @param {String} id The component {@link Ext.Component#id id}
8380          * @return Ext.Component The Component, <tt>undefined</tt> if not found, or <tt>null</tt> if a
8381          * Class was found.
8382         */
8383         getCmp : function(id){
8384             return Ext.ComponentMgr.get(id);
8385         },
8386
8387         /**
8388          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
8389          * you may want to set this to true.
8390          * @type Boolean
8391          */
8392         useShims: E.isIE6 || (E.isMac && E.isGecko2),
8393
8394         // inpired by a similar function in mootools library
8395         /**
8396          * Returns the type of object that is passed in. If the object passed in is null or undefined it
8397          * return false otherwise it returns one of the following values:<div class="mdetail-params"><ul>
8398          * <li><b>string</b>: If the object passed is a string</li>
8399          * <li><b>number</b>: If the object passed is a number</li>
8400          * <li><b>boolean</b>: If the object passed is a boolean value</li>
8401          * <li><b>date</b>: If the object passed is a Date object</li>
8402          * <li><b>function</b>: If the object passed is a function reference</li>
8403          * <li><b>object</b>: If the object passed is an object</li>
8404          * <li><b>array</b>: If the object passed is an array</li>
8405          * <li><b>regexp</b>: If the object passed is a regular expression</li>
8406          * <li><b>element</b>: If the object passed is a DOM Element</li>
8407          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
8408          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
8409          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
8410          * </ul></div>
8411          * @param {Mixed} object
8412          * @return {String}
8413          */
8414         type : function(o){
8415             if(o === undefined || o === null){
8416                 return false;
8417             }
8418             if(o.htmlElement){
8419                 return 'element';
8420             }
8421             var t = typeof o;
8422             if(t == 'object' && o.nodeName) {
8423                 switch(o.nodeType) {
8424                     case 1: return 'element';
8425                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
8426                 }
8427             }
8428             if(t == 'object' || t == 'function') {
8429                 switch(o.constructor) {
8430                     case Array: return 'array';
8431                     case RegExp: return 'regexp';
8432                     case Date: return 'date';
8433                 }
8434                 if(typeof o.length == 'number' && typeof o.item == 'function') {
8435                     return 'nodelist';
8436                 }
8437             }
8438             return t;
8439         },
8440
8441         intercept : function(o, name, fn, scope){
8442             o[name] = o[name].createInterceptor(fn, scope);
8443         },
8444
8445         // internal
8446         callback : function(cb, scope, args, delay){
8447             if(typeof cb == 'function'){
8448                 if(delay){
8449                     cb.defer(delay, scope, args || []);
8450                 }else{
8451                     cb.apply(scope, args || []);
8452                 }
8453             }
8454         }
8455     };
8456 }());
8457
8458 /**
8459  * @class Function
8460  * These functions are available on every Function object (any JavaScript function).
8461  */
8462 Ext.apply(Function.prototype, {
8463     /**
8464      * Create a combined function call sequence of the original function + the passed function.
8465      * The resulting function returns the results of the original function.
8466      * The passed fcn is called with the parameters of the original function. Example usage:
8467      * <pre><code>
8468 var sayHi = function(name){
8469     alert('Hi, ' + name);
8470 }
8471
8472 sayHi('Fred'); // alerts "Hi, Fred"
8473
8474 var sayGoodbye = sayHi.createSequence(function(name){
8475     alert('Bye, ' + name);
8476 });
8477
8478 sayGoodbye('Fred'); // both alerts show
8479 </code></pre>
8480      * @param {Function} fcn The function to sequence
8481      * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the passed function is executed.
8482      * <b>If omitted, defaults to the scope in which the original function is called or the browser window.</b>
8483      * @return {Function} The new function
8484      */
8485     createSequence : function(fcn, scope){
8486         var method = this;
8487         return (typeof fcn != 'function') ?
8488                 this :
8489                 function(){
8490                     var retval = method.apply(this || window, arguments);
8491                     fcn.apply(scope || this || window, arguments);
8492                     return retval;
8493                 };
8494     }
8495 });
8496
8497
8498 /**
8499  * @class String
8500  * These functions are available as static methods on the JavaScript String object.
8501  */
8502 Ext.applyIf(String, {
8503
8504     /**
8505      * Escapes the passed string for ' and \
8506      * @param {String} string The string to escape
8507      * @return {String} The escaped string
8508      * @static
8509      */
8510     escape : function(string) {
8511         return string.replace(/('|\\)/g, "\\$1");
8512     },
8513
8514     /**
8515      * Pads the left side of a string with a specified character.  This is especially useful
8516      * for normalizing number and date strings.  Example usage:
8517      * <pre><code>
8518 var s = String.leftPad('123', 5, '0');
8519 // s now contains the string: '00123'
8520      * </code></pre>
8521      * @param {String} string The original string
8522      * @param {Number} size The total length of the output string
8523      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
8524      * @return {String} The padded string
8525      * @static
8526      */
8527     leftPad : function (val, size, ch) {
8528         var result = String(val);
8529         if(!ch) {
8530             ch = " ";
8531         }
8532         while (result.length < size) {
8533             result = ch + result;
8534         }
8535         return result;
8536     }
8537 });
8538
8539 /**
8540  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
8541  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
8542  * they are already different, the first value passed in is returned.  Note that this method returns the new value
8543  * but does not change the current string.
8544  * <pre><code>
8545 // alternate sort directions
8546 sort = sort.toggle('ASC', 'DESC');
8547
8548 // instead of conditional logic:
8549 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
8550 </code></pre>
8551  * @param {String} value The value to compare to the current string
8552  * @param {String} other The new value to use if the string already equals the first value passed in
8553  * @return {String} The new value
8554  */
8555 String.prototype.toggle = function(value, other){
8556     return this == value ? other : value;
8557 };
8558
8559 /**
8560  * Trims whitespace from either end of a string, leaving spaces within the string intact.  Example:
8561  * <pre><code>
8562 var s = '  foo bar  ';
8563 alert('-' + s + '-');         //alerts "- foo bar -"
8564 alert('-' + s.trim() + '-');  //alerts "-foo bar-"
8565 </code></pre>
8566  * @return {String} The trimmed string
8567  */
8568 String.prototype.trim = function(){
8569     var re = /^\s+|\s+$/g;
8570     return function(){ return this.replace(re, ""); };
8571 }();
8572
8573 // here to prevent dependency on Date.js
8574 /**
8575  Returns the number of milliseconds between this date and date
8576  @param {Date} date (optional) Defaults to now
8577  @return {Number} The diff in milliseconds
8578  @member Date getElapsed
8579  */
8580 Date.prototype.getElapsed = function(date) {
8581     return Math.abs((date || new Date()).getTime()-this.getTime());
8582 };
8583
8584
8585 /**
8586  * @class Number
8587  */
8588 Ext.applyIf(Number.prototype, {
8589     /**
8590      * Checks whether or not the current number is within a desired range.  If the number is already within the
8591      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
8592      * exceeded.  Note that this method returns the constrained value but does not change the current number.
8593      * @param {Number} min The minimum number in the range
8594      * @param {Number} max The maximum number in the range
8595      * @return {Number} The constrained value if outside the range, otherwise the current value
8596      */
8597     constrain : function(min, max){
8598         return Math.min(Math.max(this, min), max);
8599     }
8600 });
8601 Ext.lib.Dom.getRegion = function(el) {
8602     return Ext.lib.Region.getRegion(el);
8603 };      Ext.lib.Region = function(t, r, b, l) {
8604                 var me = this;
8605         me.top = t;
8606         me[1] = t;
8607         me.right = r;
8608         me.bottom = b;
8609         me.left = l;
8610         me[0] = l;
8611     };
8612
8613     Ext.lib.Region.prototype = {
8614         contains : function(region) {
8615                 var me = this;
8616             return ( region.left >= me.left &&
8617                      region.right <= me.right &&
8618                      region.top >= me.top &&
8619                      region.bottom <= me.bottom );
8620
8621         },
8622
8623         getArea : function() {
8624                 var me = this;
8625             return ( (me.bottom - me.top) * (me.right - me.left) );
8626         },
8627
8628         intersect : function(region) {
8629             var me = this,
8630                 t = Math.max(me.top, region.top),
8631                 r = Math.min(me.right, region.right),
8632                 b = Math.min(me.bottom, region.bottom),
8633                 l = Math.max(me.left, region.left);
8634
8635             if (b >= t && r >= l) {
8636                 return new Ext.lib.Region(t, r, b, l);
8637             }
8638         },
8639         
8640         union : function(region) {
8641                 var me = this,
8642                 t = Math.min(me.top, region.top),
8643                 r = Math.max(me.right, region.right),
8644                 b = Math.max(me.bottom, region.bottom),
8645                 l = Math.min(me.left, region.left);
8646
8647             return new Ext.lib.Region(t, r, b, l);
8648         },
8649
8650         constrainTo : function(r) {
8651                 var me = this;
8652             me.top = me.top.constrain(r.top, r.bottom);
8653             me.bottom = me.bottom.constrain(r.top, r.bottom);
8654             me.left = me.left.constrain(r.left, r.right);
8655             me.right = me.right.constrain(r.left, r.right);
8656             return me;
8657         },
8658
8659         adjust : function(t, l, b, r) {
8660                 var me = this;
8661             me.top += t;
8662             me.left += l;
8663             me.right += r;
8664             me.bottom += b;
8665             return me;
8666         }
8667     };
8668
8669     Ext.lib.Region.getRegion = function(el) {
8670         var p = Ext.lib.Dom.getXY(el),
8671                 t = p[1],
8672                 r = p[0] + el.offsetWidth,
8673                 b = p[1] + el.offsetHeight,
8674                 l = p[0];
8675
8676         return new Ext.lib.Region(t, r, b, l);
8677     };  Ext.lib.Point = function(x, y) {
8678         if (Ext.isArray(x)) {
8679             y = x[1];
8680             x = x[0];
8681         }
8682         var me = this;
8683         me.x = me.right = me.left = me[0] = x;
8684         me.y = me.top = me.bottom = me[1] = y;
8685     };
8686
8687     Ext.lib.Point.prototype = new Ext.lib.Region();
8688 /**
8689  * @class Ext.DomHelper
8690  */
8691 Ext.apply(Ext.DomHelper,
8692 function(){
8693     var pub,
8694         afterbegin = 'afterbegin',
8695         afterend = 'afterend',
8696         beforebegin = 'beforebegin',
8697         beforeend = 'beforeend',
8698         confRe = /tag|children|cn|html$/i;
8699
8700     // private
8701     function doInsert(el, o, returnElement, pos, sibling, append){
8702         el = Ext.getDom(el);
8703         var newNode;
8704         if (pub.useDom) {
8705             newNode = createDom(o, null);
8706             if (append) {
8707                 el.appendChild(newNode);
8708             } else {
8709                 (sibling == 'firstChild' ? el : el.parentNode).insertBefore(newNode, el[sibling] || el);
8710             }
8711         } else {
8712             newNode = Ext.DomHelper.insertHtml(pos, el, Ext.DomHelper.createHtml(o));
8713         }
8714         return returnElement ? Ext.get(newNode, true) : newNode;
8715     }
8716
8717     // build as dom
8718     /** @ignore */
8719     function createDom(o, parentNode){
8720         var el,
8721             doc = document,
8722             useSet,
8723             attr,
8724             val,
8725             cn;
8726
8727         if (Ext.isArray(o)) {                       // Allow Arrays of siblings to be inserted
8728             el = doc.createDocumentFragment(); // in one shot using a DocumentFragment
8729             for (var i = 0, l = o.length; i < l; i++) {
8730                 createDom(o[i], el);
8731             }
8732         } else if (typeof o == 'string') {         // Allow a string as a child spec.
8733             el = doc.createTextNode(o);
8734         } else {
8735             el = doc.createElement( o.tag || 'div' );
8736             useSet = !!el.setAttribute; // In IE some elements don't have setAttribute
8737             for (var attr in o) {
8738                 if(!confRe.test(attr)){
8739                     val = o[attr];
8740                     if(attr == 'cls'){
8741                         el.className = val;
8742                     }else{
8743                         if(useSet){
8744                             el.setAttribute(attr, val);
8745                         }else{
8746                             el[attr] = val;
8747                         }
8748                     }
8749                 }
8750             }
8751             Ext.DomHelper.applyStyles(el, o.style);
8752
8753             if ((cn = o.children || o.cn)) {
8754                 createDom(cn, el);
8755             } else if (o.html) {
8756                 el.innerHTML = o.html;
8757             }
8758         }
8759         if(parentNode){
8760            parentNode.appendChild(el);
8761         }
8762         return el;
8763     }
8764
8765     pub = {
8766         /**
8767          * Creates a new Ext.Template from the DOM object spec.
8768          * @param {Object} o The DOM object spec (and children)
8769          * @return {Ext.Template} The new template
8770          */
8771         createTemplate : function(o){
8772             var html = Ext.DomHelper.createHtml(o);
8773             return new Ext.Template(html);
8774         },
8775
8776         /** True to force the use of DOM instead of html fragments @type Boolean */
8777         useDom : false,
8778
8779         /**
8780          * Creates new DOM element(s) and inserts them before el.
8781          * @param {Mixed} el The context element
8782          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
8783          * @param {Boolean} returnElement (optional) true to return a Ext.Element
8784          * @return {HTMLElement/Ext.Element} The new node
8785          * @hide (repeat)
8786          */
8787         insertBefore : function(el, o, returnElement){
8788             return doInsert(el, o, returnElement, beforebegin);
8789         },
8790
8791         /**
8792          * Creates new DOM element(s) and inserts them after el.
8793          * @param {Mixed} el The context element
8794          * @param {Object} o The DOM object spec (and children)
8795          * @param {Boolean} returnElement (optional) true to return a Ext.Element
8796          * @return {HTMLElement/Ext.Element} The new node
8797          * @hide (repeat)
8798          */
8799         insertAfter : function(el, o, returnElement){
8800             return doInsert(el, o, returnElement, afterend, 'nextSibling');
8801         },
8802
8803         /**
8804          * Creates new DOM element(s) and inserts them as the first child of el.
8805          * @param {Mixed} el The context element
8806          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
8807          * @param {Boolean} returnElement (optional) true to return a Ext.Element
8808          * @return {HTMLElement/Ext.Element} The new node
8809          * @hide (repeat)
8810          */
8811         insertFirst : function(el, o, returnElement){
8812             return doInsert(el, o, returnElement, afterbegin, 'firstChild');
8813         },
8814
8815         /**
8816          * Creates new DOM element(s) and appends them to el.
8817          * @param {Mixed} el The context element
8818          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
8819          * @param {Boolean} returnElement (optional) true to return a Ext.Element
8820          * @return {HTMLElement/Ext.Element} The new node
8821          * @hide (repeat)
8822          */
8823         append: function(el, o, returnElement){
8824             return doInsert(el, o, returnElement, beforeend, '', true);
8825         },
8826
8827         /**
8828          * Creates new DOM element(s) without inserting them to the document.
8829          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
8830          * @return {HTMLElement} The new uninserted node
8831          */
8832         createDom: createDom
8833     };
8834     return pub;
8835 }());
8836 /**
8837  * @class Ext.Template
8838  */
8839 Ext.apply(Ext.Template.prototype, {
8840     /**
8841      * @cfg {Boolean} disableFormats Specify <tt>true</tt> to disable format
8842      * functions in the template. If the template does not contain
8843      * {@link Ext.util.Format format functions}, setting <code>disableFormats</code>
8844      * to true will reduce <code>{@link #apply}</code> time. Defaults to <tt>false</tt>.
8845      * <pre><code>
8846 var t = new Ext.Template(
8847     '&lt;div name="{id}"&gt;',
8848         '&lt;span class="{cls}"&gt;{name} {value}&lt;/span&gt;',
8849     '&lt;/div&gt;',
8850     {
8851         compiled: true,      // {@link #compile} immediately
8852         disableFormats: true // reduce <code>{@link #apply}</code> time since no formatting
8853     }
8854 );
8855      * </code></pre>
8856      * For a list of available format functions, see {@link Ext.util.Format}.
8857      */
8858     disableFormats : false,
8859     /**
8860      * See <code>{@link #disableFormats}</code>.
8861      * @type Boolean
8862      * @property disableFormats
8863      */
8864
8865     /**
8866      * The regular expression used to match template variables
8867      * @type RegExp
8868      * @property
8869      * @hide repeat doc
8870      */
8871     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
8872     argsRe : /^\s*['"](.*)["']\s*$/,
8873     compileARe : /\\/g,
8874     compileBRe : /(\r\n|\n)/g,
8875     compileCRe : /'/g,
8876
8877     /**
8878      * Returns an HTML fragment of this template with the specified values applied.
8879      * @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'})
8880      * @return {String} The HTML fragment
8881      * @hide repeat doc
8882      */
8883     applyTemplate : function(values){
8884         var me = this,
8885             useF = me.disableFormats !== true,
8886             fm = Ext.util.Format,
8887             tpl = me;
8888
8889         if(me.compiled){
8890             return me.compiled(values);
8891         }
8892         function fn(m, name, format, args){
8893             if (format && useF) {
8894                 if (format.substr(0, 5) == "this.") {
8895                     return tpl.call(format.substr(5), values[name], values);
8896                 } else {
8897                     if (args) {
8898                         // quoted values are required for strings in compiled templates,
8899                         // but for non compiled we need to strip them
8900                         // quoted reversed for jsmin
8901                         var re = me.argsRe;
8902                         args = args.split(',');
8903                         for(var i = 0, len = args.length; i < len; i++){
8904                             args[i] = args[i].replace(re, "$1");
8905                         }
8906                         args = [values[name]].concat(args);
8907                     } else {
8908                         args = [values[name]];
8909                     }
8910                     return fm[format].apply(fm, args);
8911                 }
8912             } else {
8913                 return values[name] !== undefined ? values[name] : "";
8914             }
8915         }
8916         return me.html.replace(me.re, fn);
8917     },
8918
8919     /**
8920      * Compiles the template into an internal function, eliminating the RegEx overhead.
8921      * @return {Ext.Template} this
8922      * @hide repeat doc
8923      */
8924     compile : function(){
8925         var me = this,
8926             fm = Ext.util.Format,
8927             useF = me.disableFormats !== true,
8928             sep = Ext.isGecko ? "+" : ",",
8929             body;
8930
8931         function fn(m, name, format, args){
8932             if(format && useF){
8933                 args = args ? ',' + args : "";
8934                 if(format.substr(0, 5) != "this."){
8935                     format = "fm." + format + '(';
8936                 }else{
8937                     format = 'this.call("'+ format.substr(5) + '", ';
8938                     args = ", values";
8939                 }
8940             }else{
8941                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
8942             }
8943             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
8944         }
8945
8946         // branched to use + in gecko and [].join() in others
8947         if(Ext.isGecko){
8948             body = "this.compiled = function(values){ return '" +
8949                    me.html.replace(me.compileARe, '\\\\').replace(me.compileBRe, '\\n').replace(me.compileCRe, "\\'").replace(me.re, fn) +
8950                     "';};";
8951         }else{
8952             body = ["this.compiled = function(values){ return ['"];
8953             body.push(me.html.replace(me.compileARe, '\\\\').replace(me.compileBRe, '\\n').replace(me.compileCRe, "\\'").replace(me.re, fn));
8954             body.push("'].join('');};");
8955             body = body.join('');
8956         }
8957         eval(body);
8958         return me;
8959     },
8960
8961     // private function used to call members
8962     call : function(fnName, value, allValues){
8963         return this[fnName](value, allValues);
8964     }
8965 });
8966 Ext.Template.prototype.apply = Ext.Template.prototype.applyTemplate;
8967 /**
8968  * @class Ext.util.Functions
8969  * @singleton
8970  */
8971 Ext.util.Functions = {
8972     /**
8973      * Creates an interceptor function. The passed function is called before the original one. If it returns false,
8974      * the original one is not called. The resulting function returns the results of the original function.
8975      * The passed function is called with the parameters of the original function. Example usage:
8976      * <pre><code>
8977 var sayHi = function(name){
8978     alert('Hi, ' + name);
8979 }
8980
8981 sayHi('Fred'); // alerts "Hi, Fred"
8982
8983 // create a new function that validates input without
8984 // directly modifying the original function:
8985 var sayHiToFriend = Ext.createInterceptor(sayHi, function(name){
8986     return name == 'Brian';
8987 });
8988
8989 sayHiToFriend('Fred');  // no alert
8990 sayHiToFriend('Brian'); // alerts "Hi, Brian"
8991        </code></pre>
8992      * @param {Function} origFn The original function.
8993      * @param {Function} newFn The function to call before the original
8994      * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the passed function is executed.
8995      * <b>If omitted, defaults to the scope in which the original function is called or the browser window.</b>
8996      * @return {Function} The new function
8997      */
8998     createInterceptor: function(origFn, newFn, scope) { 
8999         var method = origFn;
9000         if (!Ext.isFunction(newFn)) {
9001             return origFn;
9002         }
9003         else {
9004             return function() {
9005                 var me = this,
9006                     args = arguments;
9007                 newFn.target = me;
9008                 newFn.method = origFn;
9009                 return (newFn.apply(scope || me || window, args) !== false) ?
9010                         origFn.apply(me || window, args) :
9011                         null;
9012             };
9013         }
9014     },
9015
9016     /**
9017      * Creates a delegate (callback) that sets the scope to obj.
9018      * Call directly on any function. Example: <code>Ext.createDelegate(this.myFunction, this, [arg1, arg2])</code>
9019      * Will create a function that is automatically scoped to obj so that the <tt>this</tt> variable inside the
9020      * callback points to obj. Example usage:
9021      * <pre><code>
9022 var sayHi = function(name){
9023     // Note this use of "this.text" here.  This function expects to
9024     // execute within a scope that contains a text property.  In this
9025     // example, the "this" variable is pointing to the btn object that
9026     // was passed in createDelegate below.
9027     alert('Hi, ' + name + '. You clicked the "' + this.text + '" button.');
9028 }
9029
9030 var btn = new Ext.Button({
9031     text: 'Say Hi',
9032     renderTo: Ext.getBody()
9033 });
9034
9035 // This callback will execute in the scope of the
9036 // button instance. Clicking the button alerts
9037 // "Hi, Fred. You clicked the "Say Hi" button."
9038 btn.on('click', Ext.createDelegate(sayHi, btn, ['Fred']));
9039        </code></pre>
9040      * @param {Function} fn The function to delegate.
9041      * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the function is executed.
9042      * <b>If omitted, defaults to the browser window.</b>
9043      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
9044      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
9045      * if a number the args are inserted at the specified position
9046      * @return {Function} The new function
9047      */
9048     createDelegate: function(fn, obj, args, appendArgs) {
9049         if (!Ext.isFunction(fn)) {
9050             return fn;
9051         }
9052         return function() {
9053             var callArgs = args || arguments;
9054             if (appendArgs === true) {
9055                 callArgs = Array.prototype.slice.call(arguments, 0);
9056                 callArgs = callArgs.concat(args);
9057             }
9058             else if (Ext.isNumber(appendArgs)) {
9059                 callArgs = Array.prototype.slice.call(arguments, 0);
9060                 // copy arguments first
9061                 var applyArgs = [appendArgs, 0].concat(args);
9062                 // create method call params
9063                 Array.prototype.splice.apply(callArgs, applyArgs);
9064                 // splice them in
9065             }
9066             return fn.apply(obj || window, callArgs);
9067         };
9068     },
9069
9070     /**
9071      * Calls this function after the number of millseconds specified, optionally in a specific scope. Example usage:
9072      * <pre><code>
9073 var sayHi = function(name){
9074     alert('Hi, ' + name);
9075 }
9076
9077 // executes immediately:
9078 sayHi('Fred');
9079
9080 // executes after 2 seconds:
9081 Ext.defer(sayHi, 2000, this, ['Fred']);
9082
9083 // this syntax is sometimes useful for deferring
9084 // execution of an anonymous function:
9085 Ext.defer(function(){
9086     alert('Anonymous');
9087 }, 100);
9088        </code></pre>
9089      * @param {Function} fn The function to defer.
9090      * @param {Number} millis The number of milliseconds for the setTimeout call (if less than or equal to 0 the function is executed immediately)
9091      * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the function is executed.
9092      * <b>If omitted, defaults to the browser window.</b>
9093      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
9094      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
9095      * if a number the args are inserted at the specified position
9096      * @return {Number} The timeout id that can be used with clearTimeout
9097      */
9098     defer: function(fn, millis, obj, args, appendArgs) {
9099         fn = Ext.util.Functions.createDelegate(fn, obj, args, appendArgs);
9100         if (millis > 0) {
9101             return setTimeout(fn, millis);
9102         }
9103         fn();
9104         return 0;
9105     },
9106
9107
9108     /**
9109      * Create a combined function call sequence of the original function + the passed function.
9110      * The resulting function returns the results of the original function.
9111      * The passed fcn is called with the parameters of the original function. Example usage:
9112      * 
9113
9114 var sayHi = function(name){
9115     alert('Hi, ' + name);
9116 }
9117
9118 sayHi('Fred'); // alerts "Hi, Fred"
9119
9120 var sayGoodbye = Ext.createSequence(sayHi, function(name){
9121     alert('Bye, ' + name);
9122 });
9123
9124 sayGoodbye('Fred'); // both alerts show
9125
9126      * @param {Function} origFn The original function.
9127      * @param {Function} newFn The function to sequence
9128      * @param {Object} scope (optional) The scope (this reference) in which the passed function is executed.
9129      * If omitted, defaults to the scope in which the original function is called or the browser window.
9130      * @return {Function} The new function
9131      */
9132     createSequence: function(origFn, newFn, scope) {
9133         if (!Ext.isFunction(newFn)) {
9134             return origFn;
9135         }
9136         else {
9137             return function() {
9138                 var retval = origFn.apply(this || window, arguments);
9139                 newFn.apply(scope || this || window, arguments);
9140                 return retval;
9141             };
9142         }
9143     }
9144 };
9145
9146 /**
9147  * Shorthand for {@link Ext.util.Functions#defer}   
9148  * @param {Function} fn The function to defer.
9149  * @param {Number} millis The number of milliseconds for the setTimeout call (if less than or equal to 0 the function is executed immediately)
9150  * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the function is executed.
9151  * <b>If omitted, defaults to the browser window.</b>
9152  * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
9153  * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
9154  * if a number the args are inserted at the specified position
9155  * @return {Number} The timeout id that can be used with clearTimeout
9156  * @member Ext
9157  * @method defer
9158  */
9159
9160 Ext.defer = Ext.util.Functions.defer;
9161
9162 /**
9163  * Shorthand for {@link Ext.util.Functions#createInterceptor}   
9164  * @param {Function} origFn The original function.
9165  * @param {Function} newFn The function to call before the original
9166  * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the passed function is executed.
9167  * <b>If omitted, defaults to the scope in which the original function is called or the browser window.</b>
9168  * @return {Function} The new function
9169  * @member Ext
9170  * @method defer
9171  */
9172
9173 Ext.createInterceptor = Ext.util.Functions.createInterceptor;
9174
9175 /**
9176  * Shorthand for {@link Ext.util.Functions#createSequence}
9177  * @param {Function} origFn The original function.
9178  * @param {Function} newFn The function to sequence
9179  * @param {Object} scope (optional) The scope (this reference) in which the passed function is executed.
9180  * If omitted, defaults to the scope in which the original function is called or the browser window.
9181  * @return {Function} The new function
9182  * @member Ext
9183  * @method defer
9184  */
9185
9186 Ext.createSequence = Ext.util.Functions.createSequence;
9187
9188 /**
9189  * Shorthand for {@link Ext.util.Functions#createDelegate}
9190  * @param {Function} fn The function to delegate.
9191  * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the function is executed.
9192  * <b>If omitted, defaults to the browser window.</b>
9193  * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
9194  * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
9195  * if a number the args are inserted at the specified position
9196  * @return {Function} The new function
9197  * @member Ext
9198  * @method defer
9199  */
9200 Ext.createDelegate = Ext.util.Functions.createDelegate;
9201 /**
9202  * @class Ext.util.Observable
9203  */
9204 Ext.apply(Ext.util.Observable.prototype, function(){
9205     // this is considered experimental (along with beforeMethod, afterMethod, removeMethodListener?)
9206     // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call
9207     // private
9208     function getMethodEvent(method){
9209         var e = (this.methodEvents = this.methodEvents ||
9210         {})[method], returnValue, v, cancel, obj = this;
9211
9212         if (!e) {
9213             this.methodEvents[method] = e = {};
9214             e.originalFn = this[method];
9215             e.methodName = method;
9216             e.before = [];
9217             e.after = [];
9218
9219             var makeCall = function(fn, scope, args){
9220                 if((v = fn.apply(scope || obj, args)) !== undefined){
9221                     if (typeof v == 'object') {
9222                         if(v.returnValue !== undefined){
9223                             returnValue = v.returnValue;
9224                         }else{
9225                             returnValue = v;
9226                         }
9227                         cancel = !!v.cancel;
9228                     }
9229                     else
9230                         if (v === false) {
9231                             cancel = true;
9232                         }
9233                         else {
9234                             returnValue = v;
9235                         }
9236                 }
9237             };
9238
9239             this[method] = function(){
9240                 var args = Array.prototype.slice.call(arguments, 0),
9241                     b;
9242                 returnValue = v = undefined;
9243                 cancel = false;
9244
9245                 for(var i = 0, len = e.before.length; i < len; i++){
9246                     b = e.before[i];
9247                     makeCall(b.fn, b.scope, args);
9248                     if (cancel) {
9249                         return returnValue;
9250                     }
9251                 }
9252
9253                 if((v = e.originalFn.apply(obj, args)) !== undefined){
9254                     returnValue = v;
9255                 }
9256
9257                 for(var i = 0, len = e.after.length; i < len; i++){
9258                     b = e.after[i];
9259                     makeCall(b.fn, b.scope, args);
9260                     if (cancel) {
9261                         return returnValue;
9262                     }
9263                 }
9264                 return returnValue;
9265             };
9266         }
9267         return e;
9268     }
9269
9270     return {
9271         // these are considered experimental
9272         // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call
9273         // adds an 'interceptor' called before the original method
9274         beforeMethod : function(method, fn, scope){
9275             getMethodEvent.call(this, method).before.push({
9276                 fn: fn,
9277                 scope: scope
9278             });
9279         },
9280
9281         // adds a 'sequence' called after the original method
9282         afterMethod : function(method, fn, scope){
9283             getMethodEvent.call(this, method).after.push({
9284                 fn: fn,
9285                 scope: scope
9286             });
9287         },
9288
9289         removeMethodListener: function(method, fn, scope){
9290             var e = this.getMethodEvent(method);
9291             for(var i = 0, len = e.before.length; i < len; i++){
9292                 if(e.before[i].fn == fn && e.before[i].scope == scope){
9293                     e.before.splice(i, 1);
9294                     return;
9295                 }
9296             }
9297             for(var i = 0, len = e.after.length; i < len; i++){
9298                 if(e.after[i].fn == fn && e.after[i].scope == scope){
9299                     e.after.splice(i, 1);
9300                     return;
9301                 }
9302             }
9303         },
9304
9305         /**
9306          * Relays selected events from the specified Observable as if the events were fired by <tt><b>this</b></tt>.
9307          * @param {Object} o The Observable whose events this object is to relay.
9308          * @param {Array} events Array of event names to relay.
9309          */
9310         relayEvents : function(o, events){
9311             var me = this;
9312             function createHandler(ename){
9313                 return function(){
9314                     return me.fireEvent.apply(me, [ename].concat(Array.prototype.slice.call(arguments, 0)));
9315                 };
9316             }
9317             for(var i = 0, len = events.length; i < len; i++){
9318                 var ename = events[i];
9319                 me.events[ename] = me.events[ename] || true;
9320                 o.on(ename, createHandler(ename), me);
9321             }
9322         },
9323
9324         /**
9325          * <p>Enables events fired by this Observable to bubble up an owner hierarchy by calling
9326          * <code>this.getBubbleTarget()</code> if present. There is no implementation in the Observable base class.</p>
9327          * <p>This is commonly used by Ext.Components to bubble events to owner Containers. See {@link Ext.Component.getBubbleTarget}. The default
9328          * implementation in Ext.Component returns the Component's immediate owner. But if a known target is required, this can be overridden to
9329          * access the required target more quickly.</p>
9330          * <p>Example:</p><pre><code>
9331 Ext.override(Ext.form.Field, {
9332     //  Add functionality to Field&#39;s initComponent to enable the change event to bubble
9333     initComponent : Ext.form.Field.prototype.initComponent.createSequence(function() {
9334         this.enableBubble('change');
9335     }),
9336
9337     //  We know that we want Field&#39;s events to bubble directly to the FormPanel.
9338     getBubbleTarget : function() {
9339         if (!this.formPanel) {
9340             this.formPanel = this.findParentByType('form');
9341         }
9342         return this.formPanel;
9343     }
9344 });
9345
9346 var myForm = new Ext.formPanel({
9347     title: 'User Details',
9348     items: [{
9349         ...
9350     }],
9351     listeners: {
9352         change: function() {
9353             // Title goes red if form has been modified.
9354             myForm.header.setStyle('color', 'red');
9355         }
9356     }
9357 });
9358 </code></pre>
9359          * @param {String/Array} events The event name to bubble, or an Array of event names.
9360          */
9361         enableBubble : function(events){
9362             var me = this;
9363             if(!Ext.isEmpty(events)){
9364                 events = Ext.isArray(events) ? events : Array.prototype.slice.call(arguments, 0);
9365                 for(var i = 0, len = events.length; i < len; i++){
9366                     var ename = events[i];
9367                     ename = ename.toLowerCase();
9368                     var ce = me.events[ename] || true;
9369                     if (typeof ce == 'boolean') {
9370                         ce = new Ext.util.Event(me, ename);
9371                         me.events[ename] = ce;
9372                     }
9373                     ce.bubble = true;
9374                 }
9375             }
9376         }
9377     };
9378 }());
9379
9380
9381 /**
9382  * Starts capture on the specified Observable. All events will be passed
9383  * to the supplied function with the event name + standard signature of the event
9384  * <b>before</b> the event is fired. If the supplied function returns false,
9385  * the event will not fire.
9386  * @param {Observable} o The Observable to capture events from.
9387  * @param {Function} fn The function to call when an event is fired.
9388  * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the Observable firing the event.
9389  * @static
9390  */
9391 Ext.util.Observable.capture = function(o, fn, scope){
9392     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
9393 };
9394
9395
9396 /**
9397  * Sets observability on the passed class constructor.<p>
9398  * <p>This makes any event fired on any instance of the passed class also fire a single event through
9399  * the <i>class</i> allowing for central handling of events on many instances at once.</p>
9400  * <p>Usage:</p><pre><code>
9401 Ext.util.Observable.observeClass(Ext.data.Connection);
9402 Ext.data.Connection.on('beforerequest', function(con, options) {
9403     console.log('Ajax request made to ' + options.url);
9404 });</code></pre>
9405  * @param {Function} c The class constructor to make observable.
9406  * @param {Object} listeners An object containing a series of listeners to add. See {@link #addListener}.
9407  * @static
9408  */
9409 Ext.util.Observable.observeClass = function(c, listeners){
9410     if(c){
9411       if(!c.fireEvent){
9412           Ext.apply(c, new Ext.util.Observable());
9413           Ext.util.Observable.capture(c.prototype, c.fireEvent, c);
9414       }
9415       if(typeof listeners == 'object'){
9416           c.on(listeners);
9417       }
9418       return c;
9419    }
9420 };
9421 /**
9422 * @class Ext.EventManager
9423 */
9424 Ext.apply(Ext.EventManager, function(){
9425    var resizeEvent,
9426        resizeTask,
9427        textEvent,
9428        textSize,
9429        D = Ext.lib.Dom,
9430        propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/,
9431        curWidth = 0,
9432        curHeight = 0,
9433        // note 1: IE fires ONLY the keydown event on specialkey autorepeat
9434        // note 2: Safari < 3.1, Gecko (Mac/Linux) & Opera fire only the keypress event on specialkey autorepeat
9435        // (research done by @Jan Wolter at http://unixpapa.com/js/key.html)
9436        useKeydown = Ext.isWebKit ?
9437                    Ext.num(navigator.userAgent.match(/AppleWebKit\/(\d+)/)[1]) >= 525 :
9438                    !((Ext.isGecko && !Ext.isWindows) || Ext.isOpera);
9439
9440    return {
9441        // private
9442        doResizeEvent: function(){
9443            var h = D.getViewHeight(),
9444                w = D.getViewWidth();
9445
9446             //whacky problem in IE where the resize event will fire even though the w/h are the same.
9447             if(curHeight != h || curWidth != w){
9448                resizeEvent.fire(curWidth = w, curHeight = h);
9449             }
9450        },
9451
9452        /**
9453         * Adds a listener to be notified when the browser window is resized and provides resize event buffering (100 milliseconds),
9454         * passes new viewport width and height to handlers.
9455         * @param {Function} fn      The handler function the window resize event invokes.
9456         * @param {Object}   scope   The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
9457         * @param {boolean}  options Options object as passed to {@link Ext.Element#addListener}
9458         */
9459        onWindowResize : function(fn, scope, options){
9460            if(!resizeEvent){
9461                resizeEvent = new Ext.util.Event();
9462                resizeTask = new Ext.util.DelayedTask(this.doResizeEvent);
9463                Ext.EventManager.on(window, "resize", this.fireWindowResize, this);
9464            }
9465            resizeEvent.addListener(fn, scope, options);
9466        },
9467
9468        // exposed only to allow manual firing
9469        fireWindowResize : function(){
9470            if(resizeEvent){
9471                resizeTask.delay(100);
9472            }
9473        },
9474
9475        /**
9476         * 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.
9477         * @param {Function} fn      The function the event invokes.
9478         * @param {Object}   scope   The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
9479         * @param {boolean}  options Options object as passed to {@link Ext.Element#addListener}
9480         */
9481        onTextResize : function(fn, scope, options){
9482            if(!textEvent){
9483                textEvent = new Ext.util.Event();
9484                var textEl = new Ext.Element(document.createElement('div'));
9485                textEl.dom.className = 'x-text-resize';
9486                textEl.dom.innerHTML = 'X';
9487                textEl.appendTo(document.body);
9488                textSize = textEl.dom.offsetHeight;
9489                setInterval(function(){
9490                    if(textEl.dom.offsetHeight != textSize){
9491                        textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
9492                    }
9493                }, this.textResizeInterval);
9494            }
9495            textEvent.addListener(fn, scope, options);
9496        },
9497
9498        /**
9499         * Removes the passed window resize listener.
9500         * @param {Function} fn        The method the event invokes
9501         * @param {Object}   scope    The scope of handler
9502         */
9503        removeResizeListener : function(fn, scope){
9504            if(resizeEvent){
9505                resizeEvent.removeListener(fn, scope);
9506            }
9507        },
9508
9509        // private
9510        fireResize : function(){
9511            if(resizeEvent){
9512                resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
9513            }
9514        },
9515
9516         /**
9517         * The frequency, in milliseconds, to check for text resize events (defaults to 50)
9518         */
9519        textResizeInterval : 50,
9520
9521        /**
9522         * Url used for onDocumentReady with using SSL (defaults to Ext.SSL_SECURE_URL)
9523         */
9524        ieDeferSrc : false,
9525        
9526        // protected, short accessor for useKeydown
9527        getKeyEvent : function(){
9528            return useKeydown ? 'keydown' : 'keypress';
9529        },
9530
9531        // protected for use inside the framework
9532        // detects whether we should use keydown or keypress based on the browser.
9533        useKeydown: useKeydown
9534    };
9535 }());
9536
9537 Ext.EventManager.on = Ext.EventManager.addListener;
9538
9539
9540 Ext.apply(Ext.EventObjectImpl.prototype, {
9541    /** Key constant @type Number */
9542    BACKSPACE: 8,
9543    /** Key constant @type Number */
9544    TAB: 9,
9545    /** Key constant @type Number */
9546    NUM_CENTER: 12,
9547    /** Key constant @type Number */
9548    ENTER: 13,
9549    /** Key constant @type Number */
9550    RETURN: 13,
9551    /** Key constant @type Number */
9552    SHIFT: 16,
9553    /** Key constant @type Number */
9554    CTRL: 17,
9555    CONTROL : 17, // legacy
9556    /** Key constant @type Number */
9557    ALT: 18,
9558    /** Key constant @type Number */
9559    PAUSE: 19,
9560    /** Key constant @type Number */
9561    CAPS_LOCK: 20,
9562    /** Key constant @type Number */
9563    ESC: 27,
9564    /** Key constant @type Number */
9565    SPACE: 32,
9566    /** Key constant @type Number */
9567    PAGE_UP: 33,
9568    PAGEUP : 33, // legacy
9569    /** Key constant @type Number */
9570    PAGE_DOWN: 34,
9571    PAGEDOWN : 34, // legacy
9572    /** Key constant @type Number */
9573    END: 35,
9574    /** Key constant @type Number */
9575    HOME: 36,
9576    /** Key constant @type Number */
9577    LEFT: 37,
9578    /** Key constant @type Number */
9579    UP: 38,
9580    /** Key constant @type Number */
9581    RIGHT: 39,
9582    /** Key constant @type Number */
9583    DOWN: 40,
9584    /** Key constant @type Number */
9585    PRINT_SCREEN: 44,
9586    /** Key constant @type Number */
9587    INSERT: 45,
9588    /** Key constant @type Number */
9589    DELETE: 46,
9590    /** Key constant @type Number */
9591    ZERO: 48,
9592    /** Key constant @type Number */
9593    ONE: 49,
9594    /** Key constant @type Number */
9595    TWO: 50,
9596    /** Key constant @type Number */
9597    THREE: 51,
9598    /** Key constant @type Number */
9599    FOUR: 52,
9600    /** Key constant @type Number */
9601    FIVE: 53,
9602    /** Key constant @type Number */
9603    SIX: 54,
9604    /** Key constant @type Number */
9605    SEVEN: 55,
9606    /** Key constant @type Number */
9607    EIGHT: 56,
9608    /** Key constant @type Number */
9609    NINE: 57,
9610    /** Key constant @type Number */
9611    A: 65,
9612    /** Key constant @type Number */
9613    B: 66,
9614    /** Key constant @type Number */
9615    C: 67,
9616    /** Key constant @type Number */
9617    D: 68,
9618    /** Key constant @type Number */
9619    E: 69,
9620    /** Key constant @type Number */
9621    F: 70,
9622    /** Key constant @type Number */
9623    G: 71,
9624    /** Key constant @type Number */
9625    H: 72,
9626    /** Key constant @type Number */
9627    I: 73,
9628    /** Key constant @type Number */
9629    J: 74,
9630    /** Key constant @type Number */
9631    K: 75,
9632    /** Key constant @type Number */
9633    L: 76,
9634    /** Key constant @type Number */
9635    M: 77,
9636    /** Key constant @type Number */
9637    N: 78,
9638    /** Key constant @type Number */
9639    O: 79,
9640    /** Key constant @type Number */
9641    P: 80,
9642    /** Key constant @type Number */
9643    Q: 81,
9644    /** Key constant @type Number */
9645    R: 82,
9646    /** Key constant @type Number */
9647    S: 83,
9648    /** Key constant @type Number */
9649    T: 84,
9650    /** Key constant @type Number */
9651    U: 85,
9652    /** Key constant @type Number */
9653    V: 86,
9654    /** Key constant @type Number */
9655    W: 87,
9656    /** Key constant @type Number */
9657    X: 88,
9658    /** Key constant @type Number */
9659    Y: 89,
9660    /** Key constant @type Number */
9661    Z: 90,
9662    /** Key constant @type Number */
9663    CONTEXT_MENU: 93,
9664    /** Key constant @type Number */
9665    NUM_ZERO: 96,
9666    /** Key constant @type Number */
9667    NUM_ONE: 97,
9668    /** Key constant @type Number */
9669    NUM_TWO: 98,
9670    /** Key constant @type Number */
9671    NUM_THREE: 99,
9672    /** Key constant @type Number */
9673    NUM_FOUR: 100,
9674    /** Key constant @type Number */
9675    NUM_FIVE: 101,
9676    /** Key constant @type Number */
9677    NUM_SIX: 102,
9678    /** Key constant @type Number */
9679    NUM_SEVEN: 103,
9680    /** Key constant @type Number */
9681    NUM_EIGHT: 104,
9682    /** Key constant @type Number */
9683    NUM_NINE: 105,
9684    /** Key constant @type Number */
9685    NUM_MULTIPLY: 106,
9686    /** Key constant @type Number */
9687    NUM_PLUS: 107,
9688    /** Key constant @type Number */
9689    NUM_MINUS: 109,
9690    /** Key constant @type Number */
9691    NUM_PERIOD: 110,
9692    /** Key constant @type Number */
9693    NUM_DIVISION: 111,
9694    /** Key constant @type Number */
9695    F1: 112,
9696    /** Key constant @type Number */
9697    F2: 113,
9698    /** Key constant @type Number */
9699    F3: 114,
9700    /** Key constant @type Number */
9701    F4: 115,
9702    /** Key constant @type Number */
9703    F5: 116,
9704    /** Key constant @type Number */
9705    F6: 117,
9706    /** Key constant @type Number */
9707    F7: 118,
9708    /** Key constant @type Number */
9709    F8: 119,
9710    /** Key constant @type Number */
9711    F9: 120,
9712    /** Key constant @type Number */
9713    F10: 121,
9714    /** Key constant @type Number */
9715    F11: 122,
9716    /** Key constant @type Number */
9717    F12: 123,
9718
9719    /** @private */
9720    isNavKeyPress : function(){
9721        var me = this,
9722            k = this.normalizeKey(me.keyCode);
9723        return (k >= 33 && k <= 40) ||  // Page Up/Down, End, Home, Left, Up, Right, Down
9724        k == me.RETURN ||
9725        k == me.TAB ||
9726        k == me.ESC;
9727    },
9728
9729    isSpecialKey : function(){
9730        var k = this.normalizeKey(this.keyCode);
9731        return (this.type == 'keypress' && this.ctrlKey) ||
9732        this.isNavKeyPress() ||
9733        (k == this.BACKSPACE) || // Backspace
9734        (k >= 16 && k <= 20) || // Shift, Ctrl, Alt, Pause, Caps Lock
9735        (k >= 44 && k <= 46);   // Print Screen, Insert, Delete
9736    },
9737
9738    getPoint : function(){
9739        return new Ext.lib.Point(this.xy[0], this.xy[1]);
9740    },
9741
9742    /**
9743     * Returns true if the control, meta, shift or alt key was pressed during this event.
9744     * @return {Boolean}
9745     */
9746    hasModifier : function(){
9747        return ((this.ctrlKey || this.altKey) || this.shiftKey);
9748    }
9749 });/**
9750  * @class Ext.Element
9751  */
9752 Ext.Element.addMethods({
9753     /**
9754      * Stops the specified event(s) from bubbling and optionally prevents the default action
9755      * @param {String/Array} eventName an event / array of events to stop from bubbling
9756      * @param {Boolean} preventDefault (optional) true to prevent the default action too
9757      * @return {Ext.Element} this
9758      */
9759     swallowEvent : function(eventName, preventDefault) {
9760         var me = this;
9761         function fn(e) {
9762             e.stopPropagation();
9763             if (preventDefault) {
9764                 e.preventDefault();
9765             }
9766         }
9767         
9768         if (Ext.isArray(eventName)) {
9769             Ext.each(eventName, function(e) {
9770                  me.on(e, fn);
9771             });
9772             return me;
9773         }
9774         me.on(eventName, fn);
9775         return me;
9776     },
9777
9778     /**
9779      * Create an event handler on this element such that when the event fires and is handled by this element,
9780      * it will be relayed to another object (i.e., fired again as if it originated from that object instead).
9781      * @param {String} eventName The type of event to relay
9782      * @param {Object} object Any object that extends {@link Ext.util.Observable} that will provide the context
9783      * for firing the relayed event
9784      */
9785     relayEvent : function(eventName, observable) {
9786         this.on(eventName, function(e) {
9787             observable.fireEvent(eventName, e);
9788         });
9789     },
9790
9791     /**
9792      * Removes worthless text nodes
9793      * @param {Boolean} forceReclean (optional) By default the element
9794      * keeps track if it has been cleaned already so
9795      * you can call this over and over. However, if you update the element and
9796      * need to force a reclean, you can pass true.
9797      */
9798     clean : function(forceReclean) {
9799         var me  = this,
9800             dom = me.dom,
9801             n   = dom.firstChild,
9802             ni  = -1;
9803
9804         if (Ext.Element.data(dom, 'isCleaned') && forceReclean !== true) {
9805             return me;
9806         }
9807
9808         while (n) {
9809             var nx = n.nextSibling;
9810             if (n.nodeType == 3 && !(/\S/.test(n.nodeValue))) {
9811                 dom.removeChild(n);
9812             } else {
9813                 n.nodeIndex = ++ni;
9814             }
9815             n = nx;
9816         }
9817         
9818         Ext.Element.data(dom, 'isCleaned', true);
9819         return me;
9820     },
9821
9822     /**
9823      * Direct access to the Updater {@link Ext.Updater#update} method. The method takes the same object
9824      * parameter as {@link Ext.Updater#update}
9825      * @return {Ext.Element} this
9826      */
9827     load : function() {
9828         var updateManager = this.getUpdater();
9829         updateManager.update.apply(updateManager, arguments);
9830         
9831         return this;
9832     },
9833
9834     /**
9835     * Gets this element's {@link Ext.Updater Updater}
9836     * @return {Ext.Updater} The Updater
9837     */
9838     getUpdater : function() {
9839         return this.updateManager || (this.updateManager = new Ext.Updater(this));
9840     },
9841
9842     /**
9843     * Update the innerHTML of this element, optionally searching for and processing scripts
9844     * @param {String} html The new HTML
9845     * @param {Boolean} loadScripts (optional) True to look for and process scripts (defaults to false)
9846     * @param {Function} callback (optional) For async script loading you can be notified when the update completes
9847     * @return {Ext.Element} this
9848      */
9849     update : function(html, loadScripts, callback) {
9850         if (!this.dom) {
9851             return this;
9852         }
9853         html = html || "";
9854
9855         if (loadScripts !== true) {
9856             this.dom.innerHTML = html;
9857             if (typeof callback == 'function') {
9858                 callback();
9859             }
9860             return this;
9861         }
9862
9863         var id  = Ext.id(),
9864             dom = this.dom;
9865
9866         html += '<span id="' + id + '"></span>';
9867
9868         Ext.lib.Event.onAvailable(id, function() {
9869             var DOC    = document,
9870                 hd     = DOC.getElementsByTagName("head")[0],
9871                 re     = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig,
9872                 srcRe  = /\ssrc=([\'\"])(.*?)\1/i,
9873                 typeRe = /\stype=([\'\"])(.*?)\1/i,
9874                 match,
9875                 attrs,
9876                 srcMatch,
9877                 typeMatch,
9878                 el,
9879                 s;
9880
9881             while ((match = re.exec(html))) {
9882                 attrs = match[1];
9883                 srcMatch = attrs ? attrs.match(srcRe) : false;
9884                 if (srcMatch && srcMatch[2]) {
9885                    s = DOC.createElement("script");
9886                    s.src = srcMatch[2];
9887                    typeMatch = attrs.match(typeRe);
9888                    if (typeMatch && typeMatch[2]) {
9889                        s.type = typeMatch[2];
9890                    }
9891                    hd.appendChild(s);
9892                 } else if (match[2] && match[2].length > 0) {
9893                     if (window.execScript) {
9894                        window.execScript(match[2]);
9895                     } else {
9896                        window.eval(match[2]);
9897                     }
9898                 }
9899             }
9900             
9901             el = DOC.getElementById(id);
9902             if (el) {
9903                 Ext.removeNode(el);
9904             }
9905             
9906             if (typeof callback == 'function') {
9907                 callback();
9908             }
9909         });
9910         dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
9911         return this;
9912     },
9913
9914     // inherit docs, overridden so we can add removeAnchor
9915     removeAllListeners : function() {
9916         this.removeAnchor();
9917         Ext.EventManager.removeAll(this.dom);
9918         return this;
9919     },
9920
9921     /**
9922      * Creates a proxy element of this element
9923      * @param {String/Object} config The class name of the proxy element or a DomHelper config object
9924      * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
9925      * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
9926      * @return {Ext.Element} The new proxy element
9927      */
9928     createProxy : function(config, renderTo, matchBox) {
9929         config = (typeof config == 'object') ? config : {tag : "div", cls: config};
9930
9931         var me = this,
9932             proxy = renderTo ? Ext.DomHelper.append(renderTo, config, true) :
9933                                Ext.DomHelper.insertBefore(me.dom, config, true);
9934
9935         if (matchBox && me.setBox && me.getBox) { // check to make sure Element.position.js is loaded
9936            proxy.setBox(me.getBox());
9937         }
9938         return proxy;
9939     }
9940 });
9941
9942 Ext.Element.prototype.getUpdateManager = Ext.Element.prototype.getUpdater;
9943 /**
9944  * @class Ext.Element
9945  */
9946 Ext.Element.addMethods({
9947     /**
9948      * Gets the x,y coordinates specified by the anchor position on the element.
9949      * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo}
9950      * for details on supported anchor positions.
9951      * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead
9952      * of page coordinates
9953      * @param {Object} size (optional) An object containing the size to use for calculating anchor position
9954      * {width: (target width), height: (target height)} (defaults to the element's current size)
9955      * @return {Array} [x, y] An array containing the element's x and y coordinates
9956      */
9957     getAnchorXY : function(anchor, local, s){
9958         //Passing a different size is useful for pre-calculating anchors,
9959         //especially for anchored animations that change the el size.
9960                 anchor = (anchor || "tl").toLowerCase();
9961         s = s || {};
9962         
9963         var me = this,        
9964                 vp = me.dom == document.body || me.dom == document,
9965                 w = s.width || vp ? Ext.lib.Dom.getViewWidth() : me.getWidth(),
9966                 h = s.height || vp ? Ext.lib.Dom.getViewHeight() : me.getHeight(),                              
9967                 xy,             
9968                 r = Math.round,
9969                 o = me.getXY(),
9970                 scroll = me.getScroll(),
9971                 extraX = vp ? scroll.left : !local ? o[0] : 0,
9972                 extraY = vp ? scroll.top : !local ? o[1] : 0,
9973                 hash = {
9974                         c  : [r(w * 0.5), r(h * 0.5)],
9975                         t  : [r(w * 0.5), 0],
9976                         l  : [0, r(h * 0.5)],
9977                         r  : [w, r(h * 0.5)],
9978                         b  : [r(w * 0.5), h],
9979                         tl : [0, 0],    
9980                         bl : [0, h],
9981                         br : [w, h],
9982                         tr : [w, 0]
9983                 };
9984         
9985         xy = hash[anchor];      
9986         return [xy[0] + extraX, xy[1] + extraY]; 
9987     },
9988
9989     /**
9990      * Anchors an element to another element and realigns it when the window is resized.
9991      * @param {Mixed} element The element to align to.
9992      * @param {String} position The position to align to.
9993      * @param {Array} offsets (optional) Offset the positioning by [x, y]
9994      * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
9995      * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
9996      * is a number, it is used as the buffer delay (defaults to 50ms).
9997      * @param {Function} callback The function to call after the animation finishes
9998      * @return {Ext.Element} this
9999      */
10000     anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){        
10001             var me = this,
10002             dom = me.dom,
10003             scroll = !Ext.isEmpty(monitorScroll),
10004             action = function(){
10005                 Ext.fly(dom).alignTo(el, alignment, offsets, animate);
10006                 Ext.callback(callback, Ext.fly(dom));
10007             },
10008             anchor = this.getAnchor();
10009             
10010         // previous listener anchor, remove it
10011         this.removeAnchor();
10012         Ext.apply(anchor, {
10013             fn: action,
10014             scroll: scroll
10015         });
10016
10017         Ext.EventManager.onWindowResize(action, null);
10018         
10019         if(scroll){
10020             Ext.EventManager.on(window, 'scroll', action, null,
10021                 {buffer: !isNaN(monitorScroll) ? monitorScroll : 50});
10022         }
10023         action.call(me); // align immediately
10024         return me;
10025     },
10026     
10027     /**
10028      * Remove any anchor to this element. See {@link #anchorTo}.
10029      * @return {Ext.Element} this
10030      */
10031     removeAnchor : function(){
10032         var me = this,
10033             anchor = this.getAnchor();
10034             
10035         if(anchor && anchor.fn){
10036             Ext.EventManager.removeResizeListener(anchor.fn);
10037             if(anchor.scroll){
10038                 Ext.EventManager.un(window, 'scroll', anchor.fn);
10039             }
10040             delete anchor.fn;
10041         }
10042         return me;
10043     },
10044     
10045     // private
10046     getAnchor : function(){
10047         var data = Ext.Element.data,
10048             dom = this.dom;
10049             if (!dom) {
10050                 return;
10051             }
10052             var anchor = data(dom, '_anchor');
10053             
10054         if(!anchor){
10055             anchor = data(dom, '_anchor', {});
10056         }
10057         return anchor;
10058     },
10059
10060     /**
10061      * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
10062      * supported position values.
10063      * @param {Mixed} element The element to align to.
10064      * @param {String} position (optional, defaults to "tl-bl?") The position to align to.
10065      * @param {Array} offsets (optional) Offset the positioning by [x, y]
10066      * @return {Array} [x, y]
10067      */
10068     getAlignToXY : function(el, p, o){      
10069         el = Ext.get(el);
10070         
10071         if(!el || !el.dom){
10072             throw "Element.alignToXY with an element that doesn't exist";
10073         }
10074         
10075         o = o || [0,0];
10076         p = (!p || p == "?" ? "tl-bl?" : (!(/-/).test(p) && p !== "" ? "tl-" + p : p || "tl-bl")).toLowerCase();       
10077                 
10078         var me = this,
10079                 d = me.dom,
10080                 a1,
10081                 a2,
10082                 x,
10083                 y,
10084                 //constrain the aligned el to viewport if necessary
10085                 w,
10086                 h,
10087                 r,
10088                 dw = Ext.lib.Dom.getViewWidth() -10, // 10px of margin for ie
10089                 dh = Ext.lib.Dom.getViewHeight()-10, // 10px of margin for ie
10090                 p1y,
10091                 p1x,            
10092                 p2y,
10093                 p2x,
10094                 swapY,
10095                 swapX,
10096                 doc = document,
10097                 docElement = doc.documentElement,
10098                 docBody = doc.body,
10099                 scrollX = (docElement.scrollLeft || docBody.scrollLeft || 0)+5,
10100                 scrollY = (docElement.scrollTop || docBody.scrollTop || 0)+5,
10101                 c = false, //constrain to viewport
10102                 p1 = "", 
10103                 p2 = "",
10104                 m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
10105         
10106         if(!m){
10107            throw "Element.alignTo with an invalid alignment " + p;
10108         }
10109         
10110         p1 = m[1]; 
10111         p2 = m[2]; 
10112         c = !!m[3];
10113
10114         //Subtract the aligned el's internal xy from the target's offset xy
10115         //plus custom offset to get the aligned el's new offset xy
10116         a1 = me.getAnchorXY(p1, true);
10117         a2 = el.getAnchorXY(p2, false);
10118
10119         x = a2[0] - a1[0] + o[0];
10120         y = a2[1] - a1[1] + o[1];
10121
10122         if(c){    
10123                w = me.getWidth();
10124            h = me.getHeight();
10125            r = el.getRegion();       
10126            //If we are at a viewport boundary and the aligned el is anchored on a target border that is
10127            //perpendicular to the vp border, allow the aligned el to slide on that border,
10128            //otherwise swap the aligned el to the opposite border of the target.
10129            p1y = p1.charAt(0);
10130            p1x = p1.charAt(p1.length-1);
10131            p2y = p2.charAt(0);
10132            p2x = p2.charAt(p2.length-1);
10133            swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
10134            swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));          
10135            
10136
10137            if (x + w > dw + scrollX) {
10138                 x = swapX ? r.left-w : dw+scrollX-w;
10139            }
10140            if (x < scrollX) {
10141                x = swapX ? r.right : scrollX;
10142            }
10143            if (y + h > dh + scrollY) {
10144                 y = swapY ? r.top-h : dh+scrollY-h;
10145             }
10146            if (y < scrollY){
10147                y = swapY ? r.bottom : scrollY;
10148            }
10149         }
10150         return [x,y];
10151     },
10152
10153     /**
10154      * Aligns this element with another element relative to the specified anchor points. If the other element is the
10155      * document it aligns it to the viewport.
10156      * The position parameter is optional, and can be specified in any one of the following formats:
10157      * <ul>
10158      *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
10159      *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
10160      *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
10161      *       deprecated in favor of the newer two anchor syntax below</i>.</li>
10162      *   <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
10163      *       element's anchor point, and the second value is used as the target's anchor point.</li>
10164      * </ul>
10165      * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
10166      * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
10167      * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
10168      * that specified in order to enforce the viewport constraints.
10169      * Following are all of the supported anchor positions:
10170 <pre>
10171 Value  Description
10172 -----  -----------------------------
10173 tl     The top left corner (default)
10174 t      The center of the top edge
10175 tr     The top right corner
10176 l      The center of the left edge
10177 c      In the center of the element
10178 r      The center of the right edge
10179 bl     The bottom left corner
10180 b      The center of the bottom edge
10181 br     The bottom right corner
10182 </pre>
10183 Example Usage:
10184 <pre><code>
10185 // align el to other-el using the default positioning ("tl-bl", non-constrained)
10186 el.alignTo("other-el");
10187
10188 // align the top left corner of el with the top right corner of other-el (constrained to viewport)
10189 el.alignTo("other-el", "tr?");
10190
10191 // align the bottom right corner of el with the center left edge of other-el
10192 el.alignTo("other-el", "br-l?");
10193
10194 // align the center of el with the bottom left corner of other-el and
10195 // adjust the x position by -6 pixels (and the y position by 0)
10196 el.alignTo("other-el", "c-bl", [-6, 0]);
10197 </code></pre>
10198      * @param {Mixed} element The element to align to.
10199      * @param {String} position (optional, defaults to "tl-bl?") The position to align to.
10200      * @param {Array} offsets (optional) Offset the positioning by [x, y]
10201      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10202      * @return {Ext.Element} this
10203      */
10204     alignTo : function(element, position, offsets, animate){
10205             var me = this;
10206         return me.setXY(me.getAlignToXY(element, position, offsets),
10207                                 me.preanim && !!animate ? me.preanim(arguments, 3) : false);
10208     },
10209     
10210     // private ==>  used outside of core
10211     adjustForConstraints : function(xy, parent, offsets){
10212         return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
10213     },
10214
10215     // private ==>  used outside of core
10216     getConstrainToXY : function(el, local, offsets, proposedXY){   
10217             var os = {top:0, left:0, bottom:0, right: 0};
10218
10219         return function(el, local, offsets, proposedXY){
10220             el = Ext.get(el);
10221             offsets = offsets ? Ext.applyIf(offsets, os) : os;
10222
10223             var vw, vh, vx = 0, vy = 0;
10224             if(el.dom == document.body || el.dom == document){
10225                 vw =Ext.lib.Dom.getViewWidth();
10226                 vh = Ext.lib.Dom.getViewHeight();
10227             }else{
10228                 vw = el.dom.clientWidth;
10229                 vh = el.dom.clientHeight;
10230                 if(!local){
10231                     var vxy = el.getXY();
10232                     vx = vxy[0];
10233                     vy = vxy[1];
10234                 }
10235             }
10236
10237             var s = el.getScroll();
10238
10239             vx += offsets.left + s.left;
10240             vy += offsets.top + s.top;
10241
10242             vw -= offsets.right;
10243             vh -= offsets.bottom;
10244
10245             var vr = vx + vw,
10246                 vb = vy + vh,
10247                 xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]),
10248                 x = xy[0], y = xy[1],
10249                 offset = this.getConstrainOffset(),
10250                 w = this.dom.offsetWidth + offset, 
10251                 h = this.dom.offsetHeight + offset;
10252
10253             // only move it if it needs it
10254             var moved = false;
10255
10256             // first validate right/bottom
10257             if((x + w) > vr){
10258                 x = vr - w;
10259                 moved = true;
10260             }
10261             if((y + h) > vb){
10262                 y = vb - h;
10263                 moved = true;
10264             }
10265             // then make sure top/left isn't negative
10266             if(x < vx){
10267                 x = vx;
10268                 moved = true;
10269             }
10270             if(y < vy){
10271                 y = vy;
10272                 moved = true;
10273             }
10274             return moved ? [x, y] : false;
10275         };
10276     }(),
10277             
10278             
10279                 
10280 //         el = Ext.get(el);
10281 //         offsets = Ext.applyIf(offsets || {}, {top : 0, left : 0, bottom : 0, right : 0});
10282
10283 //         var  me = this,
10284 //              doc = document,
10285 //              s = el.getScroll(),
10286 //              vxy = el.getXY(),
10287 //              vx = offsets.left + s.left, 
10288 //              vy = offsets.top + s.top,               
10289 //              vw = -offsets.right, 
10290 //              vh = -offsets.bottom, 
10291 //              vr,
10292 //              vb,
10293 //              xy = proposedXY || (!local ? me.getXY() : [me.getLeft(true), me.getTop(true)]),
10294 //              x = xy[0],
10295 //              y = xy[1],
10296 //              w = me.dom.offsetWidth, h = me.dom.offsetHeight,
10297 //              moved = false; // only move it if it needs it
10298 //       
10299 //              
10300 //         if(el.dom == doc.body || el.dom == doc){
10301 //             vw += Ext.lib.Dom.getViewWidth();
10302 //             vh += Ext.lib.Dom.getViewHeight();
10303 //         }else{
10304 //             vw += el.dom.clientWidth;
10305 //             vh += el.dom.clientHeight;
10306 //             if(!local){                    
10307 //                 vx += vxy[0];
10308 //                 vy += vxy[1];
10309 //             }
10310 //         }
10311
10312 //         // first validate right/bottom
10313 //         if(x + w > vx + vw){
10314 //             x = vx + vw - w;
10315 //             moved = true;
10316 //         }
10317 //         if(y + h > vy + vh){
10318 //             y = vy + vh - h;
10319 //             moved = true;
10320 //         }
10321 //         // then make sure top/left isn't negative
10322 //         if(x < vx){
10323 //             x = vx;
10324 //             moved = true;
10325 //         }
10326 //         if(y < vy){
10327 //             y = vy;
10328 //             moved = true;
10329 //         }
10330 //         return moved ? [x, y] : false;
10331 //    },
10332
10333     // private, used internally
10334     getConstrainOffset : function(){
10335         return 0;
10336     },
10337     
10338     /**
10339     * Calculates the x, y to center this element on the screen
10340     * @return {Array} The x, y values [x, y]
10341     */
10342     getCenterXY : function(){
10343         return this.getAlignToXY(document, 'c-c');
10344     },
10345
10346     /**
10347     * Centers the Element in either the viewport, or another Element.
10348     * @param {Mixed} centerIn (optional) The element in which to center the element.
10349     */
10350     center : function(centerIn){
10351         return this.alignTo(centerIn || document, 'c-c');        
10352     }    
10353 });
10354 /**
10355  * @class Ext.Element
10356  */
10357 Ext.Element.addMethods({
10358     /**
10359      * Creates a {@link Ext.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
10360      * @param {String} selector The CSS selector
10361      * @param {Boolean} unique (optional) True to create a unique Ext.Element for each child (defaults to false, which creates a single shared flyweight object)
10362      * @return {CompositeElement/CompositeElementLite} The composite element
10363      */
10364     select : function(selector, unique){
10365         return Ext.Element.select(selector, unique, this.dom);
10366     }
10367 });/**
10368  * @class Ext.Element
10369  */
10370 Ext.apply(Ext.Element.prototype, function() {
10371         var GETDOM = Ext.getDom,
10372                 GET = Ext.get,
10373                 DH = Ext.DomHelper;
10374         
10375         return {        
10376                 /**
10377              * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
10378              * @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.
10379              * @param {String} where (optional) 'before' or 'after' defaults to before
10380              * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Ext.Element
10381              * @return {Ext.Element} The inserted Element. If an array is passed, the last inserted element is returned.
10382              */
10383             insertSibling: function(el, where, returnDom){
10384                 var me = this,
10385                         rt,
10386                 isAfter = (where || 'before').toLowerCase() == 'after',
10387                 insertEl;
10388                         
10389                 if(Ext.isArray(el)){
10390                 insertEl = me;
10391                     Ext.each(el, function(e) {
10392                             rt = Ext.fly(insertEl, '_internal').insertSibling(e, where, returnDom);
10393                     if(isAfter){
10394                         insertEl = rt;
10395                     }
10396                     });
10397                     return rt;
10398                 }
10399                         
10400                 el = el || {};
10401                 
10402             if(el.nodeType || el.dom){
10403                 rt = me.dom.parentNode.insertBefore(GETDOM(el), isAfter ? me.dom.nextSibling : me.dom);
10404                 if (!returnDom) {
10405                     rt = GET(rt);
10406                 }
10407             }else{
10408                 if (isAfter && !me.dom.nextSibling) {
10409                     rt = DH.append(me.dom.parentNode, el, !returnDom);
10410                 } else {                    
10411                     rt = DH[isAfter ? 'insertAfter' : 'insertBefore'](me.dom, el, !returnDom);
10412                 }
10413             }
10414                 return rt;
10415             }
10416     };
10417 }());/**
10418  * @class Ext.Element
10419  */
10420
10421 // special markup used throughout Ext when box wrapping elements
10422 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>';
10423
10424 Ext.Element.addMethods(function(){
10425     var INTERNAL = "_internal",
10426         pxMatch = /(\d+\.?\d+)px/;
10427     return {
10428         /**
10429          * More flexible version of {@link #setStyle} for setting style properties.
10430          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
10431          * a function which returns such a specification.
10432          * @return {Ext.Element} this
10433          */
10434         applyStyles : function(style){
10435             Ext.DomHelper.applyStyles(this.dom, style);
10436             return this;
10437         },
10438
10439         /**
10440          * Returns an object with properties matching the styles requested.
10441          * For example, el.getStyles('color', 'font-size', 'width') might return
10442          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
10443          * @param {String} style1 A style name
10444          * @param {String} style2 A style name
10445          * @param {String} etc.
10446          * @return {Object} The style object
10447          */
10448         getStyles : function(){
10449             var ret = {};
10450             Ext.each(arguments, function(v) {
10451                ret[v] = this.getStyle(v);
10452             },
10453             this);
10454             return ret;
10455         },
10456
10457         // private  ==> used by ext full
10458         setOverflow : function(v){
10459             var dom = this.dom;
10460             if(v=='auto' && Ext.isMac && Ext.isGecko2){ // work around stupid FF 2.0/Mac scroll bar bug
10461                 dom.style.overflow = 'hidden';
10462                 (function(){dom.style.overflow = 'auto';}).defer(1);
10463             }else{
10464                 dom.style.overflow = v;
10465             }
10466         },
10467
10468        /**
10469         * <p>Wraps the specified element with a special 9 element markup/CSS block that renders by default as
10470         * a gray container with a gradient background, rounded corners and a 4-way shadow.</p>
10471         * <p>This special markup is used throughout Ext when box wrapping elements ({@link Ext.Button},
10472         * {@link Ext.Panel} when <tt>{@link Ext.Panel#frame frame=true}</tt>, {@link Ext.Window}).  The markup
10473         * is of this form:</p>
10474         * <pre><code>
10475     Ext.Element.boxMarkup =
10476     &#39;&lt;div class="{0}-tl">&lt;div class="{0}-tr">&lt;div class="{0}-tc">&lt;/div>&lt;/div>&lt;/div>
10477      &lt;div class="{0}-ml">&lt;div class="{0}-mr">&lt;div class="{0}-mc">&lt;/div>&lt;/div>&lt;/div>
10478      &lt;div class="{0}-bl">&lt;div class="{0}-br">&lt;div class="{0}-bc">&lt;/div>&lt;/div>&lt;/div>&#39;;
10479         * </code></pre>
10480         * <p>Example usage:</p>
10481         * <pre><code>
10482     // Basic box wrap
10483     Ext.get("foo").boxWrap();
10484
10485     // You can also add a custom class and use CSS inheritance rules to customize the box look.
10486     // 'x-box-blue' is a built-in alternative -- look at the related CSS definitions as an example
10487     // for how to create a custom box wrap style.
10488     Ext.get("foo").boxWrap().addClass("x-box-blue");
10489         * </code></pre>
10490         * @param {String} class (optional) A base CSS class to apply to the containing wrapper element
10491         * (defaults to <tt>'x-box'</tt>). Note that there are a number of CSS rules that are dependent on
10492         * this name to make the overall effect work, so if you supply an alternate base class, make sure you
10493         * also supply all of the necessary rules.
10494         * @return {Ext.Element} The outermost wrapping element of the created box structure.
10495         */
10496         boxWrap : function(cls){
10497             cls = cls || 'x-box';
10498             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)));
10499             Ext.DomQuery.selectNode('.' + cls + '-mc', el.dom).appendChild(this.dom);
10500             return el;
10501         },
10502
10503         /**
10504          * Set the size of this Element. If animation is true, both width and height will be animated concurrently.
10505          * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
10506          * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).</li>
10507          * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
10508          * <li>A size object in the format <code>{width: widthValue, height: heightValue}</code>.</li>
10509          * </ul></div>
10510          * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
10511          * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels).</li>
10512          * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
10513          * </ul></div>
10514          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10515          * @return {Ext.Element} this
10516          */
10517         setSize : function(width, height, animate){
10518             var me = this;
10519             if(typeof width == 'object'){ // in case of object from getSize()
10520                 height = width.height;
10521                 width = width.width;
10522             }
10523             width = me.adjustWidth(width);
10524             height = me.adjustHeight(height);
10525             if(!animate || !me.anim){
10526                 me.dom.style.width = me.addUnits(width);
10527                 me.dom.style.height = me.addUnits(height);
10528             }else{
10529                 me.anim({width: {to: width}, height: {to: height}}, me.preanim(arguments, 2));
10530             }
10531             return me;
10532         },
10533
10534         /**
10535          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
10536          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
10537          * if a height has not been set using CSS.
10538          * @return {Number}
10539          */
10540         getComputedHeight : function(){
10541             var me = this,
10542                 h = Math.max(me.dom.offsetHeight, me.dom.clientHeight);
10543             if(!h){
10544                 h = parseFloat(me.getStyle('height')) || 0;
10545                 if(!me.isBorderBox()){
10546                     h += me.getFrameWidth('tb');
10547                 }
10548             }
10549             return h;
10550         },
10551
10552         /**
10553          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
10554          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
10555          * if a width has not been set using CSS.
10556          * @return {Number}
10557          */
10558         getComputedWidth : function(){
10559             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
10560             if(!w){
10561                 w = parseFloat(this.getStyle('width')) || 0;
10562                 if(!this.isBorderBox()){
10563                     w += this.getFrameWidth('lr');
10564                 }
10565             }
10566             return w;
10567         },
10568
10569         /**
10570          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
10571          for more information about the sides.
10572          * @param {String} sides
10573          * @return {Number}
10574          */
10575         getFrameWidth : function(sides, onlyContentBox){
10576             return onlyContentBox && this.isBorderBox() ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
10577         },
10578
10579         /**
10580          * Sets up event handlers to add and remove a css class when the mouse is over this element
10581          * @param {String} className
10582          * @return {Ext.Element} this
10583          */
10584         addClassOnOver : function(className){
10585             this.hover(
10586                 function(){
10587                     Ext.fly(this, INTERNAL).addClass(className);
10588                 },
10589                 function(){
10590                     Ext.fly(this, INTERNAL).removeClass(className);
10591                 }
10592             );
10593             return this;
10594         },
10595
10596         /**
10597          * Sets up event handlers to add and remove a css class when this element has the focus
10598          * @param {String} className
10599          * @return {Ext.Element} this
10600          */
10601         addClassOnFocus : function(className){
10602             this.on("focus", function(){
10603                 Ext.fly(this, INTERNAL).addClass(className);
10604             }, this.dom);
10605             this.on("blur", function(){
10606                 Ext.fly(this, INTERNAL).removeClass(className);
10607             }, this.dom);
10608             return this;
10609         },
10610
10611         /**
10612          * 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)
10613          * @param {String} className
10614          * @return {Ext.Element} this
10615          */
10616         addClassOnClick : function(className){
10617             var dom = this.dom;
10618             this.on("mousedown", function(){
10619                 Ext.fly(dom, INTERNAL).addClass(className);
10620                 var d = Ext.getDoc(),
10621                     fn = function(){
10622                         Ext.fly(dom, INTERNAL).removeClass(className);
10623                         d.removeListener("mouseup", fn);
10624                     };
10625                 d.on("mouseup", fn);
10626             });
10627             return this;
10628         },
10629
10630         /**
10631          * <p>Returns the dimensions of the element available to lay content out in.<p>
10632          * <p>If the element (or any ancestor element) has CSS style <code>display : none</code>, the dimensions will be zero.</p>
10633          * example:<pre><code>
10634         var vpSize = Ext.getBody().getViewSize();
10635
10636         // all Windows created afterwards will have a default value of 90% height and 95% width
10637         Ext.Window.override({
10638             width: vpSize.width * 0.9,
10639             height: vpSize.height * 0.95
10640         });
10641         // To handle window resizing you would have to hook onto onWindowResize.
10642         * </code></pre>
10643         *
10644         * getViewSize utilizes clientHeight/clientWidth which excludes sizing of scrollbars.
10645         * To obtain the size including scrollbars, use getStyleSize
10646         *
10647         * Sizing of the document body is handled at the adapter level which handles special cases for IE and strict modes, etc.
10648         */
10649
10650         getViewSize : function(){
10651             var doc = document,
10652                 d = this.dom,
10653                 isDoc = (d == doc || d == doc.body);
10654
10655             // If the body, use Ext.lib.Dom
10656             if (isDoc) {
10657                 var extdom = Ext.lib.Dom;
10658                 return {
10659                     width : extdom.getViewWidth(),
10660                     height : extdom.getViewHeight()
10661                 };
10662
10663             // Else use clientHeight/clientWidth
10664             } else {
10665                 return {
10666                     width : d.clientWidth,
10667                     height : d.clientHeight
10668                 };
10669             }
10670         },
10671
10672         /**
10673         * <p>Returns the dimensions of the element available to lay content out in.<p>
10674         *
10675         * getStyleSize utilizes prefers style sizing if present, otherwise it chooses the larger of offsetHeight/clientHeight and offsetWidth/clientWidth.
10676         * To obtain the size excluding scrollbars, use getViewSize
10677         *
10678         * Sizing of the document body is handled at the adapter level which handles special cases for IE and strict modes, etc.
10679         */
10680
10681         getStyleSize : function(){
10682             var me = this,
10683                 w, h,
10684                 doc = document,
10685                 d = this.dom,
10686                 isDoc = (d == doc || d == doc.body),
10687                 s = d.style;
10688
10689             // If the body, use Ext.lib.Dom
10690             if (isDoc) {
10691                 var extdom = Ext.lib.Dom;
10692                 return {
10693                     width : extdom.getViewWidth(),
10694                     height : extdom.getViewHeight()
10695                 };
10696             }
10697             // Use Styles if they are set
10698             if(s.width && s.width != 'auto'){
10699                 w = parseFloat(s.width);
10700                 if(me.isBorderBox()){
10701                    w -= me.getFrameWidth('lr');
10702                 }
10703             }
10704             // Use Styles if they are set
10705             if(s.height && s.height != 'auto'){
10706                 h = parseFloat(s.height);
10707                 if(me.isBorderBox()){
10708                    h -= me.getFrameWidth('tb');
10709                 }
10710             }
10711             // Use getWidth/getHeight if style not set.
10712             return {width: w || me.getWidth(true), height: h || me.getHeight(true)};
10713         },
10714
10715         /**
10716          * Returns the size of the element.
10717          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
10718          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
10719          */
10720         getSize : function(contentSize){
10721             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
10722         },
10723
10724         /**
10725          * Forces the browser to repaint this element
10726          * @return {Ext.Element} this
10727          */
10728         repaint : function(){
10729             var dom = this.dom;
10730             this.addClass("x-repaint");
10731             setTimeout(function(){
10732                 Ext.fly(dom).removeClass("x-repaint");
10733             }, 1);
10734             return this;
10735         },
10736
10737         /**
10738          * Disables text selection for this element (normalized across browsers)
10739          * @return {Ext.Element} this
10740          */
10741         unselectable : function(){
10742             this.dom.unselectable = "on";
10743             return this.swallowEvent("selectstart", true).
10744                         applyStyles("-moz-user-select:none;-khtml-user-select:none;").
10745                         addClass("x-unselectable");
10746         },
10747
10748         /**
10749          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
10750          * then it returns the calculated width of the sides (see getPadding)
10751          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
10752          * @return {Object/Number}
10753          */
10754         getMargins : function(side){
10755             var me = this,
10756                 key,
10757                 hash = {t:"top", l:"left", r:"right", b: "bottom"},
10758                 o = {};
10759
10760             if (!side) {
10761                 for (key in me.margins){
10762                     o[hash[key]] = parseFloat(me.getStyle(me.margins[key])) || 0;
10763                 }
10764                 return o;
10765             } else {
10766                 return me.addStyles.call(me, side, me.margins);
10767             }
10768         }
10769     };
10770 }());
10771 /**
10772  * @class Ext.Element
10773  */
10774 Ext.Element.addMethods({
10775     /**
10776      * 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.
10777      * @param {Object} box The box to fill {x, y, width, height}
10778      * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
10779      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10780      * @return {Ext.Element} this
10781      */
10782     setBox : function(box, adjust, animate){
10783         var me = this,
10784                 w = box.width, 
10785                 h = box.height;
10786         if((adjust && !me.autoBoxAdjust) && !me.isBorderBox()){
10787            w -= (me.getBorderWidth("lr") + me.getPadding("lr"));
10788            h -= (me.getBorderWidth("tb") + me.getPadding("tb"));
10789         }
10790         me.setBounds(box.x, box.y, w, h, me.animTest.call(me, arguments, animate, 2));
10791         return me;
10792     },
10793
10794     /**
10795      * Return an object defining the area of this Element which can be passed to {@link #setBox} to
10796      * set another Element's size/location to match this element.
10797      * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
10798      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
10799      * @return {Object} box An object in the format<pre><code>
10800 {
10801     x: &lt;Element's X position>,
10802     y: &lt;Element's Y position>,
10803     width: &lt;Element's width>,
10804     height: &lt;Element's height>,
10805     bottom: &lt;Element's lower bound>,
10806     right: &lt;Element's rightmost bound>
10807 }
10808 </code></pre>
10809      * The returned object may also be addressed as an Array where index 0 contains the X position
10810      * and index 1 contains the Y position. So the result may also be used for {@link #setXY}
10811      */
10812         getBox : function(contentBox, local) {      
10813             var me = this,
10814                 xy,
10815                 left,
10816                 top,
10817                 getBorderWidth = me.getBorderWidth,
10818                 getPadding = me.getPadding, 
10819                 l,
10820                 r,
10821                 t,
10822                 b;
10823         if(!local){
10824             xy = me.getXY();
10825         }else{
10826             left = parseInt(me.getStyle("left"), 10) || 0;
10827             top = parseInt(me.getStyle("top"), 10) || 0;
10828             xy = [left, top];
10829         }
10830         var el = me.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
10831         if(!contentBox){
10832             bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
10833         }else{
10834             l = getBorderWidth.call(me, "l") + getPadding.call(me, "l");
10835             r = getBorderWidth.call(me, "r") + getPadding.call(me, "r");
10836             t = getBorderWidth.call(me, "t") + getPadding.call(me, "t");
10837             b = getBorderWidth.call(me, "b") + getPadding.call(me, "b");
10838             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)};
10839         }
10840         bx.right = bx.x + bx.width;
10841         bx.bottom = bx.y + bx.height;
10842         return bx;
10843         },
10844         
10845     /**
10846      * Move this element relative to its current position.
10847      * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down").
10848      * @param {Number} distance How far to move the element in pixels
10849      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10850      * @return {Ext.Element} this
10851      */
10852      move : function(direction, distance, animate){
10853         var me = this,          
10854                 xy = me.getXY(),
10855                 x = xy[0],
10856                 y = xy[1],              
10857                 left = [x - distance, y],
10858                 right = [x + distance, y],
10859                 top = [x, y - distance],
10860                 bottom = [x, y + distance],
10861                 hash = {
10862                         l :     left,
10863                         left : left,
10864                         r : right,
10865                         right : right,
10866                         t : top,
10867                         top : top,
10868                         up : top,
10869                         b : bottom, 
10870                         bottom : bottom,
10871                         down : bottom                           
10872                 };
10873         
10874             direction = direction.toLowerCase();    
10875             me.moveTo(hash[direction][0], hash[direction][1], me.animTest.call(me, arguments, animate, 2));
10876     },
10877     
10878     /**
10879      * Quick set left and top adding default units
10880      * @param {String} left The left CSS property value
10881      * @param {String} top The top CSS property value
10882      * @return {Ext.Element} this
10883      */
10884      setLeftTop : function(left, top){
10885             var me = this,
10886                 style = me.dom.style;
10887         style.left = me.addUnits(left);
10888         style.top = me.addUnits(top);
10889         return me;
10890     },
10891     
10892     /**
10893      * Returns the region of the given element.
10894      * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
10895      * @return {Region} A Ext.lib.Region containing "top, left, bottom, right" member data.
10896      */
10897     getRegion : function(){
10898         return Ext.lib.Dom.getRegion(this.dom);
10899     },
10900     
10901     /**
10902      * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
10903      * @param {Number} x X value for new position (coordinates are page-based)
10904      * @param {Number} y Y value for new position (coordinates are page-based)
10905      * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
10906      * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels)</li>
10907      * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
10908      * </ul></div>
10909      * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
10910      * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels)</li>
10911      * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
10912      * </ul></div>
10913      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10914      * @return {Ext.Element} this
10915      */
10916     setBounds : function(x, y, width, height, animate){
10917             var me = this;
10918         if (!animate || !me.anim) {
10919             me.setSize(width, height);
10920             me.setLocation(x, y);
10921         } else {
10922             me.anim({points: {to: [x, y]}, 
10923                          width: {to: me.adjustWidth(width)}, 
10924                          height: {to: me.adjustHeight(height)}},
10925                      me.preanim(arguments, 4), 
10926                      'motion');
10927         }
10928         return me;
10929     },
10930
10931     /**
10932      * Sets the element's position and size the specified region. If animation is true then width, height, x and y will be animated concurrently.
10933      * @param {Ext.lib.Region} region The region to fill
10934      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10935      * @return {Ext.Element} this
10936      */
10937     setRegion : function(region, animate) {
10938         return this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.animTest.call(this, arguments, animate, 1));
10939     }
10940 });/**
10941  * @class Ext.Element
10942  */
10943 Ext.Element.addMethods({
10944     /**
10945      * 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().
10946      * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
10947      * @param {Number} value The new scroll value
10948      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10949      * @return {Element} this
10950      */
10951     scrollTo : function(side, value, animate) {
10952         //check if we're scrolling top or left
10953         var top = /top/i.test(side),
10954             me = this,
10955             dom = me.dom,
10956             prop;
10957         if (!animate || !me.anim) {
10958             // just setting the value, so grab the direction
10959             prop = 'scroll' + (top ? 'Top' : 'Left');
10960             dom[prop] = value;
10961         }
10962         else {
10963             // if scrolling top, we need to grab scrollLeft, if left, scrollTop
10964             prop = 'scroll' + (top ? 'Left' : 'Top');
10965             me.anim({scroll: {to: top ? [dom[prop], value] : [value, dom[prop]]}}, me.preanim(arguments, 2), 'scroll');
10966         }
10967         return me;
10968     },
10969     
10970     /**
10971      * Scrolls this element into view within the passed container.
10972      * @param {Mixed} container (optional) The container element to scroll (defaults to document.body).  Should be a
10973      * string (id), dom node, or Ext.Element.
10974      * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
10975      * @return {Ext.Element} this
10976      */
10977     scrollIntoView : function(container, hscroll) {
10978         var c = Ext.getDom(container) || Ext.getBody().dom,
10979             el = this.dom,
10980             o = this.getOffsetsTo(c),
10981             l = o[0] + c.scrollLeft,
10982             t = o[1] + c.scrollTop,
10983             b = t + el.offsetHeight,
10984             r = l + el.offsetWidth,
10985             ch = c.clientHeight,
10986             ct = parseInt(c.scrollTop, 10),
10987             cl = parseInt(c.scrollLeft, 10),
10988             cb = ct + ch,
10989             cr = cl + c.clientWidth;
10990
10991         if (el.offsetHeight > ch || t < ct) {
10992             c.scrollTop = t;
10993         }
10994         else if (b > cb) {
10995             c.scrollTop = b-ch;
10996         }
10997         // corrects IE, other browsers will ignore
10998         c.scrollTop = c.scrollTop;
10999
11000         if (hscroll !== false) {
11001             if (el.offsetWidth > c.clientWidth || l < cl) {
11002                 c.scrollLeft = l;
11003             }
11004             else if (r > cr) {
11005                 c.scrollLeft = r - c.clientWidth;
11006             }
11007             c.scrollLeft = c.scrollLeft;
11008         }
11009         return this;
11010     },
11011
11012     // private
11013     scrollChildIntoView : function(child, hscroll) {
11014         Ext.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
11015     },
11016     
11017     /**
11018      * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
11019      * within this element's scrollable range.
11020      * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down").
11021      * @param {Number} distance How far to scroll the element in pixels
11022      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
11023      * @return {Boolean} Returns true if a scroll was triggered or false if the element
11024      * was scrolled as far as it could go.
11025      */
11026      scroll : function(direction, distance, animate) {
11027         if (!this.isScrollable()) {
11028             return false;
11029         }
11030         var el = this.dom,
11031             l = el.scrollLeft, t = el.scrollTop,
11032             w = el.scrollWidth, h = el.scrollHeight,
11033             cw = el.clientWidth, ch = el.clientHeight,
11034             scrolled = false, v,
11035             hash = {
11036                 l: Math.min(l + distance, w-cw),
11037                 r: v = Math.max(l - distance, 0),
11038                 t: Math.max(t - distance, 0),
11039                 b: Math.min(t + distance, h-ch)
11040             };
11041             hash.d = hash.b;
11042             hash.u = hash.t;
11043         
11044         direction = direction.substr(0, 1);
11045         if ((v = hash[direction]) > -1) {
11046             scrolled = true;
11047             this.scrollTo(direction == 'l' || direction == 'r' ? 'left' : 'top', v, this.preanim(arguments, 2));
11048         }
11049         return scrolled;
11050     }
11051 });/**
11052  * @class Ext.Element
11053  */
11054 Ext.Element.addMethods(
11055     function() {
11056         var VISIBILITY      = "visibility",
11057             DISPLAY         = "display",
11058             HIDDEN          = "hidden",
11059             NONE            = "none",
11060             XMASKED         = "x-masked",
11061             XMASKEDRELATIVE = "x-masked-relative",
11062             data            = Ext.Element.data;
11063
11064         return {
11065             /**
11066              * Checks whether the element is currently visible using both visibility and display properties.
11067              * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
11068              * @return {Boolean} True if the element is currently visible, else false
11069              */
11070             isVisible : function(deep) {
11071                 var vis = !this.isStyle(VISIBILITY, HIDDEN) && !this.isStyle(DISPLAY, NONE),
11072                     p   = this.dom.parentNode;
11073                 
11074                 if (deep !== true || !vis) {
11075                     return vis;
11076                 }
11077                 
11078                 while (p && !(/^body/i.test(p.tagName))) {
11079                     if (!Ext.fly(p, '_isVisible').isVisible()) {
11080                         return false;
11081                     }
11082                     p = p.parentNode;
11083                 }
11084                 return true;
11085             },
11086
11087             /**
11088              * Returns true if display is not "none"
11089              * @return {Boolean}
11090              */
11091             isDisplayed : function() {
11092                 return !this.isStyle(DISPLAY, NONE);
11093             },
11094
11095             /**
11096              * Convenience method for setVisibilityMode(Element.DISPLAY)
11097              * @param {String} display (optional) What to set display to when visible
11098              * @return {Ext.Element} this
11099              */
11100             enableDisplayMode : function(display) {
11101                 this.setVisibilityMode(Ext.Element.DISPLAY);
11102                 
11103                 if (!Ext.isEmpty(display)) {
11104                     data(this.dom, 'originalDisplay', display);
11105                 }
11106                 
11107                 return this;
11108             },
11109
11110             /**
11111              * Puts a mask over this element to disable user interaction. Requires core.css.
11112              * This method can only be applied to elements which accept child nodes.
11113              * @param {String} msg (optional) A message to display in the mask
11114              * @param {String} msgCls (optional) A css class to apply to the msg element
11115              * @return {Element} The mask element
11116              */
11117             mask : function(msg, msgCls) {
11118                 var me  = this,
11119                     dom = me.dom,
11120                     dh  = Ext.DomHelper,
11121                     EXTELMASKMSG = "ext-el-mask-msg",
11122                     el,
11123                     mask;
11124
11125                 if (!(/^body/i.test(dom.tagName) && me.getStyle('position') == 'static')) {
11126                     me.addClass(XMASKEDRELATIVE);
11127                 }
11128                 if (el = data(dom, 'maskMsg')) {
11129                     el.remove();
11130                 }
11131                 if (el = data(dom, 'mask')) {
11132                     el.remove();
11133                 }
11134
11135                 mask = dh.append(dom, {cls : "ext-el-mask"}, true);
11136                 data(dom, 'mask', mask);
11137
11138                 me.addClass(XMASKED);
11139                 mask.setDisplayed(true);
11140                 
11141                 if (typeof msg == 'string') {
11142                     var mm = dh.append(dom, {cls : EXTELMASKMSG, cn:{tag:'div'}}, true);
11143                     data(dom, 'maskMsg', mm);
11144                     mm.dom.className = msgCls ? EXTELMASKMSG + " " + msgCls : EXTELMASKMSG;
11145                     mm.dom.firstChild.innerHTML = msg;
11146                     mm.setDisplayed(true);
11147                     mm.center(me);
11148                 }
11149                 
11150                 // ie will not expand full height automatically
11151                 if (Ext.isIE && !(Ext.isIE7 && Ext.isStrict) && me.getStyle('height') == 'auto') {
11152                     mask.setSize(undefined, me.getHeight());
11153                 }
11154                 
11155                 return mask;
11156             },
11157
11158             /**
11159              * Removes a previously applied mask.
11160              */
11161             unmask : function() {
11162                 var me      = this,
11163                     dom     = me.dom,
11164                     mask    = data(dom, 'mask'),
11165                     maskMsg = data(dom, 'maskMsg');
11166
11167                 if (mask) {
11168                     if (maskMsg) {
11169                         maskMsg.remove();
11170                         data(dom, 'maskMsg', undefined);
11171                     }
11172                     
11173                     mask.remove();
11174                     data(dom, 'mask', undefined);
11175                     me.removeClass([XMASKED, XMASKEDRELATIVE]);
11176                 }
11177             },
11178
11179             /**
11180              * Returns true if this element is masked
11181              * @return {Boolean}
11182              */
11183             isMasked : function() {
11184                 var m = data(this.dom, 'mask');
11185                 return m && m.isVisible();
11186             },
11187
11188             /**
11189              * Creates an iframe shim for this element to keep selects and other windowed objects from
11190              * showing through.
11191              * @return {Ext.Element} The new shim element
11192              */
11193             createShim : function() {
11194                 var el = document.createElement('iframe'),
11195                     shim;
11196                 
11197                 el.frameBorder = '0';
11198                 el.className = 'ext-shim';
11199                 el.src = Ext.SSL_SECURE_URL;
11200                 shim = Ext.get(this.dom.parentNode.insertBefore(el, this.dom));
11201                 shim.autoBoxAdjust = false;
11202                 return shim;
11203             }
11204         };
11205     }()
11206 );/**
11207  * @class Ext.Element
11208  */
11209 Ext.Element.addMethods({
11210     /**
11211      * Convenience method for constructing a KeyMap
11212      * @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:
11213      * <code>{key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}</code>
11214      * @param {Function} fn The function to call
11215      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the specified function is executed. Defaults to this Element.
11216      * @return {Ext.KeyMap} The KeyMap created
11217      */
11218     addKeyListener : function(key, fn, scope){
11219         var config;
11220         if(typeof key != 'object' || Ext.isArray(key)){
11221             config = {
11222                 key: key,
11223                 fn: fn,
11224                 scope: scope
11225             };
11226         }else{
11227             config = {
11228                 key : key.key,
11229                 shift : key.shift,
11230                 ctrl : key.ctrl,
11231                 alt : key.alt,
11232                 fn: fn,
11233                 scope: scope
11234             };
11235         }
11236         return new Ext.KeyMap(this, config);
11237     },
11238
11239     /**
11240      * Creates a KeyMap for this element
11241      * @param {Object} config The KeyMap config. See {@link Ext.KeyMap} for more details
11242      * @return {Ext.KeyMap} The KeyMap created
11243      */
11244     addKeyMap : function(config){
11245         return new Ext.KeyMap(this, config);
11246     }
11247 });
11248
11249 //Import the newly-added Ext.Element functions into CompositeElementLite. We call this here because
11250 //Element.keys.js is the last extra Ext.Element include in the ext-all.js build
11251 Ext.CompositeElementLite.importElementMethods();/**
11252  * @class Ext.CompositeElementLite
11253  */
11254 Ext.apply(Ext.CompositeElementLite.prototype, {
11255     addElements : function(els, root){
11256         if(!els){
11257             return this;
11258         }
11259         if(typeof els == "string"){
11260             els = Ext.Element.selectorFunction(els, root);
11261         }
11262         var yels = this.elements;
11263         Ext.each(els, function(e) {
11264             yels.push(Ext.get(e));
11265         });
11266         return this;
11267     },
11268
11269     /**
11270      * Returns the first Element
11271      * @return {Ext.Element}
11272      */
11273     first : function(){
11274         return this.item(0);
11275     },
11276
11277     /**
11278      * Returns the last Element
11279      * @return {Ext.Element}
11280      */
11281     last : function(){
11282         return this.item(this.getCount()-1);
11283     },
11284
11285     /**
11286      * Returns true if this composite contains the passed element
11287      * @param el {Mixed} The id of an element, or an Ext.Element, or an HtmlElement to find within the composite collection.
11288      * @return Boolean
11289      */
11290     contains : function(el){
11291         return this.indexOf(el) != -1;
11292     },
11293
11294     /**
11295     * Removes the specified element(s).
11296     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11297     * or an array of any of those.
11298     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11299     * @return {CompositeElement} this
11300     */
11301     removeElement : function(keys, removeDom){
11302         var me = this,
11303             els = this.elements,
11304             el;
11305         Ext.each(keys, function(val){
11306             if ((el = (els[val] || els[val = me.indexOf(val)]))) {
11307                 if(removeDom){
11308                     if(el.dom){
11309                         el.remove();
11310                     }else{
11311                         Ext.removeNode(el);
11312                     }
11313                 }
11314                 els.splice(val, 1);
11315             }
11316         });
11317         return this;
11318     }
11319 });
11320 /**
11321  * @class Ext.CompositeElement
11322  * @extends Ext.CompositeElementLite
11323  * <p>This class encapsulates a <i>collection</i> of DOM elements, providing methods to filter
11324  * members, or to perform collective actions upon the whole set.</p>
11325  * <p>Although they are not listed, this class supports all of the methods of {@link Ext.Element} and
11326  * {@link Ext.Fx}. The methods from these classes will be performed on all the elements in this collection.</p>
11327  * <p>All methods return <i>this</i> and can be chained.</p>
11328  * Usage:
11329 <pre><code>
11330 var els = Ext.select("#some-el div.some-class", true);
11331 // or select directly from an existing element
11332 var el = Ext.get('some-el');
11333 el.select('div.some-class', true);
11334
11335 els.setWidth(100); // all elements become 100 width
11336 els.hide(true); // all elements fade out and hide
11337 // or
11338 els.setWidth(100).hide(true);
11339 </code></pre>
11340  */
11341 Ext.CompositeElement = Ext.extend(Ext.CompositeElementLite, {
11342     
11343     constructor : function(els, root){
11344         this.elements = [];
11345         this.add(els, root);
11346     },
11347     
11348     // private
11349     getElement : function(el){
11350         // In this case just return it, since we already have a reference to it
11351         return el;
11352     },
11353     
11354     // private
11355     transformElement : function(el){
11356         return Ext.get(el);
11357     }
11358
11359     /**
11360     * Adds elements to this composite.
11361     * @param {String/Array} els A string CSS selector, an array of elements or an element
11362     * @return {CompositeElement} this
11363     */
11364
11365     /**
11366      * Returns the Element object at the specified index
11367      * @param {Number} index
11368      * @return {Ext.Element}
11369      */
11370
11371     /**
11372      * Iterates each <code>element</code> in this <code>composite</code>
11373      * calling the supplied function using {@link Ext#each}.
11374      * @param {Function} fn The function to be called with each
11375      * <code>element</code>. If the supplied function returns <tt>false</tt>,
11376      * iteration stops. This function is called with the following arguments:
11377      * <div class="mdetail-params"><ul>
11378      * <li><code>element</code> : <i>Ext.Element</i><div class="sub-desc">The element at the current <code>index</code>
11379      * in the <code>composite</code></div></li>
11380      * <li><code>composite</code> : <i>Object</i> <div class="sub-desc">This composite.</div></li>
11381      * <li><code>index</code> : <i>Number</i> <div class="sub-desc">The current index within the <code>composite</code> </div></li>
11382      * </ul></div>
11383      * @param {Object} scope (optional) The scope (<code><this</code> reference) in which the specified function is executed.
11384      * Defaults to the <code>element</code> at the current <code>index</code>
11385      * within the composite.
11386      * @return {CompositeElement} this
11387      */
11388 });
11389
11390 /**
11391  * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods
11392  * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
11393  * {@link Ext.CompositeElementLite CompositeElementLite} object.
11394  * @param {String/Array} selector The CSS selector or an array of elements
11395  * @param {Boolean} unique (optional) true to create a unique Ext.Element for each element (defaults to a shared flyweight object)
11396  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
11397  * @return {CompositeElementLite/CompositeElement}
11398  * @member Ext.Element
11399  * @method select
11400  */
11401 Ext.Element.select = function(selector, unique, root){
11402     var els;
11403     if(typeof selector == "string"){
11404         els = Ext.Element.selectorFunction(selector, root);
11405     }else if(selector.length !== undefined){
11406         els = selector;
11407     }else{
11408         throw "Invalid selector";
11409     }
11410
11411     return (unique === true) ? new Ext.CompositeElement(els) : new Ext.CompositeElementLite(els);
11412 };
11413
11414 /**
11415  * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods
11416  * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
11417  * {@link Ext.CompositeElementLite CompositeElementLite} object.
11418  * @param {String/Array} selector The CSS selector or an array of elements
11419  * @param {Boolean} unique (optional) true to create a unique Ext.Element for each element (defaults to a shared flyweight object)
11420  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
11421  * @return {CompositeElementLite/CompositeElement}
11422  * @member Ext
11423  * @method select
11424  */
11425 Ext.select = Ext.Element.select;/**
11426  * @class Ext.Updater
11427  * @extends Ext.util.Observable
11428  * Provides AJAX-style update capabilities for Element objects.  Updater can be used to {@link #update}
11429  * an {@link Ext.Element} once, or you can use {@link #startAutoRefresh} to set up an auto-updating
11430  * {@link Ext.Element Element} on a specific interval.<br><br>
11431  * Usage:<br>
11432  * <pre><code>
11433  * var el = Ext.get("foo"); // Get Ext.Element object
11434  * var mgr = el.getUpdater();
11435  * mgr.update({
11436         url: "http://myserver.com/index.php",
11437         params: {
11438             param1: "foo",
11439             param2: "bar"
11440         }
11441  * });
11442  * ...
11443  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11444  * <br>
11445  * // or directly (returns the same Updater instance)
11446  * var mgr = new Ext.Updater("myElementId");
11447  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11448  * mgr.on("update", myFcnNeedsToKnow);
11449  * <br>
11450  * // short handed call directly from the element object
11451  * Ext.get("foo").load({
11452         url: "bar.php",
11453         scripts: true,
11454         params: "param1=foo&amp;param2=bar",
11455         text: "Loading Foo..."
11456  * });
11457  * </code></pre>
11458  * @constructor
11459  * Create new Updater directly.
11460  * @param {Mixed} el The element to update
11461  * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already
11462  * has an Updater and if it does it returns the same instance. This will skip that check (useful for extending this class).
11463  */
11464 Ext.UpdateManager = Ext.Updater = Ext.extend(Ext.util.Observable,
11465 function() {
11466     var BEFOREUPDATE = "beforeupdate",
11467         UPDATE = "update",
11468         FAILURE = "failure";
11469
11470     // private
11471     function processSuccess(response){
11472         var me = this;
11473         me.transaction = null;
11474         if (response.argument.form && response.argument.reset) {
11475             try { // put in try/catch since some older FF releases had problems with this
11476                 response.argument.form.reset();
11477             } catch(e){}
11478         }
11479         if (me.loadScripts) {
11480             me.renderer.render(me.el, response, me,
11481                updateComplete.createDelegate(me, [response]));
11482         } else {
11483             me.renderer.render(me.el, response, me);
11484             updateComplete.call(me, response);
11485         }
11486     }
11487
11488     // private
11489     function updateComplete(response, type, success){
11490         this.fireEvent(type || UPDATE, this.el, response);
11491         if(Ext.isFunction(response.argument.callback)){
11492             response.argument.callback.call(response.argument.scope, this.el, Ext.isEmpty(success) ? true : false, response, response.argument.options);
11493         }
11494     }
11495
11496     // private
11497     function processFailure(response){
11498         updateComplete.call(this, response, FAILURE, !!(this.transaction = null));
11499     }
11500
11501     return {
11502         constructor: function(el, forceNew){
11503             var me = this;
11504             el = Ext.get(el);
11505             if(!forceNew && el.updateManager){
11506                 return el.updateManager;
11507             }
11508             /**
11509              * The Element object
11510              * @type Ext.Element
11511              */
11512             me.el = el;
11513             /**
11514              * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11515              * @type String
11516              */
11517             me.defaultUrl = null;
11518
11519             me.addEvents(
11520                 /**
11521                  * @event beforeupdate
11522                  * Fired before an update is made, return false from your handler and the update is cancelled.
11523                  * @param {Ext.Element} el
11524                  * @param {String/Object/Function} url
11525                  * @param {String/Object} params
11526                  */
11527                 BEFOREUPDATE,
11528                 /**
11529                  * @event update
11530                  * Fired after successful update is made.
11531                  * @param {Ext.Element} el
11532                  * @param {Object} oResponseObject The response Object
11533                  */
11534                 UPDATE,
11535                 /**
11536                  * @event failure
11537                  * Fired on update failure.
11538                  * @param {Ext.Element} el
11539                  * @param {Object} oResponseObject The response Object
11540                  */
11541                 FAILURE
11542             );
11543
11544             Ext.apply(me, Ext.Updater.defaults);
11545             /**
11546              * Blank page URL to use with SSL file uploads (defaults to {@link Ext.Updater.defaults#sslBlankUrl}).
11547              * @property sslBlankUrl
11548              * @type String
11549              */
11550             /**
11551              * Whether to append unique parameter on get request to disable caching (defaults to {@link Ext.Updater.defaults#disableCaching}).
11552              * @property disableCaching
11553              * @type Boolean
11554              */
11555             /**
11556              * Text for loading indicator (defaults to {@link Ext.Updater.defaults#indicatorText}).
11557              * @property indicatorText
11558              * @type String
11559              */
11560             /**
11561              * Whether to show indicatorText when loading (defaults to {@link Ext.Updater.defaults#showLoadIndicator}).
11562              * @property showLoadIndicator
11563              * @type String
11564              */
11565             /**
11566              * Timeout for requests or form posts in seconds (defaults to {@link Ext.Updater.defaults#timeout}).
11567              * @property timeout
11568              * @type Number
11569              */
11570             /**
11571              * True to process scripts in the output (defaults to {@link Ext.Updater.defaults#loadScripts}).
11572              * @property loadScripts
11573              * @type Boolean
11574              */
11575
11576             /**
11577              * Transaction object of the current executing transaction, or null if there is no active transaction.
11578              */
11579             me.transaction = null;
11580             /**
11581              * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11582              * @type Function
11583              */
11584             me.refreshDelegate = me.refresh.createDelegate(me);
11585             /**
11586              * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11587              * @type Function
11588              */
11589             me.updateDelegate = me.update.createDelegate(me);
11590             /**
11591              * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11592              * @type Function
11593              */
11594             me.formUpdateDelegate = (me.formUpdate || function(){}).createDelegate(me);
11595
11596             /**
11597              * The renderer for this Updater (defaults to {@link Ext.Updater.BasicRenderer}).
11598              */
11599             me.renderer = me.renderer || me.getDefaultRenderer();
11600
11601             Ext.Updater.superclass.constructor.call(me);
11602         },
11603
11604         /**
11605          * Sets the content renderer for this Updater. See {@link Ext.Updater.BasicRenderer#render} for more details.
11606          * @param {Object} renderer The object implementing the render() method
11607          */
11608         setRenderer : function(renderer){
11609             this.renderer = renderer;
11610         },
11611
11612         /**
11613          * Returns the current content renderer for this Updater. See {@link Ext.Updater.BasicRenderer#render} for more details.
11614          * @return {Object}
11615          */
11616         getRenderer : function(){
11617            return this.renderer;
11618         },
11619
11620         /**
11621          * This is an overrideable method which returns a reference to a default
11622          * renderer class if none is specified when creating the Ext.Updater.
11623          * Defaults to {@link Ext.Updater.BasicRenderer}
11624          */
11625         getDefaultRenderer: function() {
11626             return new Ext.Updater.BasicRenderer();
11627         },
11628
11629         /**
11630          * Sets the default URL used for updates.
11631          * @param {String/Function} defaultUrl The url or a function to call to get the url
11632          */
11633         setDefaultUrl : function(defaultUrl){
11634             this.defaultUrl = defaultUrl;
11635         },
11636
11637         /**
11638          * Get the Element this Updater is bound to
11639          * @return {Ext.Element} The element
11640          */
11641         getEl : function(){
11642             return this.el;
11643         },
11644
11645         /**
11646          * Performs an <b>asynchronous</b> request, updating this element with the response.
11647          * If params are specified it uses POST, otherwise it uses GET.<br><br>
11648          * <b>Note:</b> Due to the asynchronous nature of remote server requests, the Element
11649          * will not have been fully updated when the function returns. To post-process the returned
11650          * data, use the callback option, or an <b><code>update</code></b> event handler.
11651          * @param {Object} options A config object containing any of the following options:<ul>
11652          * <li>url : <b>String/Function</b><p class="sub-desc">The URL to request or a function which
11653          * <i>returns</i> the URL (defaults to the value of {@link Ext.Ajax#url} if not specified).</p></li>
11654          * <li>method : <b>String</b><p class="sub-desc">The HTTP method to
11655          * use. Defaults to POST if the <code>params</code> argument is present, otherwise GET.</p></li>
11656          * <li>params : <b>String/Object/Function</b><p class="sub-desc">The
11657          * parameters to pass to the server (defaults to none). These may be specified as a url-encoded
11658          * string, or as an object containing properties which represent parameters,
11659          * or as a function, which returns such an object.</p></li>
11660          * <li>scripts : <b>Boolean</b><p class="sub-desc">If <code>true</code>
11661          * any &lt;script&gt; tags embedded in the response text will be extracted
11662          * and executed (defaults to {@link Ext.Updater.defaults#loadScripts}). If this option is specified,
11663          * the callback will be called <i>after</i> the execution of the scripts.</p></li>
11664          * <li>callback : <b>Function</b><p class="sub-desc">A function to
11665          * be called when the response from the server arrives. The following
11666          * parameters are passed:<ul>
11667          * <li><b>el</b> : Ext.Element<p class="sub-desc">The Element being updated.</p></li>
11668          * <li><b>success</b> : Boolean<p class="sub-desc">True for success, false for failure.</p></li>
11669          * <li><b>response</b> : XMLHttpRequest<p class="sub-desc">The XMLHttpRequest which processed the update.</p></li>
11670          * <li><b>options</b> : Object<p class="sub-desc">The config object passed to the update call.</p></li></ul>
11671          * </p></li>
11672          * <li>scope : <b>Object</b><p class="sub-desc">The scope in which
11673          * to execute the callback (The callback's <code>this</code> reference.) If the
11674          * <code>params</code> argument is a function, this scope is used for that function also.</p></li>
11675          * <li>discardUrl : <b>Boolean</b><p class="sub-desc">By default, the URL of this request becomes
11676          * the default URL for this Updater object, and will be subsequently used in {@link #refresh}
11677          * calls.  To bypass this behavior, pass <code>discardUrl:true</code> (defaults to false).</p></li>
11678          * <li>timeout : <b>Number</b><p class="sub-desc">The number of seconds to wait for a response before
11679          * timing out (defaults to {@link Ext.Updater.defaults#timeout}).</p></li>
11680          * <li>text : <b>String</b><p class="sub-desc">The text to use as the innerHTML of the
11681          * {@link Ext.Updater.defaults#indicatorText} div (defaults to 'Loading...').  To replace the entire div, not
11682          * just the text, override {@link Ext.Updater.defaults#indicatorText} directly.</p></li>
11683          * <li>nocache : <b>Boolean</b><p class="sub-desc">Only needed for GET
11684          * requests, this option causes an extra, auto-generated parameter to be appended to the request
11685          * to defeat caching (defaults to {@link Ext.Updater.defaults#disableCaching}).</p></li></ul>
11686          * <p>
11687          * For example:
11688     <pre><code>
11689     um.update({
11690         url: "your-url.php",
11691         params: {param1: "foo", param2: "bar"}, // or a URL encoded string
11692         callback: yourFunction,
11693         scope: yourObject, //(optional scope)
11694         discardUrl: true,
11695         nocache: true,
11696         text: "Loading...",
11697         timeout: 60,
11698         scripts: false // Save time by avoiding RegExp execution.
11699     });
11700     </code></pre>
11701          */
11702         update : function(url, params, callback, discardUrl){
11703             var me = this,
11704                 cfg,
11705                 callerScope;
11706
11707             if(me.fireEvent(BEFOREUPDATE, me.el, url, params) !== false){
11708                 if(Ext.isObject(url)){ // must be config object
11709                     cfg = url;
11710                     url = cfg.url;
11711                     params = params || cfg.params;
11712                     callback = callback || cfg.callback;
11713                     discardUrl = discardUrl || cfg.discardUrl;
11714                     callerScope = cfg.scope;
11715                     if(!Ext.isEmpty(cfg.nocache)){me.disableCaching = cfg.nocache;};
11716                     if(!Ext.isEmpty(cfg.text)){me.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11717                     if(!Ext.isEmpty(cfg.scripts)){me.loadScripts = cfg.scripts;};
11718                     if(!Ext.isEmpty(cfg.timeout)){me.timeout = cfg.timeout;};
11719                 }
11720                 me.showLoading();
11721
11722                 if(!discardUrl){
11723                     me.defaultUrl = url;
11724                 }
11725                 if(Ext.isFunction(url)){
11726                     url = url.call(me);
11727                 }
11728
11729                 var o = Ext.apply({}, {
11730                     url : url,
11731                     params: (Ext.isFunction(params) && callerScope) ? params.createDelegate(callerScope) : params,
11732                     success: processSuccess,
11733                     failure: processFailure,
11734                     scope: me,
11735                     callback: undefined,
11736                     timeout: (me.timeout*1000),
11737                     disableCaching: me.disableCaching,
11738                     argument: {
11739                         "options": cfg,
11740                         "url": url,
11741                         "form": null,
11742                         "callback": callback,
11743                         "scope": callerScope || window,
11744                         "params": params
11745                     }
11746                 }, cfg);
11747
11748                 me.transaction = Ext.Ajax.request(o);
11749             }
11750         },
11751
11752         /**
11753          * <p>Performs an asynchronous form post, updating this element with the response. If the form has the attribute
11754          * enctype="<a href="http://www.faqs.org/rfcs/rfc2388.html">multipart/form-data</a>", it assumes it's a file upload.
11755          * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.</p>
11756          * <p>File uploads are not performed using normal "Ajax" techniques, that is they are <b>not</b>
11757          * performed using XMLHttpRequests. Instead the form is submitted in the standard manner with the
11758          * DOM <code>&lt;form></code> element temporarily modified to have its
11759          * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
11760          * to a dynamically generated, hidden <code>&lt;iframe></code> which is inserted into the document
11761          * but removed after the return data has been gathered.</p>
11762          * <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>
11763          * and some server technologies (notably JEE) may require some custom processing in order to
11764          * retrieve parameter names and parameter values from the packet content.</p>
11765          * @param {String/HTMLElement} form The form Id or form element
11766          * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11767          * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11768          * @param {Function} callback (optional) Callback when transaction is complete. The following
11769          * parameters are passed:<ul>
11770          * <li><b>el</b> : Ext.Element<p class="sub-desc">The Element being updated.</p></li>
11771          * <li><b>success</b> : Boolean<p class="sub-desc">True for success, false for failure.</p></li>
11772          * <li><b>response</b> : XMLHttpRequest<p class="sub-desc">The XMLHttpRequest which processed the update.</p></li></ul>
11773          */
11774         formUpdate : function(form, url, reset, callback){
11775             var me = this;
11776             if(me.fireEvent(BEFOREUPDATE, me.el, form, url) !== false){
11777                 if(Ext.isFunction(url)){
11778                     url = url.call(me);
11779                 }
11780                 form = Ext.getDom(form);
11781                 me.transaction = Ext.Ajax.request({
11782                     form: form,
11783                     url:url,
11784                     success: processSuccess,
11785                     failure: processFailure,
11786                     scope: me,
11787                     timeout: (me.timeout*1000),
11788                     argument: {
11789                         "url": url,
11790                         "form": form,
11791                         "callback": callback,
11792                         "reset": reset
11793                     }
11794                 });
11795                 me.showLoading.defer(1, me);
11796             }
11797         },
11798
11799         /**
11800          * Set this element to auto refresh.  Can be canceled by calling {@link #stopAutoRefresh}.
11801          * @param {Number} interval How often to update (in seconds).
11802          * @param {String/Object/Function} url (optional) The url for this request, a config object in the same format
11803          * supported by {@link #load}, or a function to call to get the url (defaults to the last used url).  Note that while
11804          * the url used in a load call can be reused by this method, other load config options will not be reused and must be
11805          * sepcified as part of a config object passed as this paramter if needed.
11806          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string
11807          * "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
11808          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11809          * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11810          */
11811         startAutoRefresh : function(interval, url, params, callback, refreshNow){
11812             var me = this;
11813             if(refreshNow){
11814                 me.update(url || me.defaultUrl, params, callback, true);
11815             }
11816             if(me.autoRefreshProcId){
11817                 clearInterval(me.autoRefreshProcId);
11818             }
11819             me.autoRefreshProcId = setInterval(me.update.createDelegate(me, [url || me.defaultUrl, params, callback, true]), interval * 1000);
11820         },
11821
11822         /**
11823          * Stop auto refresh on this element.
11824          */
11825         stopAutoRefresh : function(){
11826             if(this.autoRefreshProcId){
11827                 clearInterval(this.autoRefreshProcId);
11828                 delete this.autoRefreshProcId;
11829             }
11830         },
11831
11832         /**
11833          * Returns true if the Updater is currently set to auto refresh its content (see {@link #startAutoRefresh}), otherwise false.
11834          */
11835         isAutoRefreshing : function(){
11836            return !!this.autoRefreshProcId;
11837         },
11838
11839         /**
11840          * Display the element's "loading" state. By default, the element is updated with {@link #indicatorText}. This
11841          * method may be overridden to perform a custom action while this Updater is actively updating its contents.
11842          */
11843         showLoading : function(){
11844             if(this.showLoadIndicator){
11845                 this.el.dom.innerHTML = this.indicatorText;
11846             }
11847         },
11848
11849         /**
11850          * Aborts the currently executing transaction, if any.
11851          */
11852         abort : function(){
11853             if(this.transaction){
11854                 Ext.Ajax.abort(this.transaction);
11855             }
11856         },
11857
11858         /**
11859          * Returns true if an update is in progress, otherwise false.
11860          * @return {Boolean}
11861          */
11862         isUpdating : function(){
11863             return this.transaction ? Ext.Ajax.isLoading(this.transaction) : false;
11864         },
11865
11866         /**
11867          * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11868          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11869          */
11870         refresh : function(callback){
11871             if(this.defaultUrl){
11872                 this.update(this.defaultUrl, null, callback, true);
11873             }
11874         }
11875     };
11876 }());
11877
11878 /**
11879  * @class Ext.Updater.defaults
11880  * The defaults collection enables customizing the default properties of Updater
11881  */
11882 Ext.Updater.defaults = {
11883    /**
11884      * Timeout for requests or form posts in seconds (defaults to 30 seconds).
11885      * @type Number
11886      */
11887     timeout : 30,
11888     /**
11889      * True to append a unique parameter to GET requests to disable caching (defaults to false).
11890      * @type Boolean
11891      */
11892     disableCaching : false,
11893     /**
11894      * Whether or not to show {@link #indicatorText} during loading (defaults to true).
11895      * @type Boolean
11896      */
11897     showLoadIndicator : true,
11898     /**
11899      * Text for loading indicator (defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11900      * @type String
11901      */
11902     indicatorText : '<div class="loading-indicator">Loading...</div>',
11903      /**
11904      * True to process scripts by default (defaults to false).
11905      * @type Boolean
11906      */
11907     loadScripts : false,
11908     /**
11909     * Blank page URL to use with SSL file uploads (defaults to {@link Ext#SSL_SECURE_URL} if set, or "javascript:false").
11910     * @type String
11911     */
11912     sslBlankUrl : Ext.SSL_SECURE_URL
11913 };
11914
11915
11916 /**
11917  * Static convenience method. <b>This method is deprecated in favor of el.load({url:'foo.php', ...})</b>.
11918  * Usage:
11919  * <pre><code>Ext.Updater.updateElement("my-div", "stuff.php");</code></pre>
11920  * @param {Mixed} el The element to update
11921  * @param {String} url The url
11922  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
11923  * @param {Object} options (optional) A config object with any of the Updater properties you want to set - for
11924  * example: {disableCaching:true, indicatorText: "Loading data..."}
11925  * @static
11926  * @deprecated
11927  * @member Ext.Updater
11928  */
11929 Ext.Updater.updateElement = function(el, url, params, options){
11930     var um = Ext.get(el).getUpdater();
11931     Ext.apply(um, options);
11932     um.update(url, params, options ? options.callback : null);
11933 };
11934
11935 /**
11936  * @class Ext.Updater.BasicRenderer
11937  * <p>This class is a base class implementing a simple render method which updates an element using results from an Ajax request.</p>
11938  * <p>The BasicRenderer updates the element's innerHTML with the responseText. To perform a custom render (i.e. XML or JSON processing),
11939  * create an object with a conforming {@link #render} method and pass it to setRenderer on the Updater.</p>
11940  */
11941 Ext.Updater.BasicRenderer = function(){};
11942
11943 Ext.Updater.BasicRenderer.prototype = {
11944     /**
11945      * This method is called when an Ajax response is received, and an Element needs updating.
11946      * @param {Ext.Element} el The element being rendered
11947      * @param {Object} xhr The XMLHttpRequest object
11948      * @param {Updater} updateManager The calling update manager
11949      * @param {Function} callback A callback that will need to be called if loadScripts is true on the Updater
11950      */
11951      render : function(el, response, updateManager, callback){
11952         el.update(response.responseText, updateManager.loadScripts, callback);
11953     }
11954 };/**
11955  * @class Date
11956  *
11957  * The date parsing and formatting syntax contains a subset of
11958  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
11959  * supported will provide results equivalent to their PHP versions.
11960  *
11961  * The following is a list of all currently supported formats:
11962  * <pre>
11963 Format  Description                                                               Example returned values
11964 ------  -----------------------------------------------------------------------   -----------------------
11965   d     Day of the month, 2 digits with leading zeros                             01 to 31
11966   D     A short textual representation of the day of the week                     Mon to Sun
11967   j     Day of the month without leading zeros                                    1 to 31
11968   l     A full textual representation of the day of the week                      Sunday to Saturday
11969   N     ISO-8601 numeric representation of the day of the week                    1 (for Monday) through 7 (for Sunday)
11970   S     English ordinal suffix for the day of the month, 2 characters             st, nd, rd or th. Works well with j
11971   w     Numeric representation of the day of the week                             0 (for Sunday) to 6 (for Saturday)
11972   z     The day of the year (starting from 0)                                     0 to 364 (365 in leap years)
11973   W     ISO-8601 week number of year, weeks starting on Monday                    01 to 53
11974   F     A full textual representation of a month, such as January or March        January to December
11975   m     Numeric representation of a month, with leading zeros                     01 to 12
11976   M     A short textual representation of a month                                 Jan to Dec
11977   n     Numeric representation of a month, without leading zeros                  1 to 12
11978   t     Number of days in the given month                                         28 to 31
11979   L     Whether it's a leap year                                                  1 if it is a leap year, 0 otherwise.
11980   o     ISO-8601 year number (identical to (Y), but if the ISO week number (W)    Examples: 1998 or 2004
11981         belongs to the previous or next year, that year is used instead)
11982   Y     A full numeric representation of a year, 4 digits                         Examples: 1999 or 2003
11983   y     A two digit representation of a year                                      Examples: 99 or 03
11984   a     Lowercase Ante meridiem and Post meridiem                                 am or pm
11985   A     Uppercase Ante meridiem and Post meridiem                                 AM or PM
11986   g     12-hour format of an hour without leading zeros                           1 to 12
11987   G     24-hour format of an hour without leading zeros                           0 to 23
11988   h     12-hour format of an hour with leading zeros                              01 to 12
11989   H     24-hour format of an hour with leading zeros                              00 to 23
11990   i     Minutes, with leading zeros                                               00 to 59
11991   s     Seconds, with leading zeros                                               00 to 59
11992   u     Decimal fraction of a second                                              Examples:
11993         (minimum 1 digit, arbitrary number of digits allowed)                     001 (i.e. 0.001s) or
11994                                                                                   100 (i.e. 0.100s) or
11995                                                                                   999 (i.e. 0.999s) or
11996                                                                                   999876543210 (i.e. 0.999876543210s)
11997   O     Difference to Greenwich time (GMT) in hours and minutes                   Example: +1030
11998   P     Difference to Greenwich time (GMT) with colon between hours and minutes   Example: -08:00
11999   T     Timezone abbreviation of the machine running the code                     Examples: EST, MDT, PDT ...
12000   Z     Timezone offset in seconds (negative if west of UTC, positive if east)    -43200 to 50400
12001   c     ISO 8601 date
12002         Notes:                                                                    Examples:
12003         1) If unspecified, the month / day defaults to the current month / day,   1991 or
12004            the time defaults to midnight, while the timezone defaults to the      1992-10 or
12005            browser's timezone. If a time is specified, it must include both hours 1993-09-20 or
12006            and minutes. The "T" delimiter, seconds, milliseconds and timezone     1994-08-19T16:20+01:00 or
12007            are optional.                                                          1995-07-18T17:21:28-02:00 or
12008         2) The decimal fraction of a second, if specified, must contain at        1996-06-17T18:22:29.98765+03:00 or
12009            least 1 digit (there is no limit to the maximum number                 1997-05-16T19:23:30,12345-0400 or
12010            of digits allowed), and may be delimited by either a '.' or a ','      1998-04-15T20:24:31.2468Z or
12011         Refer to the examples on the right for the various levels of              1999-03-14T20:24:32Z or
12012         date-time granularity which are supported, or see                         2000-02-13T21:25:33
12013         http://www.w3.org/TR/NOTE-datetime for more info.                         2001-01-12 22:26:34
12014   U     Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT)                1193432466 or -2138434463
12015   M$    Microsoft AJAX serialized dates                                           \/Date(1238606590509)\/ (i.e. UTC milliseconds since epoch) or
12016                                                                                   \/Date(1238606590509+0800)\/
12017 </pre>
12018  *
12019  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
12020  * <pre><code>
12021 // Sample date:
12022 // 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
12023
12024 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
12025 document.write(dt.format('Y-m-d'));                           // 2007-01-10
12026 document.write(dt.format('F j, Y, g:i a'));                   // January 10, 2007, 3:05 pm
12027 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
12028 </code></pre>
12029  *
12030  * Here are some standard date/time patterns that you might find helpful.  They
12031  * are not part of the source of Date.js, but to use them you can simply copy this
12032  * block of code into any script that is included after Date.js and they will also become
12033  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
12034  * <pre><code>
12035 Date.patterns = {
12036     ISO8601Long:"Y-m-d H:i:s",
12037     ISO8601Short:"Y-m-d",
12038     ShortDate: "n/j/Y",
12039     LongDate: "l, F d, Y",
12040     FullDateTime: "l, F d, Y g:i:s A",
12041     MonthDay: "F d",
12042     ShortTime: "g:i A",
12043     LongTime: "g:i:s A",
12044     SortableDateTime: "Y-m-d\\TH:i:s",
12045     UniversalSortableDateTime: "Y-m-d H:i:sO",
12046     YearMonth: "F, Y"
12047 };
12048 </code></pre>
12049  *
12050  * Example usage:
12051  * <pre><code>
12052 var dt = new Date();
12053 document.write(dt.format(Date.patterns.ShortDate));
12054 </code></pre>
12055  * <p>Developer-written, custom formats may be used by supplying both a formatting and a parsing function
12056  * which perform to specialized requirements. The functions are stored in {@link #parseFunctions} and {@link #formatFunctions}.</p>
12057  */
12058
12059 /*
12060  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
12061  * (see http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/)
12062  * They generate precompiled functions from format patterns instead of parsing and
12063  * processing each pattern every time a date is formatted. These functions are available
12064  * on every Date object.
12065  */
12066
12067 (function() {
12068
12069 /**
12070  * Global flag which determines if strict date parsing should be used.
12071  * Strict date parsing will not roll-over invalid dates, which is the
12072  * default behaviour of javascript Date objects.
12073  * (see {@link #parseDate} for more information)
12074  * Defaults to <tt>false</tt>.
12075  * @static
12076  * @type Boolean
12077 */
12078 Date.useStrict = false;
12079
12080
12081 // create private copy of Ext's String.format() method
12082 // - to remove unnecessary dependency
12083 // - to resolve namespace conflict with M$-Ajax's implementation
12084 function xf(format) {
12085     var args = Array.prototype.slice.call(arguments, 1);
12086     return format.replace(/\{(\d+)\}/g, function(m, i) {
12087         return args[i];
12088     });
12089 }
12090
12091
12092 // private
12093 Date.formatCodeToRegex = function(character, currentGroup) {
12094     // Note: currentGroup - position in regex result array (see notes for Date.parseCodes below)
12095     var p = Date.parseCodes[character];
12096
12097     if (p) {
12098       p = typeof p == 'function'? p() : p;
12099       Date.parseCodes[character] = p; // reassign function result to prevent repeated execution
12100     }
12101
12102     return p ? Ext.applyIf({
12103       c: p.c ? xf(p.c, currentGroup || "{0}") : p.c
12104     }, p) : {
12105         g:0,
12106         c:null,
12107         s:Ext.escapeRe(character) // treat unrecognised characters as literals
12108     };
12109 };
12110
12111 // private shorthand for Date.formatCodeToRegex since we'll be using it fairly often
12112 var $f = Date.formatCodeToRegex;
12113
12114 Ext.apply(Date, {
12115     /**
12116      * <p>An object hash in which each property is a date parsing function. The property name is the
12117      * format string which that function parses.</p>
12118      * <p>This object is automatically populated with date parsing functions as
12119      * date formats are requested for Ext standard formatting strings.</p>
12120      * <p>Custom parsing functions may be inserted into this object, keyed by a name which from then on
12121      * may be used as a format string to {@link #parseDate}.<p>
12122      * <p>Example:</p><pre><code>
12123 Date.parseFunctions['x-date-format'] = myDateParser;
12124 </code></pre>
12125      * <p>A parsing function should return a Date object, and is passed the following parameters:<div class="mdetail-params"><ul>
12126      * <li><code>date</code> : String<div class="sub-desc">The date string to parse.</div></li>
12127      * <li><code>strict</code> : Boolean<div class="sub-desc">True to validate date strings while parsing
12128      * (i.e. prevent javascript Date "rollover") (The default must be false).
12129      * Invalid date strings should return null when parsed.</div></li>
12130      * </ul></div></p>
12131      * <p>To enable Dates to also be <i>formatted</i> according to that format, a corresponding
12132      * formatting function must be placed into the {@link #formatFunctions} property.
12133      * @property parseFunctions
12134      * @static
12135      * @type Object
12136      */
12137     parseFunctions: {
12138         "M$": function(input, strict) {
12139             // note: the timezone offset is ignored since the M$ Ajax server sends
12140             // a UTC milliseconds-since-Unix-epoch value (negative values are allowed)
12141             var re = new RegExp('\\/Date\\(([-+])?(\\d+)(?:[+-]\\d{4})?\\)\\/');
12142             var r = (input || '').match(re);
12143             return r? new Date(((r[1] || '') + r[2]) * 1) : null;
12144         }
12145     },
12146     parseRegexes: [],
12147
12148     /**
12149      * <p>An object hash in which each property is a date formatting function. The property name is the
12150      * format string which corresponds to the produced formatted date string.</p>
12151      * <p>This object is automatically populated with date formatting functions as
12152      * date formats are requested for Ext standard formatting strings.</p>
12153      * <p>Custom formatting functions may be inserted into this object, keyed by a name which from then on
12154      * may be used as a format string to {@link #format}. Example:</p><pre><code>
12155 Date.formatFunctions['x-date-format'] = myDateFormatter;
12156 </code></pre>
12157      * <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>
12158      * <li><code>date</code> : Date<div class="sub-desc">The Date to format.</div></li>
12159      * </ul></div></p>
12160      * <p>To enable date strings to also be <i>parsed</i> according to that format, a corresponding
12161      * parsing function must be placed into the {@link #parseFunctions} property.
12162      * @property formatFunctions
12163      * @static
12164      * @type Object
12165      */
12166     formatFunctions: {
12167         "M$": function() {
12168             // UTC milliseconds since Unix epoch (M$-AJAX serialized date format (MRSF))
12169             return '\\/Date(' + this.getTime() + ')\\/';
12170         }
12171     },
12172
12173     y2kYear : 50,
12174
12175     /**
12176      * Date interval constant
12177      * @static
12178      * @type String
12179      */
12180     MILLI : "ms",
12181
12182     /**
12183      * Date interval constant
12184      * @static
12185      * @type String
12186      */
12187     SECOND : "s",
12188
12189     /**
12190      * Date interval constant
12191      * @static
12192      * @type String
12193      */
12194     MINUTE : "mi",
12195
12196     /** Date interval constant
12197      * @static
12198      * @type String
12199      */
12200     HOUR : "h",
12201
12202     /**
12203      * Date interval constant
12204      * @static
12205      * @type String
12206      */
12207     DAY : "d",
12208
12209     /**
12210      * Date interval constant
12211      * @static
12212      * @type String
12213      */
12214     MONTH : "mo",
12215
12216     /**
12217      * Date interval constant
12218      * @static
12219      * @type String
12220      */
12221     YEAR : "y",
12222
12223     /**
12224      * <p>An object hash containing default date values used during date parsing.</p>
12225      * <p>The following properties are available:<div class="mdetail-params"><ul>
12226      * <li><code>y</code> : Number<div class="sub-desc">The default year value. (defaults to undefined)</div></li>
12227      * <li><code>m</code> : Number<div class="sub-desc">The default 1-based month value. (defaults to undefined)</div></li>
12228      * <li><code>d</code> : Number<div class="sub-desc">The default day value. (defaults to undefined)</div></li>
12229      * <li><code>h</code> : Number<div class="sub-desc">The default hour value. (defaults to undefined)</div></li>
12230      * <li><code>i</code> : Number<div class="sub-desc">The default minute value. (defaults to undefined)</div></li>
12231      * <li><code>s</code> : Number<div class="sub-desc">The default second value. (defaults to undefined)</div></li>
12232      * <li><code>ms</code> : Number<div class="sub-desc">The default millisecond value. (defaults to undefined)</div></li>
12233      * </ul></div></p>
12234      * <p>Override these properties to customize the default date values used by the {@link #parseDate} method.</p>
12235      * <p><b>Note: In countries which experience Daylight Saving Time (i.e. DST), the <tt>h</tt>, <tt>i</tt>, <tt>s</tt>
12236      * and <tt>ms</tt> properties may coincide with the exact time in which DST takes effect.
12237      * It is the responsiblity of the developer to account for this.</b></p>
12238      * Example Usage:
12239      * <pre><code>
12240 // set default day value to the first day of the month
12241 Date.defaults.d = 1;
12242
12243 // parse a February date string containing only year and month values.
12244 // setting the default day value to 1 prevents weird date rollover issues
12245 // when attempting to parse the following date string on, for example, March 31st 2009.
12246 Date.parseDate('2009-02', 'Y-m'); // returns a Date object representing February 1st 2009
12247 </code></pre>
12248      * @property defaults
12249      * @static
12250      * @type Object
12251      */
12252     defaults: {},
12253
12254     /**
12255      * An array of textual day names.
12256      * Override these values for international dates.
12257      * Example:
12258      * <pre><code>
12259 Date.dayNames = [
12260     'SundayInYourLang',
12261     'MondayInYourLang',
12262     ...
12263 ];
12264 </code></pre>
12265      * @type Array
12266      * @static
12267      */
12268     dayNames : [
12269         "Sunday",
12270         "Monday",
12271         "Tuesday",
12272         "Wednesday",
12273         "Thursday",
12274         "Friday",
12275         "Saturday"
12276     ],
12277
12278     /**
12279      * An array of textual month names.
12280      * Override these values for international dates.
12281      * Example:
12282      * <pre><code>
12283 Date.monthNames = [
12284     'JanInYourLang',
12285     'FebInYourLang',
12286     ...
12287 ];
12288 </code></pre>
12289      * @type Array
12290      * @static
12291      */
12292     monthNames : [
12293         "January",
12294         "February",
12295         "March",
12296         "April",
12297         "May",
12298         "June",
12299         "July",
12300         "August",
12301         "September",
12302         "October",
12303         "November",
12304         "December"
12305     ],
12306
12307     /**
12308      * An object hash of zero-based javascript month numbers (with short month names as keys. note: keys are case-sensitive).
12309      * Override these values for international dates.
12310      * Example:
12311      * <pre><code>
12312 Date.monthNumbers = {
12313     'ShortJanNameInYourLang':0,
12314     'ShortFebNameInYourLang':1,
12315     ...
12316 };
12317 </code></pre>
12318      * @type Object
12319      * @static
12320      */
12321     monthNumbers : {
12322         Jan:0,
12323         Feb:1,
12324         Mar:2,
12325         Apr:3,
12326         May:4,
12327         Jun:5,
12328         Jul:6,
12329         Aug:7,
12330         Sep:8,
12331         Oct:9,
12332         Nov:10,
12333         Dec:11
12334     },
12335
12336     /**
12337      * Get the short month name for the given month number.
12338      * Override this function for international dates.
12339      * @param {Number} month A zero-based javascript month number.
12340      * @return {String} The short month name.
12341      * @static
12342      */
12343     getShortMonthName : function(month) {
12344         return Date.monthNames[month].substring(0, 3);
12345     },
12346
12347     /**
12348      * Get the short day name for the given day number.
12349      * Override this function for international dates.
12350      * @param {Number} day A zero-based javascript day number.
12351      * @return {String} The short day name.
12352      * @static
12353      */
12354     getShortDayName : function(day) {
12355         return Date.dayNames[day].substring(0, 3);
12356     },
12357
12358     /**
12359      * Get the zero-based javascript month number for the given short/full month name.
12360      * Override this function for international dates.
12361      * @param {String} name The short/full month name.
12362      * @return {Number} The zero-based javascript month number.
12363      * @static
12364      */
12365     getMonthNumber : function(name) {
12366         // handle camel casing for english month names (since the keys for the Date.monthNumbers hash are case sensitive)
12367         return Date.monthNumbers[name.substring(0, 1).toUpperCase() + name.substring(1, 3).toLowerCase()];
12368     },
12369
12370     /**
12371      * The base format-code to formatting-function hashmap used by the {@link #format} method.
12372      * Formatting functions are strings (or functions which return strings) which
12373      * will return the appropriate value when evaluated in the context of the Date object
12374      * from which the {@link #format} method is called.
12375      * Add to / override these mappings for custom date formatting.
12376      * Note: Date.format() treats characters as literals if an appropriate mapping cannot be found.
12377      * Example:
12378      * <pre><code>
12379 Date.formatCodes.x = "String.leftPad(this.getDate(), 2, '0')";
12380 (new Date()).format("X"); // returns the current day of the month
12381 </code></pre>
12382      * @type Object
12383      * @static
12384      */
12385     formatCodes : {
12386         d: "String.leftPad(this.getDate(), 2, '0')",
12387         D: "Date.getShortDayName(this.getDay())", // get localised short day name
12388         j: "this.getDate()",
12389         l: "Date.dayNames[this.getDay()]",
12390         N: "(this.getDay() ? this.getDay() : 7)",
12391         S: "this.getSuffix()",
12392         w: "this.getDay()",
12393         z: "this.getDayOfYear()",
12394         W: "String.leftPad(this.getWeekOfYear(), 2, '0')",
12395         F: "Date.monthNames[this.getMonth()]",
12396         m: "String.leftPad(this.getMonth() + 1, 2, '0')",
12397         M: "Date.getShortMonthName(this.getMonth())", // get localised short month name
12398         n: "(this.getMonth() + 1)",
12399         t: "this.getDaysInMonth()",
12400         L: "(this.isLeapYear() ? 1 : 0)",
12401         o: "(this.getFullYear() + (this.getWeekOfYear() == 1 && this.getMonth() > 0 ? +1 : (this.getWeekOfYear() >= 52 && this.getMonth() < 11 ? -1 : 0)))",
12402         Y: "String.leftPad(this.getFullYear(), 4, '0')",
12403         y: "('' + this.getFullYear()).substring(2, 4)",
12404         a: "(this.getHours() < 12 ? 'am' : 'pm')",
12405         A: "(this.getHours() < 12 ? 'AM' : 'PM')",
12406         g: "((this.getHours() % 12) ? this.getHours() % 12 : 12)",
12407         G: "this.getHours()",
12408         h: "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0')",
12409         H: "String.leftPad(this.getHours(), 2, '0')",
12410         i: "String.leftPad(this.getMinutes(), 2, '0')",
12411         s: "String.leftPad(this.getSeconds(), 2, '0')",
12412         u: "String.leftPad(this.getMilliseconds(), 3, '0')",
12413         O: "this.getGMTOffset()",
12414         P: "this.getGMTOffset(true)",
12415         T: "this.getTimezone()",
12416         Z: "(this.getTimezoneOffset() * -60)",
12417
12418         c: function() { // ISO-8601 -- GMT format
12419             for (var c = "Y-m-dTH:i:sP", code = [], i = 0, l = c.length; i < l; ++i) {
12420                 var e = c.charAt(i);
12421                 code.push(e == "T" ? "'T'" : Date.getFormatCode(e)); // treat T as a character literal
12422             }
12423             return code.join(" + ");
12424         },
12425         /*
12426         c: function() { // ISO-8601 -- UTC format
12427             return [
12428               "this.getUTCFullYear()", "'-'",
12429               "String.leftPad(this.getUTCMonth() + 1, 2, '0')", "'-'",
12430               "String.leftPad(this.getUTCDate(), 2, '0')",
12431               "'T'",
12432               "String.leftPad(this.getUTCHours(), 2, '0')", "':'",
12433               "String.leftPad(this.getUTCMinutes(), 2, '0')", "':'",
12434               "String.leftPad(this.getUTCSeconds(), 2, '0')",
12435               "'Z'"
12436             ].join(" + ");
12437         },
12438         */
12439
12440         U: "Math.round(this.getTime() / 1000)"
12441     },
12442
12443     /**
12444      * Checks if the passed Date parameters will cause a javascript Date "rollover".
12445      * @param {Number} year 4-digit year
12446      * @param {Number} month 1-based month-of-year
12447      * @param {Number} day Day of month
12448      * @param {Number} hour (optional) Hour
12449      * @param {Number} minute (optional) Minute
12450      * @param {Number} second (optional) Second
12451      * @param {Number} millisecond (optional) Millisecond
12452      * @return {Boolean} true if the passed parameters do not cause a Date "rollover", false otherwise.
12453      * @static
12454      */
12455     isValid : function(y, m, d, h, i, s, ms) {
12456         // setup defaults
12457         h = h || 0;
12458         i = i || 0;
12459         s = s || 0;
12460         ms = ms || 0;
12461
12462         // Special handling for year < 100
12463         var dt = new Date(y < 100 ? 100 : y, m - 1, d, h, i, s, ms).add(Date.YEAR, y < 100 ? y - 100 : 0);
12464
12465         return y == dt.getFullYear() &&
12466             m == dt.getMonth() + 1 &&
12467             d == dt.getDate() &&
12468             h == dt.getHours() &&
12469             i == dt.getMinutes() &&
12470             s == dt.getSeconds() &&
12471             ms == dt.getMilliseconds();
12472     },
12473
12474     /**
12475      * Parses the passed string using the specified date format.
12476      * Note that this function expects normal calendar dates, meaning that months are 1-based (i.e. 1 = January).
12477      * The {@link #defaults} hash will be used for any date value (i.e. year, month, day, hour, minute, second or millisecond)
12478      * which cannot be found in the passed string. If a corresponding default date value has not been specified in the {@link #defaults} hash,
12479      * the current date's year, month, day or DST-adjusted zero-hour time value will be used instead.
12480      * Keep in mind that the input date string must precisely match the specified format string
12481      * in order for the parse operation to be successful (failed parse operations return a null value).
12482      * <p>Example:</p><pre><code>
12483 //dt = Fri May 25 2007 (current date)
12484 var dt = new Date();
12485
12486 //dt = Thu May 25 2006 (today&#39;s month/day in 2006)
12487 dt = Date.parseDate("2006", "Y");
12488
12489 //dt = Sun Jan 15 2006 (all date parts specified)
12490 dt = Date.parseDate("2006-01-15", "Y-m-d");
12491
12492 //dt = Sun Jan 15 2006 15:20:01
12493 dt = Date.parseDate("2006-01-15 3:20:01 PM", "Y-m-d g:i:s A");
12494
12495 // attempt to parse Sun Feb 29 2006 03:20:01 in strict mode
12496 dt = Date.parseDate("2006-02-29 03:20:01", "Y-m-d H:i:s", true); // returns null
12497 </code></pre>
12498      * @param {String} input The raw date string.
12499      * @param {String} format The expected date string format.
12500      * @param {Boolean} strict (optional) True to validate date strings while parsing (i.e. prevents javascript Date "rollover")
12501                         (defaults to false). Invalid date strings will return null when parsed.
12502      * @return {Date} The parsed Date.
12503      * @static
12504      */
12505     parseDate : function(input, format, strict) {
12506         var p = Date.parseFunctions;
12507         if (p[format] == null) {
12508             Date.createParser(format);
12509         }
12510         return p[format](input, Ext.isDefined(strict) ? strict : Date.useStrict);
12511     },
12512
12513     // private
12514     getFormatCode : function(character) {
12515         var f = Date.formatCodes[character];
12516
12517         if (f) {
12518           f = typeof f == 'function'? f() : f;
12519           Date.formatCodes[character] = f; // reassign function result to prevent repeated execution
12520         }
12521
12522         // note: unknown characters are treated as literals
12523         return f || ("'" + String.escape(character) + "'");
12524     },
12525
12526     // private
12527     createFormat : function(format) {
12528         var code = [],
12529             special = false,
12530             ch = '';
12531
12532         for (var i = 0; i < format.length; ++i) {
12533             ch = format.charAt(i);
12534             if (!special && ch == "\\") {
12535                 special = true;
12536             } else if (special) {
12537                 special = false;
12538                 code.push("'" + String.escape(ch) + "'");
12539             } else {
12540                 code.push(Date.getFormatCode(ch));
12541             }
12542         }
12543         Date.formatFunctions[format] = new Function("return " + code.join('+'));
12544     },
12545
12546     // private
12547     createParser : function() {
12548         var code = [
12549             "var dt, y, m, d, h, i, s, ms, o, z, zz, u, v,",
12550                 "def = Date.defaults,",
12551                 "results = String(input).match(Date.parseRegexes[{0}]);", // either null, or an array of matched strings
12552
12553             "if(results){",
12554                 "{1}",
12555
12556                 "if(u != null){", // i.e. unix time is defined
12557                     "v = new Date(u * 1000);", // give top priority to UNIX time
12558                 "}else{",
12559                     // create Date object representing midnight of the current day;
12560                     // this will provide us with our date defaults
12561                     // (note: clearTime() handles Daylight Saving Time automatically)
12562                     "dt = (new Date()).clearTime();",
12563
12564                     // date calculations (note: these calculations create a dependency on Ext.num())
12565                     "y = Ext.num(y, Ext.num(def.y, dt.getFullYear()));",
12566                     "m = Ext.num(m, Ext.num(def.m - 1, dt.getMonth()));",
12567                     "d = Ext.num(d, Ext.num(def.d, dt.getDate()));",
12568
12569                     // time calculations (note: these calculations create a dependency on Ext.num())
12570                     "h  = Ext.num(h, Ext.num(def.h, dt.getHours()));",
12571                     "i  = Ext.num(i, Ext.num(def.i, dt.getMinutes()));",
12572                     "s  = Ext.num(s, Ext.num(def.s, dt.getSeconds()));",
12573                     "ms = Ext.num(ms, Ext.num(def.ms, dt.getMilliseconds()));",
12574
12575                     "if(z >= 0 && y >= 0){",
12576                         // both the year and zero-based day of year are defined and >= 0.
12577                         // these 2 values alone provide sufficient info to create a full date object
12578
12579                         // create Date object representing January 1st for the given year
12580                         // handle years < 100 appropriately
12581                         "v = new Date(y < 100 ? 100 : y, 0, 1, h, i, s, ms).add(Date.YEAR, y < 100 ? y - 100 : 0);",
12582
12583                         // then add day of year, checking for Date "rollover" if necessary
12584                         "v = !strict? v : (strict === true && (z <= 364 || (v.isLeapYear() && z <= 365))? v.add(Date.DAY, z) : null);",
12585                     "}else if(strict === true && !Date.isValid(y, m + 1, d, h, i, s, ms)){", // check for Date "rollover"
12586                         "v = null;", // invalid date, so return null
12587                     "}else{",
12588                         // plain old Date object
12589                         // handle years < 100 properly
12590                         "v = new Date(y < 100 ? 100 : y, m, d, h, i, s, ms).add(Date.YEAR, y < 100 ? y - 100 : 0);",
12591                     "}",
12592                 "}",
12593             "}",
12594
12595             "if(v){",
12596                 // favour UTC offset over GMT offset
12597                 "if(zz != null){",
12598                     // reset to UTC, then add offset
12599                     "v = v.add(Date.SECOND, -v.getTimezoneOffset() * 60 - zz);",
12600                 "}else if(o){",
12601                     // reset to GMT, then add offset
12602                     "v = v.add(Date.MINUTE, -v.getTimezoneOffset() + (sn == '+'? -1 : 1) * (hr * 60 + mn));",
12603                 "}",
12604             "}",
12605
12606             "return v;"
12607         ].join('\n');
12608
12609         return function(format) {
12610             var regexNum = Date.parseRegexes.length,
12611                 currentGroup = 1,
12612                 calc = [],
12613                 regex = [],
12614                 special = false,
12615                 ch = "",
12616                 i = 0,
12617                 obj,
12618                 last;
12619
12620             for (; i < format.length; ++i) {
12621                 ch = format.charAt(i);
12622                 if (!special && ch == "\\") {
12623                     special = true;
12624                 } else if (special) {
12625                     special = false;
12626                     regex.push(String.escape(ch));
12627                 } else {
12628                     obj = $f(ch, currentGroup);
12629                     currentGroup += obj.g;
12630                     regex.push(obj.s);
12631                     if (obj.g && obj.c) {
12632                         if (obj.calcLast) {
12633                             last = obj.c;
12634                         } else {
12635                             calc.push(obj.c);
12636                         }
12637                     }
12638                 }
12639             }
12640             
12641             if (last) {
12642                 calc.push(last);
12643             }
12644
12645             Date.parseRegexes[regexNum] = new RegExp("^" + regex.join('') + "$", 'i');
12646             Date.parseFunctions[format] = new Function("input", "strict", xf(code, regexNum, calc.join('')));
12647         };
12648     }(),
12649
12650     // private
12651     parseCodes : {
12652         /*
12653          * Notes:
12654          * g = {Number} calculation group (0 or 1. only group 1 contributes to date calculations.)
12655          * c = {String} calculation method (required for group 1. null for group 0. {0} = currentGroup - position in regex result array)
12656          * s = {String} regex pattern. all matches are stored in results[], and are accessible by the calculation mapped to 'c'
12657          */
12658         d: {
12659             g:1,
12660             c:"d = parseInt(results[{0}], 10);\n",
12661             s:"(\\d{2})" // day of month with leading zeroes (01 - 31)
12662         },
12663         j: {
12664             g:1,
12665             c:"d = parseInt(results[{0}], 10);\n",
12666             s:"(\\d{1,2})" // day of month without leading zeroes (1 - 31)
12667         },
12668         D: function() {
12669             for (var a = [], i = 0; i < 7; a.push(Date.getShortDayName(i)), ++i); // get localised short day names
12670             return {
12671                 g:0,
12672                 c:null,
12673                 s:"(?:" + a.join("|") +")"
12674             };
12675         },
12676         l: function() {
12677             return {
12678                 g:0,
12679                 c:null,
12680                 s:"(?:" + Date.dayNames.join("|") + ")"
12681             };
12682         },
12683         N: {
12684             g:0,
12685             c:null,
12686             s:"[1-7]" // ISO-8601 day number (1 (monday) - 7 (sunday))
12687         },
12688         S: {
12689             g:0,
12690             c:null,
12691             s:"(?:st|nd|rd|th)"
12692         },
12693         w: {
12694             g:0,
12695             c:null,
12696             s:"[0-6]" // javascript day number (0 (sunday) - 6 (saturday))
12697         },
12698         z: {
12699             g:1,
12700             c:"z = parseInt(results[{0}], 10);\n",
12701             s:"(\\d{1,3})" // day of the year (0 - 364 (365 in leap years))
12702         },
12703         W: {
12704             g:0,
12705             c:null,
12706             s:"(?:\\d{2})" // ISO-8601 week number (with leading zero)
12707         },
12708         F: function() {
12709             return {
12710                 g:1,
12711                 c:"m = parseInt(Date.getMonthNumber(results[{0}]), 10);\n", // get localised month number
12712                 s:"(" + Date.monthNames.join("|") + ")"
12713             };
12714         },
12715         M: function() {
12716             for (var a = [], i = 0; i < 12; a.push(Date.getShortMonthName(i)), ++i); // get localised short month names
12717             return Ext.applyIf({
12718                 s:"(" + a.join("|") + ")"
12719             }, $f("F"));
12720         },
12721         m: {
12722             g:1,
12723             c:"m = parseInt(results[{0}], 10) - 1;\n",
12724             s:"(\\d{2})" // month number with leading zeros (01 - 12)
12725         },
12726         n: {
12727             g:1,
12728             c:"m = parseInt(results[{0}], 10) - 1;\n",
12729             s:"(\\d{1,2})" // month number without leading zeros (1 - 12)
12730         },
12731         t: {
12732             g:0,
12733             c:null,
12734             s:"(?:\\d{2})" // no. of days in the month (28 - 31)
12735         },
12736         L: {
12737             g:0,
12738             c:null,
12739             s:"(?:1|0)"
12740         },
12741         o: function() {
12742             return $f("Y");
12743         },
12744         Y: {
12745             g:1,
12746             c:"y = parseInt(results[{0}], 10);\n",
12747             s:"(\\d{4})" // 4-digit year
12748         },
12749         y: {
12750             g:1,
12751             c:"var ty = parseInt(results[{0}], 10);\n"
12752                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n", // 2-digit year
12753             s:"(\\d{1,2})"
12754         },
12755         /**
12756          * In the am/pm parsing routines, we allow both upper and lower case 
12757          * even though it doesn't exactly match the spec. It gives much more flexibility
12758          * in being able to specify case insensitive regexes.
12759          */
12760         a: function(){
12761             return $f("A");
12762         },
12763         A: {
12764             // We need to calculate the hour before we apply AM/PM when parsing
12765             calcLast: true,
12766             g:1,
12767             c:"if (/(am)/i.test(results[{0}])) {\n"
12768                 + "if (!h || h == 12) { h = 0; }\n"
12769                 + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
12770             s:"(AM|PM|am|pm)"
12771         },
12772         g: function() {
12773             return $f("G");
12774         },
12775         G: {
12776             g:1,
12777             c:"h = parseInt(results[{0}], 10);\n",
12778             s:"(\\d{1,2})" // 24-hr format of an hour without leading zeroes (0 - 23)
12779         },
12780         h: function() {
12781             return $f("H");
12782         },
12783         H: {
12784             g:1,
12785             c:"h = parseInt(results[{0}], 10);\n",
12786             s:"(\\d{2})" //  24-hr format of an hour with leading zeroes (00 - 23)
12787         },
12788         i: {
12789             g:1,
12790             c:"i = parseInt(results[{0}], 10);\n",
12791             s:"(\\d{2})" // minutes with leading zeros (00 - 59)
12792         },
12793         s: {
12794             g:1,
12795             c:"s = parseInt(results[{0}], 10);\n",
12796             s:"(\\d{2})" // seconds with leading zeros (00 - 59)
12797         },
12798         u: {
12799             g:1,
12800             c:"ms = results[{0}]; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n",
12801             s:"(\\d+)" // decimal fraction of a second (minimum = 1 digit, maximum = unlimited)
12802         },
12803         O: {
12804             g:1,
12805             c:[
12806                 "o = results[{0}];",
12807                 "var sn = o.substring(0,1),", // get + / - sign
12808                     "hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
12809                     "mn = o.substring(3,5) % 60;", // get minutes
12810                 "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
12811             ].join("\n"),
12812             s: "([+\-]\\d{4})" // GMT offset in hrs and mins
12813         },
12814         P: {
12815             g:1,
12816             c:[
12817                 "o = results[{0}];",
12818                 "var sn = o.substring(0,1),", // get + / - sign
12819                     "hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
12820                     "mn = o.substring(4,6) % 60;", // get minutes
12821                 "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
12822             ].join("\n"),
12823             s: "([+\-]\\d{2}:\\d{2})" // GMT offset in hrs and mins (with colon separator)
12824         },
12825         T: {
12826             g:0,
12827             c:null,
12828             s:"[A-Z]{1,4}" // timezone abbrev. may be between 1 - 4 chars
12829         },
12830         Z: {
12831             g:1,
12832             c:"zz = results[{0}] * 1;\n" // -43200 <= UTC offset <= 50400
12833                   + "zz = (-43200 <= zz && zz <= 50400)? zz : null;\n",
12834             s:"([+\-]?\\d{1,5})" // leading '+' sign is optional for UTC offset
12835         },
12836         c: function() {
12837             var calc = [],
12838                 arr = [
12839                     $f("Y", 1), // year
12840                     $f("m", 2), // month
12841                     $f("d", 3), // day
12842                     $f("h", 4), // hour
12843                     $f("i", 5), // minute
12844                     $f("s", 6), // second
12845                     {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)
12846                     {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
12847                         "if(results[8]) {", // timezone specified
12848                             "if(results[8] == 'Z'){",
12849                                 "zz = 0;", // UTC
12850                             "}else if (results[8].indexOf(':') > -1){",
12851                                 $f("P", 8).c, // timezone offset with colon separator
12852                             "}else{",
12853                                 $f("O", 8).c, // timezone offset without colon separator
12854                             "}",
12855                         "}"
12856                     ].join('\n')}
12857                 ];
12858
12859             for (var i = 0, l = arr.length; i < l; ++i) {
12860                 calc.push(arr[i].c);
12861             }
12862
12863             return {
12864                 g:1,
12865                 c:calc.join(""),
12866                 s:[
12867                     arr[0].s, // year (required)
12868                     "(?:", "-", arr[1].s, // month (optional)
12869                         "(?:", "-", arr[2].s, // day (optional)
12870                             "(?:",
12871                                 "(?:T| )?", // time delimiter -- either a "T" or a single blank space
12872                                 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
12873                                 "(?::", arr[5].s, ")?", // seconds (optional)
12874                                 "(?:(?:\\.|,)(\\d+))?", // decimal fraction of a second (e.g. ",12345" or ".98765") (optional)
12875                                 "(Z|(?:[-+]\\d{2}(?::)?\\d{2}))?", // "Z" (UTC) or "-0530" (UTC offset without colon delimiter) or "+08:00" (UTC offset with colon delimiter) (optional)
12876                             ")?",
12877                         ")?",
12878                     ")?"
12879                 ].join("")
12880             };
12881         },
12882         U: {
12883             g:1,
12884             c:"u = parseInt(results[{0}], 10);\n",
12885             s:"(-?\\d+)" // leading minus sign indicates seconds before UNIX epoch
12886         }
12887     }
12888 });
12889
12890 }());
12891
12892 Ext.apply(Date.prototype, {
12893     // private
12894     dateFormat : function(format) {
12895         if (Date.formatFunctions[format] == null) {
12896             Date.createFormat(format);
12897         }
12898         return Date.formatFunctions[format].call(this);
12899     },
12900
12901     /**
12902      * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
12903      *
12904      * Note: The date string returned by the javascript Date object's toString() method varies
12905      * between browsers (e.g. FF vs IE) and system region settings (e.g. IE in Asia vs IE in America).
12906      * For a given date string e.g. "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)",
12907      * getTimezone() first tries to get the timezone abbreviation from between a pair of parentheses
12908      * (which may or may not be present), failing which it proceeds to get the timezone abbreviation
12909      * from the GMT offset portion of the date string.
12910      * @return {String} The abbreviated timezone name (e.g. 'CST', 'PDT', 'EDT', 'MPST' ...).
12911      */
12912     getTimezone : function() {
12913         // the following list shows the differences between date strings from different browsers on a WinXP SP2 machine from an Asian locale:
12914         //
12915         // Opera  : "Thu, 25 Oct 2007 22:53:45 GMT+0800" -- shortest (weirdest) date string of the lot
12916         // 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)
12917         // FF     : "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)" -- value in parentheses always gives the correct timezone
12918         // IE     : "Thu Oct 25 22:54:35 UTC+0800 2007" -- (Asian system setting) look for 3-4 letter timezone abbrev
12919         // IE     : "Thu Oct 25 17:06:37 PDT 2007" -- (American system setting) look for 3-4 letter timezone abbrev
12920         //
12921         // this crazy regex attempts to guess the correct timezone abbreviation despite these differences.
12922         // step 1: (?:\((.*)\) -- find timezone in parentheses
12923         // 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
12924         // step 3: remove all non uppercase characters found in step 1 and 2
12925         return this.toString().replace(/^.* (?:\((.*)\)|([A-Z]{1,4})(?:[\-+][0-9]{4})?(?: -?\d+)?)$/, "$1$2").replace(/[^A-Z]/g, "");
12926     },
12927
12928     /**
12929      * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
12930      * @param {Boolean} colon (optional) true to separate the hours and minutes with a colon (defaults to false).
12931      * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600').
12932      */
12933     getGMTOffset : function(colon) {
12934         return (this.getTimezoneOffset() > 0 ? "-" : "+")
12935             + String.leftPad(Math.floor(Math.abs(this.getTimezoneOffset()) / 60), 2, "0")
12936             + (colon ? ":" : "")
12937             + String.leftPad(Math.abs(this.getTimezoneOffset() % 60), 2, "0");
12938     },
12939
12940     /**
12941      * Get the numeric day number of the year, adjusted for leap year.
12942      * @return {Number} 0 to 364 (365 in leap years).
12943      */
12944     getDayOfYear: function() {
12945         var num = 0,
12946             d = this.clone(),
12947             m = this.getMonth(),
12948             i;
12949
12950         for (i = 0, d.setDate(1), d.setMonth(0); i < m; d.setMonth(++i)) {
12951             num += d.getDaysInMonth();
12952         }
12953         return num + this.getDate() - 1;
12954     },
12955
12956     /**
12957      * Get the numeric ISO-8601 week number of the year.
12958      * (equivalent to the format specifier 'W', but without a leading zero).
12959      * @return {Number} 1 to 53
12960      */
12961     getWeekOfYear : function() {
12962         // adapted from http://www.merlyn.demon.co.uk/weekcalc.htm
12963         var ms1d = 864e5, // milliseconds in a day
12964             ms7d = 7 * ms1d; // milliseconds in a week
12965
12966         return function() { // return a closure so constants get calculated only once
12967             var DC3 = Date.UTC(this.getFullYear(), this.getMonth(), this.getDate() + 3) / ms1d, // an Absolute Day Number
12968                 AWN = Math.floor(DC3 / 7), // an Absolute Week Number
12969                 Wyr = new Date(AWN * ms7d).getUTCFullYear();
12970
12971             return AWN - Math.floor(Date.UTC(Wyr, 0, 7) / ms7d) + 1;
12972         };
12973     }(),
12974
12975     /**
12976      * Checks if the current date falls within a leap year.
12977      * @return {Boolean} True if the current date falls within a leap year, false otherwise.
12978      */
12979     isLeapYear : function() {
12980         var year = this.getFullYear();
12981         return !!((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
12982     },
12983
12984     /**
12985      * Get the first day of the current month, adjusted for leap year.  The returned value
12986      * is the numeric day index within the week (0-6) which can be used in conjunction with
12987      * the {@link #monthNames} array to retrieve the textual day name.
12988      * Example:
12989      * <pre><code>
12990 var dt = new Date('1/10/2007');
12991 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
12992 </code></pre>
12993      * @return {Number} The day number (0-6).
12994      */
12995     getFirstDayOfMonth : function() {
12996         var day = (this.getDay() - (this.getDate() - 1)) % 7;
12997         return (day < 0) ? (day + 7) : day;
12998     },
12999
13000     /**
13001      * Get the last day of the current month, adjusted for leap year.  The returned value
13002      * is the numeric day index within the week (0-6) which can be used in conjunction with
13003      * the {@link #monthNames} array to retrieve the textual day name.
13004      * Example:
13005      * <pre><code>
13006 var dt = new Date('1/10/2007');
13007 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
13008 </code></pre>
13009      * @return {Number} The day number (0-6).
13010      */
13011     getLastDayOfMonth : function() {
13012         return this.getLastDateOfMonth().getDay();
13013     },
13014
13015
13016     /**
13017      * Get the date of the first day of the month in which this date resides.
13018      * @return {Date}
13019      */
13020     getFirstDateOfMonth : function() {
13021         return new Date(this.getFullYear(), this.getMonth(), 1);
13022     },
13023
13024     /**
13025      * Get the date of the last day of the month in which this date resides.
13026      * @return {Date}
13027      */
13028     getLastDateOfMonth : function() {
13029         return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
13030     },
13031
13032     /**
13033      * Get the number of days in the current month, adjusted for leap year.
13034      * @return {Number} The number of days in the month.
13035      */
13036     getDaysInMonth: function() {
13037         var daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
13038
13039         return function() { // return a closure for efficiency
13040             var m = this.getMonth();
13041
13042             return m == 1 && this.isLeapYear() ? 29 : daysInMonth[m];
13043         };
13044     }(),
13045
13046     /**
13047      * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
13048      * @return {String} 'st, 'nd', 'rd' or 'th'.
13049      */
13050     getSuffix : function() {
13051         switch (this.getDate()) {
13052             case 1:
13053             case 21:
13054             case 31:
13055                 return "st";
13056             case 2:
13057             case 22:
13058                 return "nd";
13059             case 3:
13060             case 23:
13061                 return "rd";
13062             default:
13063                 return "th";
13064         }
13065     },
13066
13067     /**
13068      * Creates and returns a new Date instance with the exact same date value as the called instance.
13069      * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
13070      * variable will also be changed.  When the intention is to create a new variable that will not
13071      * modify the original instance, you should create a clone.
13072      *
13073      * Example of correctly cloning a date:
13074      * <pre><code>
13075 //wrong way:
13076 var orig = new Date('10/1/2006');
13077 var copy = orig;
13078 copy.setDate(5);
13079 document.write(orig);  //returns 'Thu Oct 05 2006'!
13080
13081 //correct way:
13082 var orig = new Date('10/1/2006');
13083 var copy = orig.clone();
13084 copy.setDate(5);
13085 document.write(orig);  //returns 'Thu Oct 01 2006'
13086 </code></pre>
13087      * @return {Date} The new Date instance.
13088      */
13089     clone : function() {
13090         return new Date(this.getTime());
13091     },
13092
13093     /**
13094      * Checks if the current date is affected by Daylight Saving Time (DST).
13095      * @return {Boolean} True if the current date is affected by DST.
13096      */
13097     isDST : function() {
13098         // adapted from http://extjs.com/forum/showthread.php?p=247172#post247172
13099         // courtesy of @geoffrey.mcgill
13100         return new Date(this.getFullYear(), 0, 1).getTimezoneOffset() != this.getTimezoneOffset();
13101     },
13102
13103     /**
13104      * Attempts to clear all time information from this Date by setting the time to midnight of the same day,
13105      * automatically adjusting for Daylight Saving Time (DST) where applicable.
13106      * (note: DST timezone information for the browser's host operating system is assumed to be up-to-date)
13107      * @param {Boolean} clone true to create a clone of this date, clear the time and return it (defaults to false).
13108      * @return {Date} this or the clone.
13109      */
13110     clearTime : function(clone) {
13111         if (clone) {
13112             return this.clone().clearTime();
13113         }
13114
13115         // get current date before clearing time
13116         var d = this.getDate();
13117
13118         // clear time
13119         this.setHours(0);
13120         this.setMinutes(0);
13121         this.setSeconds(0);
13122         this.setMilliseconds(0);
13123
13124         if (this.getDate() != d) { // account for DST (i.e. day of month changed when setting hour = 0)
13125             // note: DST adjustments are assumed to occur in multiples of 1 hour (this is almost always the case)
13126             // refer to http://www.timeanddate.com/time/aboutdst.html for the (rare) exceptions to this rule
13127
13128             // increment hour until cloned date == current date
13129             for (var hr = 1, c = this.add(Date.HOUR, hr); c.getDate() != d; hr++, c = this.add(Date.HOUR, hr));
13130
13131             this.setDate(d);
13132             this.setHours(c.getHours());
13133         }
13134
13135         return this;
13136     },
13137
13138     /**
13139      * Provides a convenient method for performing basic date arithmetic. This method
13140      * does not modify the Date instance being called - it creates and returns
13141      * a new Date instance containing the resulting date value.
13142      *
13143      * Examples:
13144      * <pre><code>
13145 // Basic usage:
13146 var dt = new Date('10/29/2006').add(Date.DAY, 5);
13147 document.write(dt); //returns 'Fri Nov 03 2006 00:00:00'
13148
13149 // Negative values will be subtracted:
13150 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
13151 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
13152
13153 // You can even chain several calls together in one line:
13154 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
13155 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
13156 </code></pre>
13157      *
13158      * @param {String} interval A valid date interval enum value.
13159      * @param {Number} value The amount to add to the current date.
13160      * @return {Date} The new Date instance.
13161      */
13162     add : function(interval, value) {
13163         var d = this.clone();
13164         if (!interval || value === 0) return d;
13165
13166         switch(interval.toLowerCase()) {
13167             case Date.MILLI:
13168                 d.setMilliseconds(this.getMilliseconds() + value);
13169                 break;
13170             case Date.SECOND:
13171                 d.setSeconds(this.getSeconds() + value);
13172                 break;
13173             case Date.MINUTE:
13174                 d.setMinutes(this.getMinutes() + value);
13175                 break;
13176             case Date.HOUR:
13177                 d.setHours(this.getHours() + value);
13178                 break;
13179             case Date.DAY:
13180                 d.setDate(this.getDate() + value);
13181                 break;
13182             case Date.MONTH:
13183                 var day = this.getDate();
13184                 if (day > 28) {
13185                     day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
13186                 }
13187                 d.setDate(day);
13188                 d.setMonth(this.getMonth() + value);
13189                 break;
13190             case Date.YEAR:
13191                 d.setFullYear(this.getFullYear() + value);
13192                 break;
13193         }
13194         return d;
13195     },
13196
13197     /**
13198      * Checks if this date falls on or between the given start and end dates.
13199      * @param {Date} start Start date
13200      * @param {Date} end End date
13201      * @return {Boolean} true if this date falls on or between the given start and end dates.
13202      */
13203     between : function(start, end) {
13204         var t = this.getTime();
13205         return start.getTime() <= t && t <= end.getTime();
13206     }
13207 });
13208
13209
13210 /**
13211  * Formats a date given the supplied format string.
13212  * @param {String} format The format string.
13213  * @return {String} The formatted date.
13214  * @method format
13215  */
13216 Date.prototype.format = Date.prototype.dateFormat;
13217
13218
13219 // private
13220 if (Ext.isSafari && (navigator.userAgent.match(/WebKit\/(\d+)/)[1] || NaN) < 420) {
13221     Ext.apply(Date.prototype, {
13222         _xMonth : Date.prototype.setMonth,
13223         _xDate  : Date.prototype.setDate,
13224
13225         // Bug in Safari 1.3, 2.0 (WebKit build < 420)
13226         // Date.setMonth does not work consistently if iMonth is not 0-11
13227         setMonth : function(num) {
13228             if (num <= -1) {
13229                 var n = Math.ceil(-num),
13230                     back_year = Math.ceil(n / 12),
13231                     month = (n % 12) ? 12 - n % 12 : 0;
13232
13233                 this.setFullYear(this.getFullYear() - back_year);
13234
13235                 return this._xMonth(month);
13236             } else {
13237                 return this._xMonth(num);
13238             }
13239         },
13240
13241         // Bug in setDate() method (resolved in WebKit build 419.3, so to be safe we target Webkit builds < 420)
13242         // The parameter for Date.setDate() is converted to a signed byte integer in Safari
13243         // http://brianary.blogspot.com/2006/03/safari-date-bug.html
13244         setDate : function(d) {
13245             // use setTime() to workaround setDate() bug
13246             // subtract current day of month in milliseconds, then add desired day of month in milliseconds
13247             return this.setTime(this.getTime() - (this.getDate() - d) * 864e5);
13248         }
13249     });
13250 }
13251
13252
13253
13254 /* Some basic Date tests... (requires Firebug)
13255
13256 Date.parseDate('', 'c'); // call Date.parseDate() once to force computation of regex string so we can console.log() it
13257 console.log('Insane Regex for "c" format: %o', Date.parseCodes.c.s); // view the insane regex for the "c" format specifier
13258
13259 // standard tests
13260 console.group('Standard Date.parseDate() Tests');
13261     console.log('Date.parseDate("2009-01-05T11:38:56", "c")               = %o', Date.parseDate("2009-01-05T11:38:56", "c")); // assumes browser's timezone setting
13262     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
13263     console.log('Date.parseDate("2009-03-03T13:36:54,101000Z", "c")       = %o', Date.parseDate("2009-03-03T13:36:54,101000Z", "c")); // UTC
13264     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
13265     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
13266 console.groupEnd();
13267
13268 // ISO-8601 format as specified in http://www.w3.org/TR/NOTE-datetime
13269 // -- accepts ALL 6 levels of date-time granularity
13270 console.group('ISO-8601 Granularity Test (see http://www.w3.org/TR/NOTE-datetime)');
13271     console.log('Date.parseDate("1997", "c")                              = %o', Date.parseDate("1997", "c")); // YYYY (e.g. 1997)
13272     console.log('Date.parseDate("1997-07", "c")                           = %o', Date.parseDate("1997-07", "c")); // YYYY-MM (e.g. 1997-07)
13273     console.log('Date.parseDate("1997-07-16", "c")                        = %o', Date.parseDate("1997-07-16", "c")); // YYYY-MM-DD (e.g. 1997-07-16)
13274     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)
13275     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)
13276     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)
13277     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)
13278     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
13279 console.groupEnd();
13280
13281 */
13282 /**
13283  * @class Ext.util.MixedCollection
13284  * @extends Ext.util.Observable
13285  * A Collection class that maintains both numeric indexes and keys and exposes events.
13286  * @constructor
13287  * @param {Boolean} allowFunctions Specify <tt>true</tt> if the {@link #addAll}
13288  * function should add function references to the collection. Defaults to
13289  * <tt>false</tt>.
13290  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
13291  * and return the key value for that item.  This is used when available to look up the key on items that
13292  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
13293  * equivalent to providing an implementation for the {@link #getKey} method.
13294  */
13295 Ext.util.MixedCollection = function(allowFunctions, keyFn){
13296     this.items = [];
13297     this.map = {};
13298     this.keys = [];
13299     this.length = 0;
13300     this.addEvents(
13301         /**
13302          * @event clear
13303          * Fires when the collection is cleared.
13304          */
13305         'clear',
13306         /**
13307          * @event add
13308          * Fires when an item is added to the collection.
13309          * @param {Number} index The index at which the item was added.
13310          * @param {Object} o The item added.
13311          * @param {String} key The key associated with the added item.
13312          */
13313         'add',
13314         /**
13315          * @event replace
13316          * Fires when an item is replaced in the collection.
13317          * @param {String} key he key associated with the new added.
13318          * @param {Object} old The item being replaced.
13319          * @param {Object} new The new item.
13320          */
13321         'replace',
13322         /**
13323          * @event remove
13324          * Fires when an item is removed from the collection.
13325          * @param {Object} o The item being removed.
13326          * @param {String} key (optional) The key associated with the removed item.
13327          */
13328         'remove',
13329         'sort'
13330     );
13331     this.allowFunctions = allowFunctions === true;
13332     if(keyFn){
13333         this.getKey = keyFn;
13334     }
13335     Ext.util.MixedCollection.superclass.constructor.call(this);
13336 };
13337
13338 Ext.extend(Ext.util.MixedCollection, Ext.util.Observable, {
13339
13340     /**
13341      * @cfg {Boolean} allowFunctions Specify <tt>true</tt> if the {@link #addAll}
13342      * function should add function references to the collection. Defaults to
13343      * <tt>false</tt>.
13344      */
13345     allowFunctions : false,
13346
13347     /**
13348      * Adds an item to the collection. Fires the {@link #add} event when complete.
13349      * @param {String} key <p>The key to associate with the item, or the new item.</p>
13350      * <p>If a {@link #getKey} implementation was specified for this MixedCollection,
13351      * or if the key of the stored items is in a property called <tt><b>id</b></tt>,
13352      * the MixedCollection will be able to <i>derive</i> the key for the new item.
13353      * In this case just pass the new item in this parameter.</p>
13354      * @param {Object} o The item to add.
13355      * @return {Object} The item added.
13356      */
13357     add : function(key, o){
13358         if(arguments.length == 1){
13359             o = arguments[0];
13360             key = this.getKey(o);
13361         }
13362         if(typeof key != 'undefined' && key !== null){
13363             var old = this.map[key];
13364             if(typeof old != 'undefined'){
13365                 return this.replace(key, o);
13366             }
13367             this.map[key] = o;
13368         }
13369         this.length++;
13370         this.items.push(o);
13371         this.keys.push(key);
13372         this.fireEvent('add', this.length-1, o, key);
13373         return o;
13374     },
13375
13376     /**
13377       * MixedCollection has a generic way to fetch keys if you implement getKey.  The default implementation
13378       * simply returns <b><code>item.id</code></b> but you can provide your own implementation
13379       * to return a different value as in the following examples:<pre><code>
13380 // normal way
13381 var mc = new Ext.util.MixedCollection();
13382 mc.add(someEl.dom.id, someEl);
13383 mc.add(otherEl.dom.id, otherEl);
13384 //and so on
13385
13386 // using getKey
13387 var mc = new Ext.util.MixedCollection();
13388 mc.getKey = function(el){
13389    return el.dom.id;
13390 };
13391 mc.add(someEl);
13392 mc.add(otherEl);
13393
13394 // or via the constructor
13395 var mc = new Ext.util.MixedCollection(false, function(el){
13396    return el.dom.id;
13397 });
13398 mc.add(someEl);
13399 mc.add(otherEl);
13400      * </code></pre>
13401      * @param {Object} item The item for which to find the key.
13402      * @return {Object} The key for the passed item.
13403      */
13404     getKey : function(o){
13405          return o.id;
13406     },
13407
13408     /**
13409      * Replaces an item in the collection. Fires the {@link #replace} event when complete.
13410      * @param {String} key <p>The key associated with the item to replace, or the replacement item.</p>
13411      * <p>If you supplied a {@link #getKey} implementation for this MixedCollection, or if the key
13412      * of your stored items is in a property called <tt><b>id</b></tt>, then the MixedCollection
13413      * will be able to <i>derive</i> the key of the replacement item. If you want to replace an item
13414      * with one having the same key value, then just pass the replacement item in this parameter.</p>
13415      * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate
13416      * with that key.
13417      * @return {Object}  The new item.
13418      */
13419     replace : function(key, o){
13420         if(arguments.length == 1){
13421             o = arguments[0];
13422             key = this.getKey(o);
13423         }
13424         var old = this.map[key];
13425         if(typeof key == 'undefined' || key === null || typeof old == 'undefined'){
13426              return this.add(key, o);
13427         }
13428         var index = this.indexOfKey(key);
13429         this.items[index] = o;
13430         this.map[key] = o;
13431         this.fireEvent('replace', key, old, o);
13432         return o;
13433     },
13434
13435     /**
13436      * Adds all elements of an Array or an Object to the collection.
13437      * @param {Object/Array} objs An Object containing properties which will be added
13438      * to the collection, or an Array of values, each of which are added to the collection.
13439      * Functions references will be added to the collection if <code>{@link #allowFunctions}</code>
13440      * has been set to <tt>true</tt>.
13441      */
13442     addAll : function(objs){
13443         if(arguments.length > 1 || Ext.isArray(objs)){
13444             var args = arguments.length > 1 ? arguments : objs;
13445             for(var i = 0, len = args.length; i < len; i++){
13446                 this.add(args[i]);
13447             }
13448         }else{
13449             for(var key in objs){
13450                 if(this.allowFunctions || typeof objs[key] != 'function'){
13451                     this.add(key, objs[key]);
13452                 }
13453             }
13454         }
13455     },
13456
13457     /**
13458      * Executes the specified function once for every item in the collection, passing the following arguments:
13459      * <div class="mdetail-params"><ul>
13460      * <li><b>item</b> : Mixed<p class="sub-desc">The collection item</p></li>
13461      * <li><b>index</b> : Number<p class="sub-desc">The item's index</p></li>
13462      * <li><b>length</b> : Number<p class="sub-desc">The total number of items in the collection</p></li>
13463      * </ul></div>
13464      * The function should return a boolean value. Returning false from the function will stop the iteration.
13465      * @param {Function} fn The function to execute for each item.
13466      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the current item in the iteration.
13467      */
13468     each : function(fn, scope){
13469         var items = [].concat(this.items); // each safe for removal
13470         for(var i = 0, len = items.length; i < len; i++){
13471             if(fn.call(scope || items[i], items[i], i, len) === false){
13472                 break;
13473             }
13474         }
13475     },
13476
13477     /**
13478      * Executes the specified function once for every key in the collection, passing each
13479      * key, and its associated item as the first two parameters.
13480      * @param {Function} fn The function to execute for each item.
13481      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
13482      */
13483     eachKey : function(fn, scope){
13484         for(var i = 0, len = this.keys.length; i < len; i++){
13485             fn.call(scope || window, this.keys[i], this.items[i], i, len);
13486         }
13487     },
13488
13489     /**
13490      * Returns the first item in the collection which elicits a true return value from the
13491      * passed selection function.
13492      * @param {Function} fn The selection function to execute for each item.
13493      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
13494      * @return {Object} The first item in the collection which returned true from the selection function.
13495      */
13496     find : function(fn, scope){
13497         for(var i = 0, len = this.items.length; i < len; i++){
13498             if(fn.call(scope || window, this.items[i], this.keys[i])){
13499                 return this.items[i];
13500             }
13501         }
13502         return null;
13503     },
13504
13505     /**
13506      * Inserts an item at the specified index in the collection. Fires the {@link #add} event when complete.
13507      * @param {Number} index The index to insert the item at.
13508      * @param {String} key The key to associate with the new item, or the item itself.
13509      * @param {Object} o (optional) If the second parameter was a key, the new item.
13510      * @return {Object} The item inserted.
13511      */
13512     insert : function(index, key, o){
13513         if(arguments.length == 2){
13514             o = arguments[1];
13515             key = this.getKey(o);
13516         }
13517         if(this.containsKey(key)){
13518             this.suspendEvents();
13519             this.removeKey(key);
13520             this.resumeEvents();
13521         }
13522         if(index >= this.length){
13523             return this.add(key, o);
13524         }
13525         this.length++;
13526         this.items.splice(index, 0, o);
13527         if(typeof key != 'undefined' && key !== null){
13528             this.map[key] = o;
13529         }
13530         this.keys.splice(index, 0, key);
13531         this.fireEvent('add', index, o, key);
13532         return o;
13533     },
13534
13535     /**
13536      * Remove an item from the collection.
13537      * @param {Object} o The item to remove.
13538      * @return {Object} The item removed or false if no item was removed.
13539      */
13540     remove : function(o){
13541         return this.removeAt(this.indexOf(o));
13542     },
13543
13544     /**
13545      * Remove an item from a specified index in the collection. Fires the {@link #remove} event when complete.
13546      * @param {Number} index The index within the collection of the item to remove.
13547      * @return {Object} The item removed or false if no item was removed.
13548      */
13549     removeAt : function(index){
13550         if(index < this.length && index >= 0){
13551             this.length--;
13552             var o = this.items[index];
13553             this.items.splice(index, 1);
13554             var key = this.keys[index];
13555             if(typeof key != 'undefined'){
13556                 delete this.map[key];
13557             }
13558             this.keys.splice(index, 1);
13559             this.fireEvent('remove', o, key);
13560             return o;
13561         }
13562         return false;
13563     },
13564
13565     /**
13566      * Removed an item associated with the passed key fom the collection.
13567      * @param {String} key The key of the item to remove.
13568      * @return {Object} The item removed or false if no item was removed.
13569      */
13570     removeKey : function(key){
13571         return this.removeAt(this.indexOfKey(key));
13572     },
13573
13574     /**
13575      * Returns the number of items in the collection.
13576      * @return {Number} the number of items in the collection.
13577      */
13578     getCount : function(){
13579         return this.length;
13580     },
13581
13582     /**
13583      * Returns index within the collection of the passed Object.
13584      * @param {Object} o The item to find the index of.
13585      * @return {Number} index of the item. Returns -1 if not found.
13586      */
13587     indexOf : function(o){
13588         return this.items.indexOf(o);
13589     },
13590
13591     /**
13592      * Returns index within the collection of the passed key.
13593      * @param {String} key The key to find the index of.
13594      * @return {Number} index of the key.
13595      */
13596     indexOfKey : function(key){
13597         return this.keys.indexOf(key);
13598     },
13599
13600     /**
13601      * Returns the item associated with the passed key OR index.
13602      * Key has priority over index.  This is the equivalent
13603      * of calling {@link #key} first, then if nothing matched calling {@link #itemAt}.
13604      * @param {String/Number} key The key or index of the item.
13605      * @return {Object} If the item is found, returns the item.  If the item was not found, returns <tt>undefined</tt>.
13606      * If an item was found, but is a Class, returns <tt>null</tt>.
13607      */
13608     item : function(key){
13609         var mk = this.map[key],
13610             item = mk !== undefined ? mk : (typeof key == 'number') ? this.items[key] : undefined;
13611         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13612     },
13613
13614     /**
13615      * Returns the item at the specified index.
13616      * @param {Number} index The index of the item.
13617      * @return {Object} The item at the specified index.
13618      */
13619     itemAt : function(index){
13620         return this.items[index];
13621     },
13622
13623     /**
13624      * Returns the item associated with the passed key.
13625      * @param {String/Number} key The key of the item.
13626      * @return {Object} The item associated with the passed key.
13627      */
13628     key : function(key){
13629         return this.map[key];
13630     },
13631
13632     /**
13633      * Returns true if the collection contains the passed Object as an item.
13634      * @param {Object} o  The Object to look for in the collection.
13635      * @return {Boolean} True if the collection contains the Object as an item.
13636      */
13637     contains : function(o){
13638         return this.indexOf(o) != -1;
13639     },
13640
13641     /**
13642      * Returns true if the collection contains the passed Object as a key.
13643      * @param {String} key The key to look for in the collection.
13644      * @return {Boolean} True if the collection contains the Object as a key.
13645      */
13646     containsKey : function(key){
13647         return typeof this.map[key] != 'undefined';
13648     },
13649
13650     /**
13651      * Removes all items from the collection.  Fires the {@link #clear} event when complete.
13652      */
13653     clear : function(){
13654         this.length = 0;
13655         this.items = [];
13656         this.keys = [];
13657         this.map = {};
13658         this.fireEvent('clear');
13659     },
13660
13661     /**
13662      * Returns the first item in the collection.
13663      * @return {Object} the first item in the collection..
13664      */
13665     first : function(){
13666         return this.items[0];
13667     },
13668
13669     /**
13670      * Returns the last item in the collection.
13671      * @return {Object} the last item in the collection..
13672      */
13673     last : function(){
13674         return this.items[this.length-1];
13675     },
13676
13677     /**
13678      * @private
13679      * Performs the actual sorting based on a direction and a sorting function. Internally,
13680      * this creates a temporary array of all items in the MixedCollection, sorts it and then writes
13681      * the sorted array data back into this.items and this.keys
13682      * @param {String} property Property to sort by ('key', 'value', or 'index')
13683      * @param {String} dir (optional) Direction to sort 'ASC' or 'DESC'. Defaults to 'ASC'.
13684      * @param {Function} fn (optional) Comparison function that defines the sort order.
13685      * Defaults to sorting by numeric value.
13686      */
13687     _sort : function(property, dir, fn){
13688         var i, len,
13689             dsc   = String(dir).toUpperCase() == 'DESC' ? -1 : 1,
13690
13691             //this is a temporary array used to apply the sorting function
13692             c     = [],
13693             keys  = this.keys,
13694             items = this.items;
13695
13696         //default to a simple sorter function if one is not provided
13697         fn = fn || function(a, b) {
13698             return a - b;
13699         };
13700
13701         //copy all the items into a temporary array, which we will sort
13702         for(i = 0, len = items.length; i < len; i++){
13703             c[c.length] = {
13704                 key  : keys[i],
13705                 value: items[i],
13706                 index: i
13707             };
13708         }
13709
13710         //sort the temporary array
13711         c.sort(function(a, b){
13712             var v = fn(a[property], b[property]) * dsc;
13713             if(v === 0){
13714                 v = (a.index < b.index ? -1 : 1);
13715             }
13716             return v;
13717         });
13718
13719         //copy the temporary array back into the main this.items and this.keys objects
13720         for(i = 0, len = c.length; i < len; i++){
13721             items[i] = c[i].value;
13722             keys[i]  = c[i].key;
13723         }
13724
13725         this.fireEvent('sort', this);
13726     },
13727
13728     /**
13729      * Sorts this collection by <b>item</b> value with the passed comparison function.
13730      * @param {String} direction (optional) 'ASC' or 'DESC'. Defaults to 'ASC'.
13731      * @param {Function} fn (optional) Comparison function that defines the sort order.
13732      * Defaults to sorting by numeric value.
13733      */
13734     sort : function(dir, fn){
13735         this._sort('value', dir, fn);
13736     },
13737
13738     /**
13739      * Reorders each of the items based on a mapping from old index to new index. Internally this
13740      * just translates into a sort. The 'sort' event is fired whenever reordering has occured.
13741      * @param {Object} mapping Mapping from old item index to new item index
13742      */
13743     reorder: function(mapping) {
13744         this.suspendEvents();
13745
13746         var items = this.items,
13747             index = 0,
13748             length = items.length,
13749             order = [],
13750             remaining = [],
13751             oldIndex;
13752
13753         //object of {oldPosition: newPosition} reversed to {newPosition: oldPosition}
13754         for (oldIndex in mapping) {
13755             order[mapping[oldIndex]] = items[oldIndex];
13756         }
13757
13758         for (index = 0; index < length; index++) {
13759             if (mapping[index] == undefined) {
13760                 remaining.push(items[index]);
13761             }
13762         }
13763
13764         for (index = 0; index < length; index++) {
13765             if (order[index] == undefined) {
13766                 order[index] = remaining.shift();
13767             }
13768         }
13769
13770         this.clear();
13771         this.addAll(order);
13772
13773         this.resumeEvents();
13774         this.fireEvent('sort', this);
13775     },
13776
13777     /**
13778      * Sorts this collection by <b>key</b>s.
13779      * @param {String} direction (optional) 'ASC' or 'DESC'. Defaults to 'ASC'.
13780      * @param {Function} fn (optional) Comparison function that defines the sort order.
13781      * Defaults to sorting by case insensitive string.
13782      */
13783     keySort : function(dir, fn){
13784         this._sort('key', dir, fn || function(a, b){
13785             var v1 = String(a).toUpperCase(), v2 = String(b).toUpperCase();
13786             return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13787         });
13788     },
13789
13790     /**
13791      * Returns a range of items in this collection
13792      * @param {Number} startIndex (optional) The starting index. Defaults to 0.
13793      * @param {Number} endIndex (optional) The ending index. Defaults to the last item.
13794      * @return {Array} An array of items
13795      */
13796     getRange : function(start, end){
13797         var items = this.items;
13798         if(items.length < 1){
13799             return [];
13800         }
13801         start = start || 0;
13802         end = Math.min(typeof end == 'undefined' ? this.length-1 : end, this.length-1);
13803         var i, r = [];
13804         if(start <= end){
13805             for(i = start; i <= end; i++) {
13806                 r[r.length] = items[i];
13807             }
13808         }else{
13809             for(i = start; i >= end; i--) {
13810                 r[r.length] = items[i];
13811             }
13812         }
13813         return r;
13814     },
13815
13816     /**
13817      * Filter the <i>objects</i> in this collection by a specific property.
13818      * Returns a new collection that has been filtered.
13819      * @param {String} property A property on your objects
13820      * @param {String/RegExp} value Either string that the property values
13821      * should start with or a RegExp to test against the property
13822      * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning
13823      * @param {Boolean} caseSensitive (optional) True for case sensitive comparison (defaults to False).
13824      * @return {MixedCollection} The new filtered collection
13825      */
13826     filter : function(property, value, anyMatch, caseSensitive){
13827         if(Ext.isEmpty(value, false)){
13828             return this.clone();
13829         }
13830         value = this.createValueMatcher(value, anyMatch, caseSensitive);
13831         return this.filterBy(function(o){
13832             return o && value.test(o[property]);
13833         });
13834     },
13835
13836     /**
13837      * Filter by a function. Returns a <i>new</i> collection that has been filtered.
13838      * The passed function will be called with each object in the collection.
13839      * If the function returns true, the value is included otherwise it is filtered.
13840      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13841      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this MixedCollection.
13842      * @return {MixedCollection} The new filtered collection
13843      */
13844     filterBy : function(fn, scope){
13845         var r = new Ext.util.MixedCollection();
13846         r.getKey = this.getKey;
13847         var k = this.keys, it = this.items;
13848         for(var i = 0, len = it.length; i < len; i++){
13849             if(fn.call(scope||this, it[i], k[i])){
13850                 r.add(k[i], it[i]);
13851             }
13852         }
13853         return r;
13854     },
13855
13856     /**
13857      * Finds the index of the first matching object in this collection by a specific property/value.
13858      * @param {String} property The name of a property on your objects.
13859      * @param {String/RegExp} value A string that the property values
13860      * should start with or a RegExp to test against the property.
13861      * @param {Number} start (optional) The index to start searching at (defaults to 0).
13862      * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning.
13863      * @param {Boolean} caseSensitive (optional) True for case sensitive comparison.
13864      * @return {Number} The matched index or -1
13865      */
13866     findIndex : function(property, value, start, anyMatch, caseSensitive){
13867         if(Ext.isEmpty(value, false)){
13868             return -1;
13869         }
13870         value = this.createValueMatcher(value, anyMatch, caseSensitive);
13871         return this.findIndexBy(function(o){
13872             return o && value.test(o[property]);
13873         }, null, start);
13874     },
13875
13876     /**
13877      * Find the index of the first matching object in this collection by a function.
13878      * If the function returns <i>true</i> it is considered a match.
13879      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key).
13880      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this MixedCollection.
13881      * @param {Number} start (optional) The index to start searching at (defaults to 0).
13882      * @return {Number} The matched index or -1
13883      */
13884     findIndexBy : function(fn, scope, start){
13885         var k = this.keys, it = this.items;
13886         for(var i = (start||0), len = it.length; i < len; i++){
13887             if(fn.call(scope||this, it[i], k[i])){
13888                 return i;
13889             }
13890         }
13891         return -1;
13892     },
13893
13894     /**
13895      * Returns a regular expression based on the given value and matching options. This is used internally for finding and filtering,
13896      * and by Ext.data.Store#filter
13897      * @private
13898      * @param {String} value The value to create the regex for. This is escaped using Ext.escapeRe
13899      * @param {Boolean} anyMatch True to allow any match - no regex start/end line anchors will be added. Defaults to false
13900      * @param {Boolean} caseSensitive True to make the regex case sensitive (adds 'i' switch to regex). Defaults to false.
13901      * @param {Boolean} exactMatch True to force exact match (^ and $ characters added to the regex). Defaults to false. Ignored if anyMatch is true.
13902      */
13903     createValueMatcher : function(value, anyMatch, caseSensitive, exactMatch) {
13904         if (!value.exec) { // not a regex
13905             var er = Ext.escapeRe;
13906             value = String(value);
13907
13908             if (anyMatch === true) {
13909                 value = er(value);
13910             } else {
13911                 value = '^' + er(value);
13912                 if (exactMatch === true) {
13913                     value += '$';
13914                 }
13915             }
13916             value = new RegExp(value, caseSensitive ? '' : 'i');
13917          }
13918          return value;
13919     },
13920
13921     /**
13922      * Creates a shallow copy of this collection
13923      * @return {MixedCollection}
13924      */
13925     clone : function(){
13926         var r = new Ext.util.MixedCollection();
13927         var k = this.keys, it = this.items;
13928         for(var i = 0, len = it.length; i < len; i++){
13929             r.add(k[i], it[i]);
13930         }
13931         r.getKey = this.getKey;
13932         return r;
13933     }
13934 });
13935 /**
13936  * This method calls {@link #item item()}.
13937  * Returns the item associated with the passed key OR index. Key has priority
13938  * over index.  This is the equivalent of calling {@link #key} first, then if
13939  * nothing matched calling {@link #itemAt}.
13940  * @param {String/Number} key The key or index of the item.
13941  * @return {Object} If the item is found, returns the item.  If the item was
13942  * not found, returns <tt>undefined</tt>. If an item was found, but is a Class,
13943  * returns <tt>null</tt>.
13944  */
13945 Ext.util.MixedCollection.prototype.get = Ext.util.MixedCollection.prototype.item;
13946 /**
13947  * @class Ext.AbstractManager
13948  * @extends Object
13949  * Base Manager class - extended by ComponentMgr and PluginMgr
13950  */
13951 Ext.AbstractManager = Ext.extend(Object, {
13952     typeName: 'type',
13953     
13954     constructor: function(config) {
13955         Ext.apply(this, config || {});
13956         
13957         /**
13958          * Contains all of the items currently managed
13959          * @property all
13960          * @type Ext.util.MixedCollection
13961          */
13962         this.all = new Ext.util.MixedCollection();
13963         
13964         this.types = {};
13965     },
13966     
13967     /**
13968      * Returns a component by {@link Ext.Component#id id}.
13969      * For additional details see {@link Ext.util.MixedCollection#get}.
13970      * @param {String} id The component {@link Ext.Component#id id}
13971      * @return Ext.Component The Component, <code>undefined</code> if not found, or <code>null</code> if a
13972      * Class was found.
13973      */
13974     get : function(id){
13975         return this.all.get(id);
13976     },
13977     
13978     /**
13979      * Registers an item to be managed
13980      * @param {Mixed} item The item to register
13981      */
13982     register: function(item) {
13983         this.all.add(item);
13984     },
13985     
13986     /**
13987      * Unregisters a component by removing it from this manager
13988      * @param {Mixed} item The item to unregister
13989      */
13990     unregister: function(item) {
13991         this.all.remove(item);        
13992     },
13993     
13994     /**
13995      * <p>Registers a new Component constructor, keyed by a new
13996      * {@link Ext.Component#xtype}.</p>
13997      * <p>Use this method (or its alias {@link Ext#reg Ext.reg}) to register new
13998      * subclasses of {@link Ext.Component} so that lazy instantiation may be used when specifying
13999      * child Components.
14000      * see {@link Ext.Container#items}</p>
14001      * @param {String} xtype The mnemonic string by which the Component class may be looked up.
14002      * @param {Constructor} cls The new Component class.
14003      */
14004     registerType : function(type, cls){
14005         this.types[type] = cls;
14006         cls[this.typeName] = type;
14007     },
14008     
14009     /**
14010      * Checks if a Component type is registered.
14011      * @param {Ext.Component} xtype The mnemonic string by which the Component class may be looked up
14012      * @return {Boolean} Whether the type is registered.
14013      */
14014     isRegistered : function(type){
14015         return this.types[type] !== undefined;    
14016     },
14017     
14018     /**
14019      * Creates and returns an instance of whatever this manager manages, based on the supplied type and config object
14020      * @param {Object} config The config object
14021      * @param {String} defaultType If no type is discovered in the config object, we fall back to this type
14022      * @return {Mixed} The instance of whatever this manager is managing
14023      */
14024     create: function(config, defaultType) {
14025         var type        = config[this.typeName] || config.type || defaultType,
14026             Constructor = this.types[type];
14027         
14028         if (Constructor == undefined) {
14029             throw new Error(String.format("The '{0}' type has not been registered with this manager", type));
14030         }
14031         
14032         return new Constructor(config);
14033     },
14034     
14035     /**
14036      * Registers a function that will be called when a Component with the specified id is added to the manager. This will happen on instantiation.
14037      * @param {String} id The component {@link Ext.Component#id id}
14038      * @param {Function} fn The callback function
14039      * @param {Object} scope The scope (<code>this</code> reference) in which the callback is executed. Defaults to the Component.
14040      */
14041     onAvailable : function(id, fn, scope){
14042         var all = this.all;
14043         
14044         all.on("add", function(index, o){
14045             if (o.id == id) {
14046                 fn.call(scope || o, o);
14047                 all.un("add", fn, scope);
14048             }
14049         });
14050     }
14051 });/**
14052  * @class Ext.util.Format
14053  * Reusable data formatting functions
14054  * @singleton
14055  */
14056 Ext.util.Format = function() {
14057     var trimRe         = /^\s+|\s+$/g,
14058         stripTagsRE    = /<\/?[^>]+>/gi,
14059         stripScriptsRe = /(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig,
14060         nl2brRe        = /\r?\n/g;
14061
14062     return {
14063         /**
14064          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
14065          * @param {String} value The string to truncate
14066          * @param {Number} length The maximum length to allow before truncating
14067          * @param {Boolean} word True to try to find a common work break
14068          * @return {String} The converted text
14069          */
14070         ellipsis : function(value, len, word) {
14071             if (value && value.length > len) {
14072                 if (word) {
14073                     var vs    = value.substr(0, len - 2),
14074                         index = Math.max(vs.lastIndexOf(' '), vs.lastIndexOf('.'), vs.lastIndexOf('!'), vs.lastIndexOf('?'));
14075                     if (index == -1 || index < (len - 15)) {
14076                         return value.substr(0, len - 3) + "...";
14077                     } else {
14078                         return vs.substr(0, index) + "...";
14079                     }
14080                 } else {
14081                     return value.substr(0, len - 3) + "...";
14082                 }
14083             }
14084             return value;
14085         },
14086
14087         /**
14088          * Checks a reference and converts it to empty string if it is undefined
14089          * @param {Mixed} value Reference to check
14090          * @return {Mixed} Empty string if converted, otherwise the original value
14091          */
14092         undef : function(value) {
14093             return value !== undefined ? value : "";
14094         },
14095
14096         /**
14097          * Checks a reference and converts it to the default value if it's empty
14098          * @param {Mixed} value Reference to check
14099          * @param {String} defaultValue The value to insert of it's undefined (defaults to "")
14100          * @return {String}
14101          */
14102         defaultValue : function(value, defaultValue) {
14103             return value !== undefined && value !== '' ? value : defaultValue;
14104         },
14105
14106         /**
14107          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
14108          * @param {String} value The string to encode
14109          * @return {String} The encoded text
14110          */
14111         htmlEncode : function(value) {
14112             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
14113         },
14114
14115         /**
14116          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
14117          * @param {String} value The string to decode
14118          * @return {String} The decoded text
14119          */
14120         htmlDecode : function(value) {
14121             return !value ? value : String(value).replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"').replace(/&amp;/g, "&");
14122         },
14123
14124         /**
14125          * Trims any whitespace from either side of a string
14126          * @param {String} value The text to trim
14127          * @return {String} The trimmed text
14128          */
14129         trim : function(value) {
14130             return String(value).replace(trimRe, "");
14131         },
14132
14133         /**
14134          * Returns a substring from within an original string
14135          * @param {String} value The original text
14136          * @param {Number} start The start index of the substring
14137          * @param {Number} length The length of the substring
14138          * @return {String} The substring
14139          */
14140         substr : function(value, start, length) {
14141             return String(value).substr(start, length);
14142         },
14143
14144         /**
14145          * Converts a string to all lower case letters
14146          * @param {String} value The text to convert
14147          * @return {String} The converted text
14148          */
14149         lowercase : function(value) {
14150             return String(value).toLowerCase();
14151         },
14152
14153         /**
14154          * Converts a string to all upper case letters
14155          * @param {String} value The text to convert
14156          * @return {String} The converted text
14157          */
14158         uppercase : function(value) {
14159             return String(value).toUpperCase();
14160         },
14161
14162         /**
14163          * Converts the first character only of a string to upper case
14164          * @param {String} value The text to convert
14165          * @return {String} The converted text
14166          */
14167         capitalize : function(value) {
14168             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
14169         },
14170
14171         // private
14172         call : function(value, fn) {
14173             if (arguments.length > 2) {
14174                 var args = Array.prototype.slice.call(arguments, 2);
14175                 args.unshift(value);
14176                 return eval(fn).apply(window, args);
14177             } else {
14178                 return eval(fn).call(window, value);
14179             }
14180         },
14181
14182         /**
14183          * Format a number as US currency
14184          * @param {Number/String} value The numeric value to format
14185          * @return {String} The formatted currency string
14186          */
14187         usMoney : function(v) {
14188             v = (Math.round((v-0)*100))/100;
14189             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
14190             v = String(v);
14191             var ps = v.split('.'),
14192                 whole = ps[0],
14193                 sub = ps[1] ? '.'+ ps[1] : '.00',
14194                 r = /(\d+)(\d{3})/;
14195             while (r.test(whole)) {
14196                 whole = whole.replace(r, '$1' + ',' + '$2');
14197             }
14198             v = whole + sub;
14199             if (v.charAt(0) == '-') {
14200                 return '-$' + v.substr(1);
14201             }
14202             return "$" +  v;
14203         },
14204
14205         /**
14206          * Parse a value into a formatted date using the specified format pattern.
14207          * @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)
14208          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
14209          * @return {String} The formatted date string
14210          */
14211         date : function(v, format) {
14212             if (!v) {
14213                 return "";
14214             }
14215             if (!Ext.isDate(v)) {
14216                 v = new Date(Date.parse(v));
14217             }
14218             return v.dateFormat(format || "m/d/Y");
14219         },
14220
14221         /**
14222          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
14223          * @param {String} format Any valid date format string
14224          * @return {Function} The date formatting function
14225          */
14226         dateRenderer : function(format) {
14227             return function(v) {
14228                 return Ext.util.Format.date(v, format);
14229             };
14230         },
14231
14232         /**
14233          * Strips all HTML tags
14234          * @param {Mixed} value The text from which to strip tags
14235          * @return {String} The stripped text
14236          */
14237         stripTags : function(v) {
14238             return !v ? v : String(v).replace(stripTagsRE, "");
14239         },
14240
14241         /**
14242          * Strips all script tags
14243          * @param {Mixed} value The text from which to strip script tags
14244          * @return {String} The stripped text
14245          */
14246         stripScripts : function(v) {
14247             return !v ? v : String(v).replace(stripScriptsRe, "");
14248         },
14249
14250         /**
14251          * Simple format for a file size (xxx bytes, xxx KB, xxx MB)
14252          * @param {Number/String} size The numeric value to format
14253          * @return {String} The formatted file size
14254          */
14255         fileSize : function(size) {
14256             if (size < 1024) {
14257                 return size + " bytes";
14258             } else if (size < 1048576) {
14259                 return (Math.round(((size*10) / 1024))/10) + " KB";
14260             } else {
14261                 return (Math.round(((size*10) / 1048576))/10) + " MB";
14262             }
14263         },
14264
14265         /**
14266          * It does simple math for use in a template, for example:<pre><code>
14267          * var tpl = new Ext.Template('{value} * 10 = {value:math("* 10")}');
14268          * </code></pre>
14269          * @return {Function} A function that operates on the passed value.
14270          */
14271         math : function(){
14272             var fns = {};
14273             
14274             return function(v, a){
14275                 if (!fns[a]) {
14276                     fns[a] = new Function('v', 'return v ' + a + ';');
14277                 }
14278                 return fns[a](v);
14279             };
14280         }(),
14281
14282         /**
14283          * Rounds the passed number to the required decimal precision.
14284          * @param {Number/String} value The numeric value to round.
14285          * @param {Number} precision The number of decimal places to which to round the first parameter's value.
14286          * @return {Number} The rounded value.
14287          */
14288         round : function(value, precision) {
14289             var result = Number(value);
14290             if (typeof precision == 'number') {
14291                 precision = Math.pow(10, precision);
14292                 result = Math.round(value * precision) / precision;
14293             }
14294             return result;
14295         },
14296
14297         /**
14298          * Formats the number according to the format string.
14299          * <div style="margin-left:40px">examples (123456.789):
14300          * <div style="margin-left:10px">
14301          * 0 - (123456) show only digits, no precision<br>
14302          * 0.00 - (123456.78) show only digits, 2 precision<br>
14303          * 0.0000 - (123456.7890) show only digits, 4 precision<br>
14304          * 0,000 - (123,456) show comma and digits, no precision<br>
14305          * 0,000.00 - (123,456.78) show comma and digits, 2 precision<br>
14306          * 0,0.00 - (123,456.78) shortcut method, show comma and digits, 2 precision<br>
14307          * To reverse the grouping (,) and decimal (.) for international numbers, add /i to the end.
14308          * For example: 0.000,00/i
14309          * </div></div>
14310          * @param {Number} v The number to format.
14311          * @param {String} format The way you would like to format this text.
14312          * @return {String} The formatted number.
14313          */
14314         number: function(v, format) {
14315             if (!format) {
14316                 return v;
14317             }
14318             v = Ext.num(v, NaN);
14319             if (isNaN(v)) {
14320                 return '';
14321             }
14322             var comma = ',',
14323                 dec   = '.',
14324                 i18n  = false,
14325                 neg   = v < 0;
14326
14327             v = Math.abs(v);
14328             if (format.substr(format.length - 2) == '/i') {
14329                 format = format.substr(0, format.length - 2);
14330                 i18n   = true;
14331                 comma  = '.';
14332                 dec    = ',';
14333             }
14334
14335             var hasComma = format.indexOf(comma) != -1,
14336                 psplit   = (i18n ? format.replace(/[^\d\,]/g, '') : format.replace(/[^\d\.]/g, '')).split(dec);
14337
14338             if (1 < psplit.length) {
14339                 v = v.toFixed(psplit[1].length);
14340             } else if(2 < psplit.length) {
14341                 throw ('NumberFormatException: invalid format, formats should have no more than 1 period: ' + format);
14342             } else {
14343                 v = v.toFixed(0);
14344             }
14345
14346             var fnum = v.toString();
14347
14348             psplit = fnum.split('.');
14349
14350             if (hasComma) {
14351                 var cnum = psplit[0], 
14352                     parr = [], 
14353                     j    = cnum.length, 
14354                     m    = Math.floor(j / 3),
14355                     n    = cnum.length % 3 || 3,
14356                     i;
14357
14358                 for (i = 0; i < j; i += n) {
14359                     if (i != 0) {
14360                         n = 3;
14361                     }
14362                     
14363                     parr[parr.length] = cnum.substr(i, n);
14364                     m -= 1;
14365                 }
14366                 fnum = parr.join(comma);
14367                 if (psplit[1]) {
14368                     fnum += dec + psplit[1];
14369                 }
14370             } else {
14371                 if (psplit[1]) {
14372                     fnum = psplit[0] + dec + psplit[1];
14373                 }
14374             }
14375
14376             return (neg ? '-' : '') + format.replace(/[\d,?\.?]+/, fnum);
14377         },
14378
14379         /**
14380          * Returns a number rendering function that can be reused to apply a number format multiple times efficiently
14381          * @param {String} format Any valid number format string for {@link #number}
14382          * @return {Function} The number formatting function
14383          */
14384         numberRenderer : function(format) {
14385             return function(v) {
14386                 return Ext.util.Format.number(v, format);
14387             };
14388         },
14389
14390         /**
14391          * Selectively do a plural form of a word based on a numeric value. For example, in a template,
14392          * {commentCount:plural("Comment")}  would result in "1 Comment" if commentCount was 1 or would be "x Comments"
14393          * if the value is 0 or greater than 1.
14394          * @param {Number} value The value to compare against
14395          * @param {String} singular The singular form of the word
14396          * @param {String} plural (optional) The plural form of the word (defaults to the singular with an "s")
14397          */
14398         plural : function(v, s, p) {
14399             return v +' ' + (v == 1 ? s : (p ? p : s+'s'));
14400         },
14401
14402         /**
14403          * Converts newline characters to the HTML tag &lt;br/>
14404          * @param {String} The string value to format.
14405          * @return {String} The string with embedded &lt;br/> tags in place of newlines.
14406          */
14407         nl2br : function(v) {
14408             return Ext.isEmpty(v) ? '' : v.replace(nl2brRe, '<br/>');
14409         }
14410     };
14411 }();
14412 /**
14413  * @class Ext.XTemplate
14414  * @extends Ext.Template
14415  * <p>A template class that supports advanced functionality like:<div class="mdetail-params"><ul>
14416  * <li>Autofilling arrays using templates and sub-templates</li>
14417  * <li>Conditional processing with basic comparison operators</li>
14418  * <li>Basic math function support</li>
14419  * <li>Execute arbitrary inline code with special built-in template variables</li>
14420  * <li>Custom member functions</li>
14421  * <li>Many special tags and built-in operators that aren't defined as part of
14422  * the API, but are supported in the templates that can be created</li>
14423  * </ul></div></p>
14424  * <p>XTemplate provides the templating mechanism built into:<div class="mdetail-params"><ul>
14425  * <li>{@link Ext.DataView}</li>
14426  * <li>{@link Ext.ListView}</li>
14427  * <li>{@link Ext.form.ComboBox}</li>
14428  * <li>{@link Ext.grid.TemplateColumn}</li>
14429  * <li>{@link Ext.grid.GroupingView}</li>
14430  * <li>{@link Ext.menu.Item}</li>
14431  * <li>{@link Ext.layout.MenuLayout}</li>
14432  * <li>{@link Ext.ColorPalette}</li>
14433  * </ul></div></p>
14434  *
14435  * <p>For example usage {@link #XTemplate see the constructor}.</p>
14436  *
14437  * @constructor
14438  * The {@link Ext.Template#Template Ext.Template constructor} describes
14439  * the acceptable parameters to pass to the constructor. The following
14440  * examples demonstrate all of the supported features.</p>
14441  *
14442  * <div class="mdetail-params"><ul>
14443  *
14444  * <li><b><u>Sample Data</u></b>
14445  * <div class="sub-desc">
14446  * <p>This is the data object used for reference in each code example:</p>
14447  * <pre><code>
14448 var data = {
14449     name: 'Jack Slocum',
14450     title: 'Lead Developer',
14451     company: 'Ext JS, LLC',
14452     email: 'jack@extjs.com',
14453     address: '4 Red Bulls Drive',
14454     city: 'Cleveland',
14455     state: 'Ohio',
14456     zip: '44102',
14457     drinks: ['Red Bull', 'Coffee', 'Water'],
14458     kids: [{
14459         name: 'Sara Grace',
14460         age:3
14461     },{
14462         name: 'Zachary',
14463         age:2
14464     },{
14465         name: 'John James',
14466         age:0
14467     }]
14468 };
14469  * </code></pre>
14470  * </div>
14471  * </li>
14472  *
14473  *
14474  * <li><b><u>Auto filling of arrays</u></b>
14475  * <div class="sub-desc">
14476  * <p>The <b><tt>tpl</tt></b> tag and the <b><tt>for</tt></b> operator are used
14477  * to process the provided data object:
14478  * <ul>
14479  * <li>If the value specified in <tt>for</tt> is an array, it will auto-fill,
14480  * repeating the template block inside the <tt>tpl</tt> tag for each item in the
14481  * array.</li>
14482  * <li>If <tt>for="."</tt> is specified, the data object provided is examined.</li>
14483  * <li>While processing an array, the special variable <tt>{#}</tt>
14484  * will provide the current array index + 1 (starts at 1, not 0).</li>
14485  * </ul>
14486  * </p>
14487  * <pre><code>
14488 &lt;tpl <b>for</b>=".">...&lt;/tpl>       // loop through array at root node
14489 &lt;tpl <b>for</b>="foo">...&lt;/tpl>     // loop through array at foo node
14490 &lt;tpl <b>for</b>="foo.bar">...&lt;/tpl> // loop through array at foo.bar node
14491  * </code></pre>
14492  * Using the sample data above:
14493  * <pre><code>
14494 var tpl = new Ext.XTemplate(
14495     '&lt;p>Kids: ',
14496     '&lt;tpl <b>for</b>=".">',       // process the data.kids node
14497         '&lt;p>{#}. {name}&lt;/p>',  // use current array index to autonumber
14498     '&lt;/tpl>&lt;/p>'
14499 );
14500 tpl.overwrite(panel.body, data.kids); // pass the kids property of the data object
14501  * </code></pre>
14502  * <p>An example illustrating how the <b><tt>for</tt></b> property can be leveraged
14503  * to access specified members of the provided data object to populate the template:</p>
14504  * <pre><code>
14505 var tpl = new Ext.XTemplate(
14506     '&lt;p>Name: {name}&lt;/p>',
14507     '&lt;p>Title: {title}&lt;/p>',
14508     '&lt;p>Company: {company}&lt;/p>',
14509     '&lt;p>Kids: ',
14510     '&lt;tpl <b>for="kids"</b>>',     // interrogate the kids property within the data
14511         '&lt;p>{name}&lt;/p>',
14512     '&lt;/tpl>&lt;/p>'
14513 );
14514 tpl.overwrite(panel.body, data);  // pass the root node of the data object
14515  * </code></pre>
14516  * <p>Flat arrays that contain values (and not objects) can be auto-rendered
14517  * using the special <b><tt>{.}</tt></b> variable inside a loop.  This variable
14518  * will represent the value of the array at the current index:</p>
14519  * <pre><code>
14520 var tpl = new Ext.XTemplate(
14521     '&lt;p>{name}\&#39;s favorite beverages:&lt;/p>',
14522     '&lt;tpl for="drinks">',
14523        '&lt;div> - {.}&lt;/div>',
14524     '&lt;/tpl>'
14525 );
14526 tpl.overwrite(panel.body, data);
14527  * </code></pre>
14528  * <p>When processing a sub-template, for example while looping through a child array,
14529  * you can access the parent object's members via the <b><tt>parent</tt></b> object:</p>
14530  * <pre><code>
14531 var tpl = new Ext.XTemplate(
14532     '&lt;p>Name: {name}&lt;/p>',
14533     '&lt;p>Kids: ',
14534     '&lt;tpl for="kids">',
14535         '&lt;tpl if="age > 1">',
14536             '&lt;p>{name}&lt;/p>',
14537             '&lt;p>Dad: {<b>parent</b>.name}&lt;/p>',
14538         '&lt;/tpl>',
14539     '&lt;/tpl>&lt;/p>'
14540 );
14541 tpl.overwrite(panel.body, data);
14542  * </code></pre>
14543  * </div>
14544  * </li>
14545  *
14546  *
14547  * <li><b><u>Conditional processing with basic comparison operators</u></b>
14548  * <div class="sub-desc">
14549  * <p>The <b><tt>tpl</tt></b> tag and the <b><tt>if</tt></b> operator are used
14550  * to provide conditional checks for deciding whether or not to render specific
14551  * parts of the template. Notes:<div class="sub-desc"><ul>
14552  * <li>Double quotes must be encoded if used within the conditional</li>
14553  * <li>There is no <tt>else</tt> operator &mdash; if needed, two opposite
14554  * <tt>if</tt> statements should be used.</li>
14555  * </ul></div>
14556  * <pre><code>
14557 &lt;tpl if="age &gt; 1 &amp;&amp; age &lt; 10">Child&lt;/tpl>
14558 &lt;tpl if="age >= 10 && age < 18">Teenager&lt;/tpl>
14559 &lt;tpl <b>if</b>="this.isGirl(name)">...&lt;/tpl>
14560 &lt;tpl <b>if</b>="id==\'download\'">...&lt;/tpl>
14561 &lt;tpl <b>if</b>="needsIcon">&lt;img src="{icon}" class="{iconCls}"/>&lt;/tpl>
14562 // no good:
14563 &lt;tpl if="name == "Jack"">Hello&lt;/tpl>
14564 // encode &#34; if it is part of the condition, e.g.
14565 &lt;tpl if="name == &#38;quot;Jack&#38;quot;">Hello&lt;/tpl>
14566  * </code></pre>
14567  * Using the sample data above:
14568  * <pre><code>
14569 var tpl = new Ext.XTemplate(
14570     '&lt;p>Name: {name}&lt;/p>',
14571     '&lt;p>Kids: ',
14572     '&lt;tpl for="kids">',
14573         '&lt;tpl if="age > 1">',
14574             '&lt;p>{name}&lt;/p>',
14575         '&lt;/tpl>',
14576     '&lt;/tpl>&lt;/p>'
14577 );
14578 tpl.overwrite(panel.body, data);
14579  * </code></pre>
14580  * </div>
14581  * </li>
14582  *
14583  *
14584  * <li><b><u>Basic math support</u></b>
14585  * <div class="sub-desc">
14586  * <p>The following basic math operators may be applied directly on numeric
14587  * data values:</p><pre>
14588  * + - * /
14589  * </pre>
14590  * For example:
14591  * <pre><code>
14592 var tpl = new Ext.XTemplate(
14593     '&lt;p>Name: {name}&lt;/p>',
14594     '&lt;p>Kids: ',
14595     '&lt;tpl for="kids">',
14596         '&lt;tpl if="age &amp;gt; 1">',  // <-- Note that the &gt; is encoded
14597             '&lt;p>{#}: {name}&lt;/p>',  // <-- Auto-number each item
14598             '&lt;p>In 5 Years: {age+5}&lt;/p>',  // <-- Basic math
14599             '&lt;p>Dad: {parent.name}&lt;/p>',
14600         '&lt;/tpl>',
14601     '&lt;/tpl>&lt;/p>'
14602 );
14603 tpl.overwrite(panel.body, data);
14604 </code></pre>
14605  * </div>
14606  * </li>
14607  *
14608  *
14609  * <li><b><u>Execute arbitrary inline code with special built-in template variables</u></b>
14610  * <div class="sub-desc">
14611  * <p>Anything between <code>{[ ... ]}</code> is considered code to be executed
14612  * in the scope of the template. There are some special variables available in that code:
14613  * <ul>
14614  * <li><b><tt>values</tt></b>: The values in the current scope. If you are using
14615  * scope changing sub-templates, you can change what <tt>values</tt> is.</li>
14616  * <li><b><tt>parent</tt></b>: The scope (values) of the ancestor template.</li>
14617  * <li><b><tt>xindex</tt></b>: If you are in a looping template, the index of the
14618  * loop you are in (1-based).</li>
14619  * <li><b><tt>xcount</tt></b>: If you are in a looping template, the total length
14620  * of the array you are looping.</li>
14621  * <li><b><tt>fm</tt></b>: An alias for <tt>Ext.util.Format</tt>.</li>
14622  * </ul>
14623  * This example demonstrates basic row striping using an inline code block and the
14624  * <tt>xindex</tt> variable:</p>
14625  * <pre><code>
14626 var tpl = new Ext.XTemplate(
14627     '&lt;p>Name: {name}&lt;/p>',
14628     '&lt;p>Company: {[values.company.toUpperCase() + ", " + values.title]}&lt;/p>',
14629     '&lt;p>Kids: ',
14630     '&lt;tpl for="kids">',
14631        '&lt;div class="{[xindex % 2 === 0 ? "even" : "odd"]}">',
14632         '{name}',
14633         '&lt;/div>',
14634     '&lt;/tpl>&lt;/p>'
14635 );
14636 tpl.overwrite(panel.body, data);
14637  * </code></pre>
14638  * </div>
14639  * </li>
14640  *
14641  * <li><b><u>Template member functions</u></b>
14642  * <div class="sub-desc">
14643  * <p>One or more member functions can be specified in a configuration
14644  * object passed into the XTemplate constructor for more complex processing:</p>
14645  * <pre><code>
14646 var tpl = new Ext.XTemplate(
14647     '&lt;p>Name: {name}&lt;/p>',
14648     '&lt;p>Kids: ',
14649     '&lt;tpl for="kids">',
14650         '&lt;tpl if="this.isGirl(name)">',
14651             '&lt;p>Girl: {name} - {age}&lt;/p>',
14652         '&lt;/tpl>',
14653         // use opposite if statement to simulate 'else' processing:
14654         '&lt;tpl if="this.isGirl(name) == false">',
14655             '&lt;p>Boy: {name} - {age}&lt;/p>',
14656         '&lt;/tpl>',
14657         '&lt;tpl if="this.isBaby(age)">',
14658             '&lt;p>{name} is a baby!&lt;/p>',
14659         '&lt;/tpl>',
14660     '&lt;/tpl>&lt;/p>',
14661     {
14662         // XTemplate configuration:
14663         compiled: true,
14664         disableFormats: true,
14665         // member functions:
14666         isGirl: function(name){
14667             return name == 'Sara Grace';
14668         },
14669         isBaby: function(age){
14670             return age < 1;
14671         }
14672     }
14673 );
14674 tpl.overwrite(panel.body, data);
14675  * </code></pre>
14676  * </div>
14677  * </li>
14678  *
14679  * </ul></div>
14680  *
14681  * @param {Mixed} config
14682  */
14683 Ext.XTemplate = function(){
14684     Ext.XTemplate.superclass.constructor.apply(this, arguments);
14685
14686     var me = this,
14687         s = me.html,
14688         re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
14689         nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
14690         ifRe = /^<tpl\b[^>]*?if="(.*?)"/,
14691         execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
14692         m,
14693         id = 0,
14694         tpls = [],
14695         VALUES = 'values',
14696         PARENT = 'parent',
14697         XINDEX = 'xindex',
14698         XCOUNT = 'xcount',
14699         RETURN = 'return ',
14700         WITHVALUES = 'with(values){ ';
14701
14702     s = ['<tpl>', s, '</tpl>'].join('');
14703
14704     while((m = s.match(re))){
14705         var m2 = m[0].match(nameRe),
14706             m3 = m[0].match(ifRe),
14707             m4 = m[0].match(execRe),
14708             exp = null,
14709             fn = null,
14710             exec = null,
14711             name = m2 && m2[1] ? m2[1] : '';
14712
14713        if (m3) {
14714            exp = m3 && m3[1] ? m3[1] : null;
14715            if(exp){
14716                fn = new Function(VALUES, PARENT, XINDEX, XCOUNT, WITHVALUES + RETURN +(Ext.util.Format.htmlDecode(exp))+'; }');
14717            }
14718        }
14719        if (m4) {
14720            exp = m4 && m4[1] ? m4[1] : null;
14721            if(exp){
14722                exec = new Function(VALUES, PARENT, XINDEX, XCOUNT, WITHVALUES +(Ext.util.Format.htmlDecode(exp))+'; }');
14723            }
14724        }
14725        if(name){
14726            switch(name){
14727                case '.': name = new Function(VALUES, PARENT, WITHVALUES + RETURN + VALUES + '; }'); break;
14728                case '..': name = new Function(VALUES, PARENT, WITHVALUES + RETURN + PARENT + '; }'); break;
14729                default: name = new Function(VALUES, PARENT, WITHVALUES + RETURN + name + '; }');
14730            }
14731        }
14732        tpls.push({
14733             id: id,
14734             target: name,
14735             exec: exec,
14736             test: fn,
14737             body: m[1]||''
14738         });
14739        s = s.replace(m[0], '{xtpl'+ id + '}');
14740        ++id;
14741     }
14742     for(var i = tpls.length-1; i >= 0; --i){
14743         me.compileTpl(tpls[i]);
14744     }
14745     me.master = tpls[tpls.length-1];
14746     me.tpls = tpls;
14747 };
14748 Ext.extend(Ext.XTemplate, Ext.Template, {
14749     // private
14750     re : /\{([\w-\.\#]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?(\s?[\+\-\*\\]\s?[\d\.\+\-\*\\\(\)]+)?\}/g,
14751     // private
14752     codeRe : /\{\[((?:\\\]|.|\n)*?)\]\}/g,
14753
14754     // private
14755     applySubTemplate : function(id, values, parent, xindex, xcount){
14756         var me = this,
14757             len,
14758             t = me.tpls[id],
14759             vs,
14760             buf = [];
14761         if ((t.test && !t.test.call(me, values, parent, xindex, xcount)) ||
14762             (t.exec && t.exec.call(me, values, parent, xindex, xcount))) {
14763             return '';
14764         }
14765         vs = t.target ? t.target.call(me, values, parent) : values;
14766         len = vs.length;
14767         parent = t.target ? values : parent;
14768         if(t.target && Ext.isArray(vs)){
14769             for(var i = 0, len = vs.length; i < len; i++){
14770                 buf[buf.length] = t.compiled.call(me, vs[i], parent, i+1, len);
14771             }
14772             return buf.join('');
14773         }
14774         return t.compiled.call(me, vs, parent, xindex, xcount);
14775     },
14776
14777     // private
14778     compileTpl : function(tpl){
14779         var fm = Ext.util.Format,
14780             useF = this.disableFormats !== true,
14781             sep = Ext.isGecko ? "+" : ",",
14782             body;
14783
14784         function fn(m, name, format, args, math){
14785             if(name.substr(0, 4) == 'xtpl'){
14786                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent, xindex, xcount)'+sep+"'";
14787             }
14788             var v;
14789             if(name === '.'){
14790                 v = 'values';
14791             }else if(name === '#'){
14792                 v = 'xindex';
14793             }else if(name.indexOf('.') != -1){
14794                 v = name;
14795             }else{
14796                 v = "values['" + name + "']";
14797             }
14798             if(math){
14799                 v = '(' + v + math + ')';
14800             }
14801             if (format && useF) {
14802                 args = args ? ',' + args : "";
14803                 if(format.substr(0, 5) != "this."){
14804                     format = "fm." + format + '(';
14805                 }else{
14806                     format = 'this.call("'+ format.substr(5) + '", ';
14807                     args = ", values";
14808                 }
14809             } else {
14810                 args= ''; format = "("+v+" === undefined ? '' : ";
14811             }
14812             return "'"+ sep + format + v + args + ")"+sep+"'";
14813         }
14814
14815         function codeFn(m, code){
14816             // Single quotes get escaped when the template is compiled, however we want to undo this when running code.
14817             return "'" + sep + '(' + code.replace(/\\'/g, "'") + ')' + sep + "'";
14818         }
14819
14820         // branched to use + in gecko and [].join() in others
14821         if(Ext.isGecko){
14822             body = "tpl.compiled = function(values, parent, xindex, xcount){ return '" +
14823                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn).replace(this.codeRe, codeFn) +
14824                     "';};";
14825         }else{
14826             body = ["tpl.compiled = function(values, parent, xindex, xcount){ return ['"];
14827             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn).replace(this.codeRe, codeFn));
14828             body.push("'].join('');};");
14829             body = body.join('');
14830         }
14831         eval(body);
14832         return this;
14833     },
14834
14835     /**
14836      * Returns an HTML fragment of this template with the specified values applied.
14837      * @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'})
14838      * @return {String} The HTML fragment
14839      */
14840     applyTemplate : function(values){
14841         return this.master.compiled.call(this, values, {}, 1, 1);
14842     },
14843
14844     /**
14845      * Compile the template to a function for optimized performance.  Recommended if the template will be used frequently.
14846      * @return {Function} The compiled function
14847      */
14848     compile : function(){return this;}
14849
14850     /**
14851      * @property re
14852      * @hide
14853      */
14854     /**
14855      * @property disableFormats
14856      * @hide
14857      */
14858     /**
14859      * @method set
14860      * @hide
14861      */
14862
14863 });
14864 /**
14865  * Alias for {@link #applyTemplate}
14866  * Returns an HTML fragment of this template with the specified values applied.
14867  * @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'})
14868  * @return {String} The HTML fragment
14869  * @member Ext.XTemplate
14870  * @method apply
14871  */
14872 Ext.XTemplate.prototype.apply = Ext.XTemplate.prototype.applyTemplate;
14873
14874 /**
14875  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
14876  * @param {String/HTMLElement} el A DOM element or its id
14877  * @return {Ext.Template} The created template
14878  * @static
14879  */
14880 Ext.XTemplate.from = function(el){
14881     el = Ext.getDom(el);
14882     return new Ext.XTemplate(el.value || el.innerHTML);
14883 };
14884 /**
14885  * @class Ext.util.CSS
14886  * Utility class for manipulating CSS rules
14887  * @singleton
14888  */
14889 Ext.util.CSS = function(){
14890         var rules = null;
14891         var doc = document;
14892
14893     var camelRe = /(-[a-z])/gi;
14894     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
14895
14896    return {
14897    /**
14898     * Creates a stylesheet from a text blob of rules.
14899     * These rules will be wrapped in a STYLE tag and appended to the HEAD of the document.
14900     * @param {String} cssText The text containing the css rules
14901     * @param {String} id An id to add to the stylesheet for later removal
14902     * @return {StyleSheet}
14903     */
14904    createStyleSheet : function(cssText, id){
14905        var ss;
14906        var head = doc.getElementsByTagName("head")[0];
14907        var rules = doc.createElement("style");
14908        rules.setAttribute("type", "text/css");
14909        if(id){
14910            rules.setAttribute("id", id);
14911        }
14912        if(Ext.isIE){
14913            head.appendChild(rules);
14914            ss = rules.styleSheet;
14915            ss.cssText = cssText;
14916        }else{
14917            try{
14918                 rules.appendChild(doc.createTextNode(cssText));
14919            }catch(e){
14920                rules.cssText = cssText;
14921            }
14922            head.appendChild(rules);
14923            ss = rules.styleSheet ? rules.styleSheet : (rules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
14924        }
14925        this.cacheStyleSheet(ss);
14926        return ss;
14927    },
14928
14929    /**
14930     * Removes a style or link tag by id
14931     * @param {String} id The id of the tag
14932     */
14933    removeStyleSheet : function(id){
14934        var existing = doc.getElementById(id);
14935        if(existing){
14936            existing.parentNode.removeChild(existing);
14937        }
14938    },
14939
14940    /**
14941     * Dynamically swaps an existing stylesheet reference for a new one
14942     * @param {String} id The id of an existing link tag to remove
14943     * @param {String} url The href of the new stylesheet to include
14944     */
14945    swapStyleSheet : function(id, url){
14946        this.removeStyleSheet(id);
14947        var ss = doc.createElement("link");
14948        ss.setAttribute("rel", "stylesheet");
14949        ss.setAttribute("type", "text/css");
14950        ss.setAttribute("id", id);
14951        ss.setAttribute("href", url);
14952        doc.getElementsByTagName("head")[0].appendChild(ss);
14953    },
14954    
14955    /**
14956     * Refresh the rule cache if you have dynamically added stylesheets
14957     * @return {Object} An object (hash) of rules indexed by selector
14958     */
14959    refreshCache : function(){
14960        return this.getRules(true);
14961    },
14962
14963    // private
14964    cacheStyleSheet : function(ss){
14965        if(!rules){
14966            rules = {};
14967        }
14968        try{// try catch for cross domain access issue
14969            var ssRules = ss.cssRules || ss.rules;
14970            for(var j = ssRules.length-1; j >= 0; --j){
14971                rules[ssRules[j].selectorText.toLowerCase()] = ssRules[j];
14972            }
14973        }catch(e){}
14974    },
14975    
14976    /**
14977     * Gets all css rules for the document
14978     * @param {Boolean} refreshCache true to refresh the internal cache
14979     * @return {Object} An object (hash) of rules indexed by selector
14980     */
14981    getRules : function(refreshCache){
14982                 if(rules === null || refreshCache){
14983                         rules = {};
14984                         var ds = doc.styleSheets;
14985                         for(var i =0, len = ds.length; i < len; i++){
14986                             try{
14987                         this.cacheStyleSheet(ds[i]);
14988                     }catch(e){} 
14989                 }
14990                 }
14991                 return rules;
14992         },
14993         
14994         /**
14995     * Gets an an individual CSS rule by selector(s)
14996     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14997     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14998     * @return {CSSRule} The CSS rule or null if one is not found
14999     */
15000    getRule : function(selector, refreshCache){
15001                 var rs = this.getRules(refreshCache);
15002                 if(!Ext.isArray(selector)){
15003                     return rs[selector.toLowerCase()];
15004                 }
15005                 for(var i = 0; i < selector.length; i++){
15006                         if(rs[selector[i]]){
15007                                 return rs[selector[i].toLowerCase()];
15008                         }
15009                 }
15010                 return null;
15011         },
15012         
15013         
15014         /**
15015     * Updates a rule property
15016     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
15017     * @param {String} property The css property
15018     * @param {String} value The new value for the property
15019     * @return {Boolean} true If a rule was found and updated
15020     */
15021    updateRule : function(selector, property, value){
15022                 if(!Ext.isArray(selector)){
15023                         var rule = this.getRule(selector);
15024                         if(rule){
15025                                 rule.style[property.replace(camelRe, camelFn)] = value;
15026                                 return true;
15027                         }
15028                 }else{
15029                         for(var i = 0; i < selector.length; i++){
15030                                 if(this.updateRule(selector[i], property, value)){
15031                                         return true;
15032                                 }
15033                         }
15034                 }
15035                 return false;
15036         }
15037    };   
15038 }();/**
15039  @class Ext.util.ClickRepeater
15040  @extends Ext.util.Observable
15041
15042  A wrapper class which can be applied to any element. Fires a "click" event while the
15043  mouse is pressed. The interval between firings may be specified in the config but
15044  defaults to 20 milliseconds.
15045
15046  Optionally, a CSS class may be applied to the element during the time it is pressed.
15047
15048  @cfg {Mixed} el The element to act as a button.
15049  @cfg {Number} delay The initial delay before the repeating event begins firing.
15050  Similar to an autorepeat key delay.
15051  @cfg {Number} interval The interval between firings of the "click" event. Default 20 ms.
15052  @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
15053  @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
15054            "interval" and "delay" are ignored.
15055  @cfg {Boolean} preventDefault True to prevent the default click event
15056  @cfg {Boolean} stopDefault True to stop the default click event
15057
15058  @history
15059     2007-02-02 jvs Original code contributed by Nige "Animal" White
15060     2007-02-02 jvs Renamed to ClickRepeater
15061     2007-02-03 jvs Modifications for FF Mac and Safari
15062
15063  @constructor
15064  @param {Mixed} el The element to listen on
15065  @param {Object} config
15066  */
15067 Ext.util.ClickRepeater = Ext.extend(Ext.util.Observable, {
15068     
15069     constructor : function(el, config){
15070         this.el = Ext.get(el);
15071         this.el.unselectable();
15072
15073         Ext.apply(this, config);
15074
15075         this.addEvents(
15076         /**
15077          * @event mousedown
15078          * Fires when the mouse button is depressed.
15079          * @param {Ext.util.ClickRepeater} this
15080          * @param {Ext.EventObject} e
15081          */
15082         "mousedown",
15083         /**
15084          * @event click
15085          * Fires on a specified interval during the time the element is pressed.
15086          * @param {Ext.util.ClickRepeater} this
15087          * @param {Ext.EventObject} e
15088          */
15089         "click",
15090         /**
15091          * @event mouseup
15092          * Fires when the mouse key is released.
15093          * @param {Ext.util.ClickRepeater} this
15094          * @param {Ext.EventObject} e
15095          */
15096         "mouseup"
15097         );
15098
15099         if(!this.disabled){
15100             this.disabled = true;
15101             this.enable();
15102         }
15103
15104         // allow inline handler
15105         if(this.handler){
15106             this.on("click", this.handler,  this.scope || this);
15107         }
15108
15109         Ext.util.ClickRepeater.superclass.constructor.call(this);        
15110     },
15111     
15112     interval : 20,
15113     delay: 250,
15114     preventDefault : true,
15115     stopDefault : false,
15116     timer : 0,
15117
15118     /**
15119      * Enables the repeater and allows events to fire.
15120      */
15121     enable: function(){
15122         if(this.disabled){
15123             this.el.on('mousedown', this.handleMouseDown, this);
15124             if (Ext.isIE){
15125                 this.el.on('dblclick', this.handleDblClick, this);
15126             }
15127             if(this.preventDefault || this.stopDefault){
15128                 this.el.on('click', this.eventOptions, this);
15129             }
15130         }
15131         this.disabled = false;
15132     },
15133
15134     /**
15135      * Disables the repeater and stops events from firing.
15136      */
15137     disable: function(/* private */ force){
15138         if(force || !this.disabled){
15139             clearTimeout(this.timer);
15140             if(this.pressClass){
15141                 this.el.removeClass(this.pressClass);
15142             }
15143             Ext.getDoc().un('mouseup', this.handleMouseUp, this);
15144             this.el.removeAllListeners();
15145         }
15146         this.disabled = true;
15147     },
15148
15149     /**
15150      * Convenience function for setting disabled/enabled by boolean.
15151      * @param {Boolean} disabled
15152      */
15153     setDisabled: function(disabled){
15154         this[disabled ? 'disable' : 'enable']();
15155     },
15156
15157     eventOptions: function(e){
15158         if(this.preventDefault){
15159             e.preventDefault();
15160         }
15161         if(this.stopDefault){
15162             e.stopEvent();
15163         }
15164     },
15165
15166     // private
15167     destroy : function() {
15168         this.disable(true);
15169         Ext.destroy(this.el);
15170         this.purgeListeners();
15171     },
15172
15173     handleDblClick : function(e){
15174         clearTimeout(this.timer);
15175         this.el.blur();
15176
15177         this.fireEvent("mousedown", this, e);
15178         this.fireEvent("click", this, e);
15179     },
15180
15181     // private
15182     handleMouseDown : function(e){
15183         clearTimeout(this.timer);
15184         this.el.blur();
15185         if(this.pressClass){
15186             this.el.addClass(this.pressClass);
15187         }
15188         this.mousedownTime = new Date();
15189
15190         Ext.getDoc().on("mouseup", this.handleMouseUp, this);
15191         this.el.on("mouseout", this.handleMouseOut, this);
15192
15193         this.fireEvent("mousedown", this, e);
15194         this.fireEvent("click", this, e);
15195
15196         // Do not honor delay or interval if acceleration wanted.
15197         if (this.accelerate) {
15198             this.delay = 400;
15199         }
15200         this.timer = this.click.defer(this.delay || this.interval, this, [e]);
15201     },
15202
15203     // private
15204     click : function(e){
15205         this.fireEvent("click", this, e);
15206         this.timer = this.click.defer(this.accelerate ?
15207             this.easeOutExpo(this.mousedownTime.getElapsed(),
15208                 400,
15209                 -390,
15210                 12000) :
15211             this.interval, this, [e]);
15212     },
15213
15214     easeOutExpo : function (t, b, c, d) {
15215         return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
15216     },
15217
15218     // private
15219     handleMouseOut : function(){
15220         clearTimeout(this.timer);
15221         if(this.pressClass){
15222             this.el.removeClass(this.pressClass);
15223         }
15224         this.el.on("mouseover", this.handleMouseReturn, this);
15225     },
15226
15227     // private
15228     handleMouseReturn : function(){
15229         this.el.un("mouseover", this.handleMouseReturn, this);
15230         if(this.pressClass){
15231             this.el.addClass(this.pressClass);
15232         }
15233         this.click();
15234     },
15235
15236     // private
15237     handleMouseUp : function(e){
15238         clearTimeout(this.timer);
15239         this.el.un("mouseover", this.handleMouseReturn, this);
15240         this.el.un("mouseout", this.handleMouseOut, this);
15241         Ext.getDoc().un("mouseup", this.handleMouseUp, this);
15242         this.el.removeClass(this.pressClass);
15243         this.fireEvent("mouseup", this, e);
15244     }
15245 });/**
15246  * @class Ext.KeyNav
15247  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
15248  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
15249  * way to implement custom navigation schemes for any UI component.</p>
15250  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
15251  * pageUp, pageDown, del, home, end.  Usage:</p>
15252  <pre><code>
15253 var nav = new Ext.KeyNav("my-element", {
15254     "left" : function(e){
15255         this.moveLeft(e.ctrlKey);
15256     },
15257     "right" : function(e){
15258         this.moveRight(e.ctrlKey);
15259     },
15260     "enter" : function(e){
15261         this.save();
15262     },
15263     scope : this
15264 });
15265 </code></pre>
15266  * @constructor
15267  * @param {Mixed} el The element to bind to
15268  * @param {Object} config The config
15269  */
15270 Ext.KeyNav = function(el, config){
15271     this.el = Ext.get(el);
15272     Ext.apply(this, config);
15273     if(!this.disabled){
15274         this.disabled = true;
15275         this.enable();
15276     }
15277 };
15278
15279 Ext.KeyNav.prototype = {
15280     /**
15281      * @cfg {Boolean} disabled
15282      * True to disable this KeyNav instance (defaults to false)
15283      */
15284     disabled : false,
15285     /**
15286      * @cfg {String} defaultEventAction
15287      * The method to call on the {@link Ext.EventObject} after this KeyNav intercepts a key.  Valid values are
15288      * {@link Ext.EventObject#stopEvent}, {@link Ext.EventObject#preventDefault} and
15289      * {@link Ext.EventObject#stopPropagation} (defaults to 'stopEvent')
15290      */
15291     defaultEventAction: "stopEvent",
15292     /**
15293      * @cfg {Boolean} forceKeyDown
15294      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
15295      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
15296      * handle keydown instead of keypress.
15297      */
15298     forceKeyDown : false,
15299
15300     // private
15301     relay : function(e){
15302         var k = e.getKey(),
15303             h = this.keyToHandler[k];
15304         if(h && this[h]){
15305             if(this.doRelay(e, this[h], h) !== true){
15306                 e[this.defaultEventAction]();
15307             }
15308         }
15309     },
15310
15311     // private
15312     doRelay : function(e, h, hname){
15313         return h.call(this.scope || this, e, hname);
15314     },
15315
15316     // possible handlers
15317     enter : false,
15318     left : false,
15319     right : false,
15320     up : false,
15321     down : false,
15322     tab : false,
15323     esc : false,
15324     pageUp : false,
15325     pageDown : false,
15326     del : false,
15327     home : false,
15328     end : false,
15329
15330     // quick lookup hash
15331     keyToHandler : {
15332         37 : "left",
15333         39 : "right",
15334         38 : "up",
15335         40 : "down",
15336         33 : "pageUp",
15337         34 : "pageDown",
15338         46 : "del",
15339         36 : "home",
15340         35 : "end",
15341         13 : "enter",
15342         27 : "esc",
15343         9  : "tab"
15344     },
15345     
15346     stopKeyUp: function(e) {
15347         var k = e.getKey();
15348
15349         if (k >= 37 && k <= 40) {
15350             // *** bugfix - safari 2.x fires 2 keyup events on cursor keys
15351             // *** (note: this bugfix sacrifices the "keyup" event originating from keyNav elements in Safari 2)
15352             e.stopEvent();
15353         }
15354     },
15355     
15356     /**
15357      * Destroy this KeyNav (this is the same as calling disable).
15358      */
15359     destroy: function(){
15360         this.disable();    
15361     },
15362
15363         /**
15364          * Enable this KeyNav
15365          */
15366         enable: function() {
15367         if (this.disabled) {
15368             if (Ext.isSafari2) {
15369                 // call stopKeyUp() on "keyup" event
15370                 this.el.on('keyup', this.stopKeyUp, this);
15371             }
15372
15373             this.el.on(this.isKeydown()? 'keydown' : 'keypress', this.relay, this);
15374             this.disabled = false;
15375         }
15376     },
15377
15378         /**
15379          * Disable this KeyNav
15380          */
15381         disable: function() {
15382         if (!this.disabled) {
15383             if (Ext.isSafari2) {
15384                 // remove "keyup" event handler
15385                 this.el.un('keyup', this.stopKeyUp, this);
15386             }
15387
15388             this.el.un(this.isKeydown()? 'keydown' : 'keypress', this.relay, this);
15389             this.disabled = true;
15390         }
15391     },
15392     
15393     /**
15394      * Convenience function for setting disabled/enabled by boolean.
15395      * @param {Boolean} disabled
15396      */
15397     setDisabled : function(disabled){
15398         this[disabled ? "disable" : "enable"]();
15399     },
15400     
15401     // private
15402     isKeydown: function(){
15403         return this.forceKeyDown || Ext.EventManager.useKeydown;
15404     }
15405 };
15406 /**
15407  * @class Ext.KeyMap
15408  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
15409  * The constructor accepts the same config object as defined by {@link #addBinding}.
15410  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
15411  * combination it will call the function with this signature (if the match is a multi-key
15412  * combination the callback will still be called only once): (String key, Ext.EventObject e)
15413  * A KeyMap can also handle a string representation of keys.<br />
15414  * Usage:
15415  <pre><code>
15416 // map one key by key code
15417 var map = new Ext.KeyMap("my-element", {
15418     key: 13, // or Ext.EventObject.ENTER
15419     fn: myHandler,
15420     scope: myObject
15421 });
15422
15423 // map multiple keys to one action by string
15424 var map = new Ext.KeyMap("my-element", {
15425     key: "a\r\n\t",
15426     fn: myHandler,
15427     scope: myObject
15428 });
15429
15430 // map multiple keys to multiple actions by strings and array of codes
15431 var map = new Ext.KeyMap("my-element", [
15432     {
15433         key: [10,13],
15434         fn: function(){ alert("Return was pressed"); }
15435     }, {
15436         key: "abc",
15437         fn: function(){ alert('a, b or c was pressed'); }
15438     }, {
15439         key: "\t",
15440         ctrl:true,
15441         shift:true,
15442         fn: function(){ alert('Control + shift + tab was pressed.'); }
15443     }
15444 ]);
15445 </code></pre>
15446  * <b>Note: A KeyMap starts enabled</b>
15447  * @constructor
15448  * @param {Mixed} el The element to bind to
15449  * @param {Object} config The config (see {@link #addBinding})
15450  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
15451  */
15452 Ext.KeyMap = function(el, config, eventName){
15453     this.el  = Ext.get(el);
15454     this.eventName = eventName || "keydown";
15455     this.bindings = [];
15456     if(config){
15457         this.addBinding(config);
15458     }
15459     this.enable();
15460 };
15461
15462 Ext.KeyMap.prototype = {
15463     /**
15464      * True to stop the event from bubbling and prevent the default browser action if the
15465      * key was handled by the KeyMap (defaults to false)
15466      * @type Boolean
15467      */
15468     stopEvent : false,
15469
15470     /**
15471      * Add a new binding to this KeyMap. The following config object properties are supported:
15472      * <pre>
15473 Property    Type             Description
15474 ----------  ---------------  ----------------------------------------------------------------------
15475 key         String/Array     A single keycode or an array of keycodes to handle
15476 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)
15477 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)
15478 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)
15479 handler     Function         The function to call when KeyMap finds the expected key combination
15480 fn          Function         Alias of handler (for backwards-compatibility)
15481 scope       Object           The scope of the callback function
15482 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)
15483 </pre>
15484      *
15485      * Usage:
15486      * <pre><code>
15487 // Create a KeyMap
15488 var map = new Ext.KeyMap(document, {
15489     key: Ext.EventObject.ENTER,
15490     fn: handleKey,
15491     scope: this
15492 });
15493
15494 //Add a new binding to the existing KeyMap later
15495 map.addBinding({
15496     key: 'abc',
15497     shift: true,
15498     fn: handleKey,
15499     scope: this
15500 });
15501 </code></pre>
15502      * @param {Object/Array} config A single KeyMap config or an array of configs
15503      */
15504         addBinding : function(config){
15505         if(Ext.isArray(config)){
15506             Ext.each(config, function(c){
15507                 this.addBinding(c);
15508             }, this);
15509             return;
15510         }
15511         var keyCode = config.key,
15512             fn = config.fn || config.handler,
15513             scope = config.scope;
15514
15515         if (config.stopEvent) {
15516             this.stopEvent = config.stopEvent;    
15517         }       
15518
15519         if(typeof keyCode == "string"){
15520             var ks = [];
15521             var keyString = keyCode.toUpperCase();
15522             for(var j = 0, len = keyString.length; j < len; j++){
15523                 ks.push(keyString.charCodeAt(j));
15524             }
15525             keyCode = ks;
15526         }
15527         var keyArray = Ext.isArray(keyCode);
15528         
15529         var handler = function(e){
15530             if(this.checkModifiers(config, e)){
15531                 var k = e.getKey();
15532                 if(keyArray){
15533                     for(var i = 0, len = keyCode.length; i < len; i++){
15534                         if(keyCode[i] == k){
15535                           if(this.stopEvent){
15536                               e.stopEvent();
15537                           }
15538                           fn.call(scope || window, k, e);
15539                           return;
15540                         }
15541                     }
15542                 }else{
15543                     if(k == keyCode){
15544                         if(this.stopEvent){
15545                            e.stopEvent();
15546                         }
15547                         fn.call(scope || window, k, e);
15548                     }
15549                 }
15550             }
15551         };
15552         this.bindings.push(handler);
15553         },
15554     
15555     // private
15556     checkModifiers: function(config, e){
15557         var val, key, keys = ['shift', 'ctrl', 'alt'];
15558         for (var i = 0, len = keys.length; i < len; ++i){
15559             key = keys[i];
15560             val = config[key];
15561             if(!(val === undefined || (val === e[key + 'Key']))){
15562                 return false;
15563             }
15564         }
15565         return true;
15566     },
15567
15568     /**
15569      * Shorthand for adding a single key listener
15570      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
15571      * following options:
15572      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
15573      * @param {Function} fn The function to call
15574      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
15575      */
15576     on : function(key, fn, scope){
15577         var keyCode, shift, ctrl, alt;
15578         if(typeof key == "object" && !Ext.isArray(key)){
15579             keyCode = key.key;
15580             shift = key.shift;
15581             ctrl = key.ctrl;
15582             alt = key.alt;
15583         }else{
15584             keyCode = key;
15585         }
15586         this.addBinding({
15587             key: keyCode,
15588             shift: shift,
15589             ctrl: ctrl,
15590             alt: alt,
15591             fn: fn,
15592             scope: scope
15593         });
15594     },
15595
15596     // private
15597     handleKeyDown : function(e){
15598             if(this.enabled){ //just in case
15599             var b = this.bindings;
15600             for(var i = 0, len = b.length; i < len; i++){
15601                 b[i].call(this, e);
15602             }
15603             }
15604         },
15605
15606         /**
15607          * Returns true if this KeyMap is enabled
15608          * @return {Boolean}
15609          */
15610         isEnabled : function(){
15611             return this.enabled;
15612         },
15613
15614         /**
15615          * Enables this KeyMap
15616          */
15617         enable: function(){
15618                 if(!this.enabled){
15619                     this.el.on(this.eventName, this.handleKeyDown, this);
15620                     this.enabled = true;
15621                 }
15622         },
15623
15624         /**
15625          * Disable this KeyMap
15626          */
15627         disable: function(){
15628                 if(this.enabled){
15629                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
15630                     this.enabled = false;
15631                 }
15632         },
15633     
15634     /**
15635      * Convenience function for setting disabled/enabled by boolean.
15636      * @param {Boolean} disabled
15637      */
15638     setDisabled : function(disabled){
15639         this[disabled ? "disable" : "enable"]();
15640     }
15641 };/**
15642  * @class Ext.util.TextMetrics
15643  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
15644  * wide, in pixels, a given block of text will be. Note that when measuring text, it should be plain text and
15645  * should not contain any HTML, otherwise it may not be measured correctly.
15646  * @singleton
15647  */
15648 Ext.util.TextMetrics = function(){
15649     var shared;
15650     return {
15651         /**
15652          * Measures the size of the specified text
15653          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
15654          * that can affect the size of the rendered text
15655          * @param {String} text The text to measure
15656          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
15657          * in order to accurately measure the text height
15658          * @return {Object} An object containing the text's size {width: (width), height: (height)}
15659          */
15660         measure : function(el, text, fixedWidth){
15661             if(!shared){
15662                 shared = Ext.util.TextMetrics.Instance(el, fixedWidth);
15663             }
15664             shared.bind(el);
15665             shared.setFixedWidth(fixedWidth || 'auto');
15666             return shared.getSize(text);
15667         },
15668
15669         /**
15670          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
15671          * the overhead of multiple calls to initialize the style properties on each measurement.
15672          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
15673          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
15674          * in order to accurately measure the text height
15675          * @return {Ext.util.TextMetrics.Instance} instance The new instance
15676          */
15677         createInstance : function(el, fixedWidth){
15678             return Ext.util.TextMetrics.Instance(el, fixedWidth);
15679         }
15680     };
15681 }();
15682
15683 Ext.util.TextMetrics.Instance = function(bindTo, fixedWidth){
15684     var ml = new Ext.Element(document.createElement('div'));
15685     document.body.appendChild(ml.dom);
15686     ml.position('absolute');
15687     ml.setLeftTop(-1000, -1000);
15688     ml.hide();
15689
15690     if(fixedWidth){
15691         ml.setWidth(fixedWidth);
15692     }
15693
15694     var instance = {
15695         /**
15696          * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
15697          * Returns the size of the specified text based on the internal element's style and width properties
15698          * @param {String} text The text to measure
15699          * @return {Object} An object containing the text's size {width: (width), height: (height)}
15700          */
15701         getSize : function(text){
15702             ml.update(text);
15703             var s = ml.getSize();
15704             ml.update('');
15705             return s;
15706         },
15707
15708         /**
15709          * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
15710          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
15711          * that can affect the size of the rendered text
15712          * @param {String/HTMLElement} el The element, dom node or id
15713          */
15714         bind : function(el){
15715             ml.setStyle(
15716                 Ext.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height', 'text-transform', 'letter-spacing')
15717             );
15718         },
15719
15720         /**
15721          * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
15722          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
15723          * to set a fixed width in order to accurately measure the text height.
15724          * @param {Number} width The width to set on the element
15725          */
15726         setFixedWidth : function(width){
15727             ml.setWidth(width);
15728         },
15729
15730         /**
15731          * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
15732          * Returns the measured width of the specified text
15733          * @param {String} text The text to measure
15734          * @return {Number} width The width in pixels
15735          */
15736         getWidth : function(text){
15737             ml.dom.style.width = 'auto';
15738             return this.getSize(text).width;
15739         },
15740
15741         /**
15742          * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
15743          * Returns the measured height of the specified text.  For multiline text, be sure to call
15744          * {@link #setFixedWidth} if necessary.
15745          * @param {String} text The text to measure
15746          * @return {Number} height The height in pixels
15747          */
15748         getHeight : function(text){
15749             return this.getSize(text).height;
15750         }
15751     };
15752
15753     instance.bind(bindTo);
15754
15755     return instance;
15756 };
15757
15758 Ext.Element.addMethods({
15759     /**
15760      * Returns the width in pixels of the passed text, or the width of the text in this Element.
15761      * @param {String} text The text to measure. Defaults to the innerHTML of the element.
15762      * @param {Number} min (Optional) The minumum value to return.
15763      * @param {Number} max (Optional) The maximum value to return.
15764      * @return {Number} The text width in pixels.
15765      * @member Ext.Element getTextWidth
15766      */
15767     getTextWidth : function(text, min, max){
15768         return (Ext.util.TextMetrics.measure(this.dom, Ext.value(text, this.dom.innerHTML, true)).width).constrain(min || 0, max || 1000000);
15769     }
15770 });
15771 /**
15772  * @class Ext.util.Cookies
15773  * Utility class for managing and interacting with cookies.
15774  * @singleton
15775  */
15776 Ext.util.Cookies = {
15777     /**
15778      * Create a cookie with the specified name and value. Additional settings
15779      * for the cookie may be optionally specified (for example: expiration,
15780      * access restriction, SSL).
15781      * @param {String} name The name of the cookie to set. 
15782      * @param {Mixed} value The value to set for the cookie.
15783      * @param {Object} expires (Optional) Specify an expiration date the
15784      * cookie is to persist until.  Note that the specified Date object will
15785      * be converted to Greenwich Mean Time (GMT). 
15786      * @param {String} path (Optional) Setting a path on the cookie restricts
15787      * access to pages that match that path. Defaults to all pages (<tt>'/'</tt>). 
15788      * @param {String} domain (Optional) Setting a domain restricts access to
15789      * pages on a given domain (typically used to allow cookie access across
15790      * subdomains). For example, "extjs.com" will create a cookie that can be
15791      * accessed from any subdomain of extjs.com, including www.extjs.com,
15792      * support.extjs.com, etc.
15793      * @param {Boolean} secure (Optional) Specify true to indicate that the cookie
15794      * should only be accessible via SSL on a page using the HTTPS protocol.
15795      * Defaults to <tt>false</tt>. Note that this will only work if the page
15796      * calling this code uses the HTTPS protocol, otherwise the cookie will be
15797      * created with default options.
15798      */
15799     set : function(name, value){
15800         var argv = arguments;
15801         var argc = arguments.length;
15802         var expires = (argc > 2) ? argv[2] : null;
15803         var path = (argc > 3) ? argv[3] : '/';
15804         var domain = (argc > 4) ? argv[4] : null;
15805         var secure = (argc > 5) ? argv[5] : false;
15806         document.cookie = name + "=" + escape(value) + ((expires === null) ? "" : ("; expires=" + expires.toGMTString())) + ((path === null) ? "" : ("; path=" + path)) + ((domain === null) ? "" : ("; domain=" + domain)) + ((secure === true) ? "; secure" : "");
15807     },
15808
15809     /**
15810      * Retrieves cookies that are accessible by the current page. If a cookie
15811      * does not exist, <code>get()</code> returns <tt>null</tt>.  The following
15812      * example retrieves the cookie called "valid" and stores the String value
15813      * in the variable <tt>validStatus</tt>.
15814      * <pre><code>
15815      * var validStatus = Ext.util.Cookies.get("valid");
15816      * </code></pre>
15817      * @param {String} name The name of the cookie to get
15818      * @return {Mixed} Returns the cookie value for the specified name;
15819      * null if the cookie name does not exist.
15820      */
15821     get : function(name){
15822         var arg = name + "=";
15823         var alen = arg.length;
15824         var clen = document.cookie.length;
15825         var i = 0;
15826         var j = 0;
15827         while(i < clen){
15828             j = i + alen;
15829             if(document.cookie.substring(i, j) == arg){
15830                 return Ext.util.Cookies.getCookieVal(j);
15831             }
15832             i = document.cookie.indexOf(" ", i) + 1;
15833             if(i === 0){
15834                 break;
15835             }
15836         }
15837         return null;
15838     },
15839
15840     /**
15841      * Removes a cookie with the provided name from the browser
15842      * if found by setting its expiration date to sometime in the past. 
15843      * @param {String} name The name of the cookie to remove
15844      */
15845     clear : function(name){
15846         if(Ext.util.Cookies.get(name)){
15847             document.cookie = name + "=" + "; expires=Thu, 01-Jan-70 00:00:01 GMT";
15848         }
15849     },
15850     /**
15851      * @private
15852      */
15853     getCookieVal : function(offset){
15854         var endstr = document.cookie.indexOf(";", offset);
15855         if(endstr == -1){
15856             endstr = document.cookie.length;
15857         }
15858         return unescape(document.cookie.substring(offset, endstr));
15859     }
15860 };/**
15861  * Framework-wide error-handler.  Developers can override this method to provide
15862  * custom exception-handling.  Framework errors will often extend from the base
15863  * Ext.Error class.
15864  * @param {Object/Error} e The thrown exception object.
15865  */
15866 Ext.handleError = function(e) {
15867     throw e;
15868 };
15869
15870 /**
15871  * @class Ext.Error
15872  * @extends Error
15873  * <p>A base error class. Future implementations are intended to provide more
15874  * robust error handling throughout the framework (<b>in the debug build only</b>)
15875  * to check for common errors and problems. The messages issued by this class
15876  * will aid error checking. Error checks will be automatically removed in the
15877  * production build so that performance is not negatively impacted.</p>
15878  * <p>Some sample messages currently implemented:</p><pre>
15879 "DataProxy attempted to execute an API-action but found an undefined
15880 url / function. Please review your Proxy url/api-configuration."
15881  * </pre><pre>
15882 "Could not locate your "root" property in your server response.
15883 Please review your JsonReader config to ensure the config-property
15884 "root" matches the property your server-response.  See the JsonReader
15885 docs for additional assistance."
15886  * </pre>
15887  * <p>An example of the code used for generating error messages:</p><pre><code>
15888 try {
15889     generateError({
15890         foo: 'bar'
15891     });
15892 }
15893 catch (e) {
15894     console.error(e);
15895 }
15896 function generateError(data) {
15897     throw new Ext.Error('foo-error', data);
15898 }
15899  * </code></pre>
15900  * @param {String} message
15901  */
15902 Ext.Error = function(message) {
15903     // Try to read the message from Ext.Error.lang
15904     this.message = (this.lang[message]) ? this.lang[message] : message;
15905 };
15906
15907 Ext.Error.prototype = new Error();
15908 Ext.apply(Ext.Error.prototype, {
15909     // protected.  Extensions place their error-strings here.
15910     lang: {},
15911
15912     name: 'Ext.Error',
15913     /**
15914      * getName
15915      * @return {String}
15916      */
15917     getName : function() {
15918         return this.name;
15919     },
15920     /**
15921      * getMessage
15922      * @return {String}
15923      */
15924     getMessage : function() {
15925         return this.message;
15926     },
15927     /**
15928      * toJson
15929      * @return {String}
15930      */
15931     toJson : function() {
15932         return Ext.encode(this);
15933     }
15934 });
15935 /**
15936  * @class Ext.ComponentMgr
15937  * <p>Provides a registry of all Components (instances of {@link Ext.Component} or any subclass
15938  * thereof) on a page so that they can be easily accessed by {@link Ext.Component component}
15939  * {@link Ext.Component#id id} (see {@link #get}, or the convenience method {@link Ext#getCmp Ext.getCmp}).</p>
15940  * <p>This object also provides a registry of available Component <i>classes</i>
15941  * indexed by a mnemonic code known as the Component's {@link Ext.Component#xtype xtype}.
15942  * The <code>{@link Ext.Component#xtype xtype}</code> provides a way to avoid instantiating child Components
15943  * when creating a full, nested config object for a complete Ext page.</p>
15944  * <p>A child Component may be specified simply as a <i>config object</i>
15945  * as long as the correct <code>{@link Ext.Component#xtype xtype}</code> is specified so that if and when the Component
15946  * needs rendering, the correct type can be looked up for lazy instantiation.</p>
15947  * <p>For a list of all available <code>{@link Ext.Component#xtype xtypes}</code>, see {@link Ext.Component}.</p>
15948  * @singleton
15949  */
15950 Ext.ComponentMgr = function(){
15951     var all = new Ext.util.MixedCollection();
15952     var types = {};
15953     var ptypes = {};
15954
15955     return {
15956         /**
15957          * Registers a component.
15958          * @param {Ext.Component} c The component
15959          */
15960         register : function(c){
15961             all.add(c);
15962         },
15963
15964         /**
15965          * Unregisters a component.
15966          * @param {Ext.Component} c The component
15967          */
15968         unregister : function(c){
15969             all.remove(c);
15970         },
15971
15972         /**
15973          * Returns a component by {@link Ext.Component#id id}.
15974          * For additional details see {@link Ext.util.MixedCollection#get}.
15975          * @param {String} id The component {@link Ext.Component#id id}
15976          * @return Ext.Component The Component, <code>undefined</code> if not found, or <code>null</code> if a
15977          * Class was found.
15978          */
15979         get : function(id){
15980             return all.get(id);
15981         },
15982
15983         /**
15984          * Registers a function that will be called when a Component with the specified id is added to ComponentMgr. This will happen on instantiation.
15985          * @param {String} id The component {@link Ext.Component#id id}
15986          * @param {Function} fn The callback function
15987          * @param {Object} scope The scope (<code>this</code> reference) in which the callback is executed. Defaults to the Component.
15988          */
15989         onAvailable : function(id, fn, scope){
15990             all.on("add", function(index, o){
15991                 if(o.id == id){
15992                     fn.call(scope || o, o);
15993                     all.un("add", fn, scope);
15994                 }
15995             });
15996         },
15997
15998         /**
15999          * The MixedCollection used internally for the component cache. An example usage may be subscribing to
16000          * events on the MixedCollection to monitor addition or removal.  Read-only.
16001          * @type {MixedCollection}
16002          */
16003         all : all,
16004         
16005         /**
16006          * The xtypes that have been registered with the component manager.
16007          * @type {Object}
16008          */
16009         types : types,
16010         
16011         /**
16012          * The ptypes that have been registered with the component manager.
16013          * @type {Object}
16014          */
16015         ptypes: ptypes,
16016         
16017         /**
16018          * Checks if a Component type is registered.
16019          * @param {Ext.Component} xtype The mnemonic string by which the Component class may be looked up
16020          * @return {Boolean} Whether the type is registered.
16021          */
16022         isRegistered : function(xtype){
16023             return types[xtype] !== undefined;    
16024         },
16025         
16026         /**
16027          * Checks if a Plugin type is registered.
16028          * @param {Ext.Component} ptype The mnemonic string by which the Plugin class may be looked up
16029          * @return {Boolean} Whether the type is registered.
16030          */
16031         isPluginRegistered : function(ptype){
16032             return ptypes[ptype] !== undefined;    
16033         },        
16034
16035         /**
16036          * <p>Registers a new Component constructor, keyed by a new
16037          * {@link Ext.Component#xtype}.</p>
16038          * <p>Use this method (or its alias {@link Ext#reg Ext.reg}) to register new
16039          * subclasses of {@link Ext.Component} so that lazy instantiation may be used when specifying
16040          * child Components.
16041          * see {@link Ext.Container#items}</p>
16042          * @param {String} xtype The mnemonic string by which the Component class may be looked up.
16043          * @param {Constructor} cls The new Component class.
16044          */
16045         registerType : function(xtype, cls){
16046             types[xtype] = cls;
16047             cls.xtype = xtype;
16048         },
16049
16050         /**
16051          * Creates a new Component from the specified config object using the
16052          * config object's {@link Ext.component#xtype xtype} to determine the class to instantiate.
16053          * @param {Object} config A configuration object for the Component you wish to create.
16054          * @param {Constructor} defaultType The constructor to provide the default Component type if
16055          * the config object does not contain a <code>xtype</code>. (Optional if the config contains a <code>xtype</code>).
16056          * @return {Ext.Component} The newly instantiated Component.
16057          */
16058         create : function(config, defaultType){
16059             return config.render ? config : new types[config.xtype || defaultType](config);
16060         },
16061
16062         /**
16063          * <p>Registers a new Plugin constructor, keyed by a new
16064          * {@link Ext.Component#ptype}.</p>
16065          * <p>Use this method (or its alias {@link Ext#preg Ext.preg}) to register new
16066          * plugins for {@link Ext.Component}s so that lazy instantiation may be used when specifying
16067          * Plugins.</p>
16068          * @param {String} ptype The mnemonic string by which the Plugin class may be looked up.
16069          * @param {Constructor} cls The new Plugin class.
16070          */
16071         registerPlugin : function(ptype, cls){
16072             ptypes[ptype] = cls;
16073             cls.ptype = ptype;
16074         },
16075
16076         /**
16077          * Creates a new Plugin from the specified config object using the
16078          * config object's {@link Ext.component#ptype ptype} to determine the class to instantiate.
16079          * @param {Object} config A configuration object for the Plugin you wish to create.
16080          * @param {Constructor} defaultType The constructor to provide the default Plugin type if
16081          * the config object does not contain a <code>ptype</code>. (Optional if the config contains a <code>ptype</code>).
16082          * @return {Ext.Component} The newly instantiated Plugin.
16083          */
16084         createPlugin : function(config, defaultType){
16085             var PluginCls = ptypes[config.ptype || defaultType];
16086             if (PluginCls.init) {
16087                 return PluginCls;                
16088             } else {
16089                 return new PluginCls(config);
16090             }            
16091         }
16092     };
16093 }();
16094
16095 /**
16096  * Shorthand for {@link Ext.ComponentMgr#registerType}
16097  * @param {String} xtype The {@link Ext.component#xtype mnemonic string} by which the Component class
16098  * may be looked up.
16099  * @param {Constructor} cls The new Component class.
16100  * @member Ext
16101  * @method reg
16102  */
16103 Ext.reg = Ext.ComponentMgr.registerType; // this will be called a lot internally, shorthand to keep the bytes down
16104 /**
16105  * Shorthand for {@link Ext.ComponentMgr#registerPlugin}
16106  * @param {String} ptype The {@link Ext.component#ptype mnemonic string} by which the Plugin class
16107  * may be looked up.
16108  * @param {Constructor} cls The new Plugin class.
16109  * @member Ext
16110  * @method preg
16111  */
16112 Ext.preg = Ext.ComponentMgr.registerPlugin;
16113 /**
16114  * Shorthand for {@link Ext.ComponentMgr#create}
16115  * Creates a new Component from the specified config object using the
16116  * config object's {@link Ext.component#xtype xtype} to determine the class to instantiate.
16117  * @param {Object} config A configuration object for the Component you wish to create.
16118  * @param {Constructor} defaultType The constructor to provide the default Component type if
16119  * the config object does not contain a <code>xtype</code>. (Optional if the config contains a <code>xtype</code>).
16120  * @return {Ext.Component} The newly instantiated Component.
16121  * @member Ext
16122  * @method create
16123  */
16124 Ext.create = Ext.ComponentMgr.create;/**
16125  * @class Ext.Component
16126  * @extends Ext.util.Observable
16127  * <p>Base class for all Ext components.  All subclasses of Component may participate in the automated
16128  * Ext component lifecycle of creation, rendering and destruction which is provided by the {@link Ext.Container Container} class.
16129  * Components may be added to a Container through the {@link Ext.Container#items items} config option at the time the Container is created,
16130  * or they may be added dynamically via the {@link Ext.Container#add add} method.</p>
16131  * <p>The Component base class has built-in support for basic hide/show and enable/disable behavior.</p>
16132  * <p>All Components are registered with the {@link Ext.ComponentMgr} on construction so that they can be referenced at any time via
16133  * {@link Ext#getCmp}, passing the {@link #id}.</p>
16134  * <p>All user-developed visual widgets that are required to participate in automated lifecycle and size management should subclass Component (or
16135  * {@link Ext.BoxComponent} if managed box model handling is required, ie height and width management).</p>
16136  * <p>See the <a href="http://extjs.com/learn/Tutorial:Creating_new_UI_controls">Creating new UI controls</a> tutorial for details on how
16137  * and to either extend or augment ExtJs base classes to create custom Components.</p>
16138  * <p>Every component has a specific xtype, which is its Ext-specific type name, along with methods for checking the
16139  * xtype like {@link #getXType} and {@link #isXType}. This is the list of all valid xtypes:</p>
16140  * <pre>
16141 xtype            Class
16142 -------------    ------------------
16143 box              {@link Ext.BoxComponent}
16144 button           {@link Ext.Button}
16145 buttongroup      {@link Ext.ButtonGroup}
16146 colorpalette     {@link Ext.ColorPalette}
16147 component        {@link Ext.Component}
16148 container        {@link Ext.Container}
16149 cycle            {@link Ext.CycleButton}
16150 dataview         {@link Ext.DataView}
16151 datepicker       {@link Ext.DatePicker}
16152 editor           {@link Ext.Editor}
16153 editorgrid       {@link Ext.grid.EditorGridPanel}
16154 flash            {@link Ext.FlashComponent}
16155 grid             {@link Ext.grid.GridPanel}
16156 listview         {@link Ext.ListView}
16157 multislider      {@link Ext.slider.MultiSlider}
16158 panel            {@link Ext.Panel}
16159 progress         {@link Ext.ProgressBar}
16160 propertygrid     {@link Ext.grid.PropertyGrid}
16161 slider           {@link Ext.slider.SingleSlider}
16162 spacer           {@link Ext.Spacer}
16163 splitbutton      {@link Ext.SplitButton}
16164 tabpanel         {@link Ext.TabPanel}
16165 treepanel        {@link Ext.tree.TreePanel}
16166 viewport         {@link Ext.ViewPort}
16167 window           {@link Ext.Window}
16168
16169 Toolbar components
16170 ---------------------------------------
16171 paging           {@link Ext.PagingToolbar}
16172 toolbar          {@link Ext.Toolbar}
16173 tbbutton         {@link Ext.Toolbar.Button}        (deprecated; use button)
16174 tbfill           {@link Ext.Toolbar.Fill}
16175 tbitem           {@link Ext.Toolbar.Item}
16176 tbseparator      {@link Ext.Toolbar.Separator}
16177 tbspacer         {@link Ext.Toolbar.Spacer}
16178 tbsplit          {@link Ext.Toolbar.SplitButton}   (deprecated; use splitbutton)
16179 tbtext           {@link Ext.Toolbar.TextItem}
16180
16181 Menu components
16182 ---------------------------------------
16183 menu             {@link Ext.menu.Menu}
16184 colormenu        {@link Ext.menu.ColorMenu}
16185 datemenu         {@link Ext.menu.DateMenu}
16186 menubaseitem     {@link Ext.menu.BaseItem}
16187 menucheckitem    {@link Ext.menu.CheckItem}
16188 menuitem         {@link Ext.menu.Item}
16189 menuseparator    {@link Ext.menu.Separator}
16190 menutextitem     {@link Ext.menu.TextItem}
16191
16192 Form components
16193 ---------------------------------------
16194 form             {@link Ext.form.FormPanel}
16195 checkbox         {@link Ext.form.Checkbox}
16196 checkboxgroup    {@link Ext.form.CheckboxGroup}
16197 combo            {@link Ext.form.ComboBox}
16198 compositefield   {@link Ext.form.CompositeField}
16199 datefield        {@link Ext.form.DateField}
16200 displayfield     {@link Ext.form.DisplayField}
16201 field            {@link Ext.form.Field}
16202 fieldset         {@link Ext.form.FieldSet}
16203 hidden           {@link Ext.form.Hidden}
16204 htmleditor       {@link Ext.form.HtmlEditor}
16205 label            {@link Ext.form.Label}
16206 numberfield      {@link Ext.form.NumberField}
16207 radio            {@link Ext.form.Radio}
16208 radiogroup       {@link Ext.form.RadioGroup}
16209 textarea         {@link Ext.form.TextArea}
16210 textfield        {@link Ext.form.TextField}
16211 timefield        {@link Ext.form.TimeField}
16212 trigger          {@link Ext.form.TriggerField}
16213
16214 Chart components
16215 ---------------------------------------
16216 chart            {@link Ext.chart.Chart}
16217 barchart         {@link Ext.chart.BarChart}
16218 cartesianchart   {@link Ext.chart.CartesianChart}
16219 columnchart      {@link Ext.chart.ColumnChart}
16220 linechart        {@link Ext.chart.LineChart}
16221 piechart         {@link Ext.chart.PieChart}
16222
16223 Store xtypes
16224 ---------------------------------------
16225 arraystore       {@link Ext.data.ArrayStore}
16226 directstore      {@link Ext.data.DirectStore}
16227 groupingstore    {@link Ext.data.GroupingStore}
16228 jsonstore        {@link Ext.data.JsonStore}
16229 simplestore      {@link Ext.data.SimpleStore}      (deprecated; use arraystore)
16230 store            {@link Ext.data.Store}
16231 xmlstore         {@link Ext.data.XmlStore}
16232 </pre>
16233  * @constructor
16234  * @param {Ext.Element/String/Object} config The configuration options may be specified as either:
16235  * <div class="mdetail-params"><ul>
16236  * <li><b>an element</b> :
16237  * <p class="sub-desc">it is set as the internal element and its id used as the component id</p></li>
16238  * <li><b>a string</b> :
16239  * <p class="sub-desc">it is assumed to be the id of an existing element and is used as the component id</p></li>
16240  * <li><b>anything else</b> :
16241  * <p class="sub-desc">it is assumed to be a standard config object and is applied to the component</p></li>
16242  * </ul></div>
16243  */
16244 Ext.Component = function(config){
16245     config = config || {};
16246     if(config.initialConfig){
16247         if(config.isAction){           // actions
16248             this.baseAction = config;
16249         }
16250         config = config.initialConfig; // component cloning / action set up
16251     }else if(config.tagName || config.dom || Ext.isString(config)){ // element object
16252         config = {applyTo: config, id: config.id || config};
16253     }
16254
16255     /**
16256      * This Component's initial configuration specification. Read-only.
16257      * @type Object
16258      * @property initialConfig
16259      */
16260     this.initialConfig = config;
16261
16262     Ext.apply(this, config);
16263     this.addEvents(
16264         /**
16265          * @event added
16266          * Fires when a component is added to an Ext.Container
16267          * @param {Ext.Component} this
16268          * @param {Ext.Container} ownerCt Container which holds the component
16269          * @param {number} index Position at which the component was added
16270          */
16271         'added',
16272         /**
16273          * @event disable
16274          * Fires after the component is disabled.
16275          * @param {Ext.Component} this
16276          */
16277         'disable',
16278         /**
16279          * @event enable
16280          * Fires after the component is enabled.
16281          * @param {Ext.Component} this
16282          */
16283         'enable',
16284         /**
16285          * @event beforeshow
16286          * Fires before the component is shown by calling the {@link #show} method.
16287          * Return false from an event handler to stop the show.
16288          * @param {Ext.Component} this
16289          */
16290         'beforeshow',
16291         /**
16292          * @event show
16293          * Fires after the component is shown when calling the {@link #show} method.
16294          * @param {Ext.Component} this
16295          */
16296         'show',
16297         /**
16298          * @event beforehide
16299          * Fires before the component is hidden by calling the {@link #hide} method.
16300          * Return false from an event handler to stop the hide.
16301          * @param {Ext.Component} this
16302          */
16303         'beforehide',
16304         /**
16305          * @event hide
16306          * Fires after the component is hidden.
16307          * Fires after the component is hidden when calling the {@link #hide} method.
16308          * @param {Ext.Component} this
16309          */
16310         'hide',
16311         /**
16312          * @event removed
16313          * Fires when a component is removed from an Ext.Container
16314          * @param {Ext.Component} this
16315          * @param {Ext.Container} ownerCt Container which holds the component
16316          */
16317         'removed',
16318         /**
16319          * @event beforerender
16320          * Fires before the component is {@link #rendered}. Return false from an
16321          * event handler to stop the {@link #render}.
16322          * @param {Ext.Component} this
16323          */
16324         'beforerender',
16325         /**
16326          * @event render
16327          * Fires after the component markup is {@link #rendered}.
16328          * @param {Ext.Component} this
16329          */
16330         'render',
16331         /**
16332          * @event afterrender
16333          * <p>Fires after the component rendering is finished.</p>
16334          * <p>The afterrender event is fired after this Component has been {@link #rendered}, been postprocesed
16335          * by any afterRender method defined for the Component, and, if {@link #stateful}, after state
16336          * has been restored.</p>
16337          * @param {Ext.Component} this
16338          */
16339         'afterrender',
16340         /**
16341          * @event beforedestroy
16342          * Fires before the component is {@link #destroy}ed. Return false from an event handler to stop the {@link #destroy}.
16343          * @param {Ext.Component} this
16344          */
16345         'beforedestroy',
16346         /**
16347          * @event destroy
16348          * Fires after the component is {@link #destroy}ed.
16349          * @param {Ext.Component} this
16350          */
16351         'destroy',
16352         /**
16353          * @event beforestaterestore
16354          * Fires before the state of the component is restored. Return false from an event handler to stop the restore.
16355          * @param {Ext.Component} this
16356          * @param {Object} state The hash of state values returned from the StateProvider. If this
16357          * event is not vetoed, then the state object is passed to <b><tt>applyState</tt></b>. By default,
16358          * that simply copies property values into this Component. The method maybe overriden to
16359          * provide custom state restoration.
16360          */
16361         'beforestaterestore',
16362         /**
16363          * @event staterestore
16364          * Fires after the state of the component is restored.
16365          * @param {Ext.Component} this
16366          * @param {Object} state The hash of state values returned from the StateProvider. This is passed
16367          * to <b><tt>applyState</tt></b>. By default, that simply copies property values into this
16368          * Component. The method maybe overriden to provide custom state restoration.
16369          */
16370         'staterestore',
16371         /**
16372          * @event beforestatesave
16373          * Fires before the state of the component is saved to the configured state provider. Return false to stop the save.
16374          * @param {Ext.Component} this
16375          * @param {Object} state The hash of state values. This is determined by calling
16376          * <b><tt>getState()</tt></b> on the Component. This method must be provided by the
16377          * developer to return whetever representation of state is required, by default, Ext.Component
16378          * has a null implementation.
16379          */
16380         'beforestatesave',
16381         /**
16382          * @event statesave
16383          * Fires after the state of the component is saved to the configured state provider.
16384          * @param {Ext.Component} this
16385          * @param {Object} state The hash of state values. This is determined by calling
16386          * <b><tt>getState()</tt></b> on the Component. This method must be provided by the
16387          * developer to return whetever representation of state is required, by default, Ext.Component
16388          * has a null implementation.
16389          */
16390         'statesave'
16391     );
16392     this.getId();
16393     Ext.ComponentMgr.register(this);
16394     Ext.Component.superclass.constructor.call(this);
16395
16396     if(this.baseAction){
16397         this.baseAction.addComponent(this);
16398     }
16399
16400     this.initComponent();
16401
16402     if(this.plugins){
16403         if(Ext.isArray(this.plugins)){
16404             for(var i = 0, len = this.plugins.length; i < len; i++){
16405                 this.plugins[i] = this.initPlugin(this.plugins[i]);
16406             }
16407         }else{
16408             this.plugins = this.initPlugin(this.plugins);
16409         }
16410     }
16411
16412     if(this.stateful !== false){
16413         this.initState();
16414     }
16415
16416     if(this.applyTo){
16417         this.applyToMarkup(this.applyTo);
16418         delete this.applyTo;
16419     }else if(this.renderTo){
16420         this.render(this.renderTo);
16421         delete this.renderTo;
16422     }
16423 };
16424
16425 // private
16426 Ext.Component.AUTO_ID = 1000;
16427
16428 Ext.extend(Ext.Component, Ext.util.Observable, {
16429     // Configs below are used for all Components when rendered by FormLayout.
16430     /**
16431      * @cfg {String} fieldLabel <p>The label text to display next to this Component (defaults to '').</p>
16432      * <br><p><b>Note</b>: this config is only used when this Component is rendered by a Container which
16433      * has been configured to use the <b>{@link Ext.layout.FormLayout FormLayout}</b> layout manager (e.g.
16434      * {@link Ext.form.FormPanel} or specifying <tt>layout:'form'</tt>).</p><br>
16435      * <p>Also see <tt>{@link #hideLabel}</tt> and
16436      * {@link Ext.layout.FormLayout}.{@link Ext.layout.FormLayout#fieldTpl fieldTpl}.</p>
16437      * Example use:<pre><code>
16438 new Ext.FormPanel({
16439     height: 100,
16440     renderTo: Ext.getBody(),
16441     items: [{
16442         xtype: 'textfield',
16443         fieldLabel: 'Name'
16444     }]
16445 });
16446 </code></pre>
16447      */
16448     /**
16449      * @cfg {String} labelStyle <p>A CSS style specification string to apply directly to this field's
16450      * label.  Defaults to the container's labelStyle value if set (e.g.,
16451      * <tt>{@link Ext.layout.FormLayout#labelStyle}</tt> , or '').</p>
16452      * <br><p><b>Note</b>: see the note for <code>{@link #clearCls}</code>.</p><br>
16453      * <p>Also see <code>{@link #hideLabel}</code> and
16454      * <code>{@link Ext.layout.FormLayout}.{@link Ext.layout.FormLayout#fieldTpl fieldTpl}.</code></p>
16455      * Example use:<pre><code>
16456 new Ext.FormPanel({
16457     height: 100,
16458     renderTo: Ext.getBody(),
16459     items: [{
16460         xtype: 'textfield',
16461         fieldLabel: 'Name',
16462         labelStyle: 'font-weight:bold;'
16463     }]
16464 });
16465 </code></pre>
16466      */
16467     /**
16468      * @cfg {String} labelSeparator <p>The separator to display after the text of each
16469      * <tt>{@link #fieldLabel}</tt>.  This property may be configured at various levels.
16470      * The order of precedence is:
16471      * <div class="mdetail-params"><ul>
16472      * <li>field / component level</li>
16473      * <li>container level</li>
16474      * <li>{@link Ext.layout.FormLayout#labelSeparator layout level} (defaults to colon <tt>':'</tt>)</li>
16475      * </ul></div>
16476      * To display no separator for this field's label specify empty string ''.</p>
16477      * <br><p><b>Note</b>: see the note for <tt>{@link #clearCls}</tt>.</p><br>
16478      * <p>Also see <tt>{@link #hideLabel}</tt> and
16479      * {@link Ext.layout.FormLayout}.{@link Ext.layout.FormLayout#fieldTpl fieldTpl}.</p>
16480      * Example use:<pre><code>
16481 new Ext.FormPanel({
16482     height: 100,
16483     renderTo: Ext.getBody(),
16484     layoutConfig: {
16485         labelSeparator: '~'   // layout config has lowest priority (defaults to ':')
16486     },
16487     {@link Ext.layout.FormLayout#labelSeparator labelSeparator}: '>>',     // config at container level
16488     items: [{
16489         xtype: 'textfield',
16490         fieldLabel: 'Field 1',
16491         labelSeparator: '...' // field/component level config supersedes others
16492     },{
16493         xtype: 'textfield',
16494         fieldLabel: 'Field 2' // labelSeparator will be '='
16495     }]
16496 });
16497 </code></pre>
16498      */
16499     /**
16500      * @cfg {Boolean} hideLabel <p><tt>true</tt> to completely hide the label element
16501      * ({@link #fieldLabel label} and {@link #labelSeparator separator}). Defaults to <tt>false</tt>.
16502      * By default, even if you do not specify a <tt>{@link #fieldLabel}</tt> the space will still be
16503      * reserved so that the field will line up with other fields that do have labels.
16504      * Setting this to <tt>true</tt> will cause the field to not reserve that space.</p>
16505      * <br><p><b>Note</b>: see the note for <tt>{@link #clearCls}</tt>.</p><br>
16506      * Example use:<pre><code>
16507 new Ext.FormPanel({
16508     height: 100,
16509     renderTo: Ext.getBody(),
16510     items: [{
16511         xtype: 'textfield'
16512         hideLabel: true
16513     }]
16514 });
16515 </code></pre>
16516      */
16517     /**
16518      * @cfg {String} clearCls <p>The CSS class used to to apply to the special clearing div rendered
16519      * directly after each form field wrapper to provide field clearing (defaults to
16520      * <tt>'x-form-clear-left'</tt>).</p>
16521      * <br><p><b>Note</b>: this config is only used when this Component is rendered by a Container
16522      * which has been configured to use the <b>{@link Ext.layout.FormLayout FormLayout}</b> layout
16523      * manager (e.g. {@link Ext.form.FormPanel} or specifying <tt>layout:'form'</tt>) and either a
16524      * <tt>{@link #fieldLabel}</tt> is specified or <tt>isFormField=true</tt> is specified.</p><br>
16525      * <p>See {@link Ext.layout.FormLayout}.{@link Ext.layout.FormLayout#fieldTpl fieldTpl} also.</p>
16526      */
16527     /**
16528      * @cfg {String} itemCls
16529      * <p><b>Note</b>: this config is only used when this Component is rendered by a Container which
16530      * has been configured to use the <b>{@link Ext.layout.FormLayout FormLayout}</b> layout manager (e.g.
16531      * {@link Ext.form.FormPanel} or specifying <tt>layout:'form'</tt>).</p><br>
16532      * <p>An additional CSS class to apply to the div wrapping the form item
16533      * element of this field.  If supplied, <tt>itemCls</tt> at the <b>field</b> level will override
16534      * the default <tt>itemCls</tt> supplied at the <b>container</b> level. The value specified for
16535      * <tt>itemCls</tt> will be added to the default class (<tt>'x-form-item'</tt>).</p>
16536      * <p>Since it is applied to the item wrapper (see
16537      * {@link Ext.layout.FormLayout}.{@link Ext.layout.FormLayout#fieldTpl fieldTpl}), it allows
16538      * you to write standard CSS rules that can apply to the field, the label (if specified), or
16539      * any other element within the markup for the field.</p>
16540      * <br><p><b>Note</b>: see the note for <tt>{@link #fieldLabel}</tt>.</p><br>
16541      * Example use:<pre><code>
16542 // Apply a style to the field&#39;s label:
16543 &lt;style>
16544     .required .x-form-item-label {font-weight:bold;color:red;}
16545 &lt;/style>
16546
16547 new Ext.FormPanel({
16548     height: 100,
16549     renderTo: Ext.getBody(),
16550     items: [{
16551         xtype: 'textfield',
16552         fieldLabel: 'Name',
16553         itemCls: 'required' //this label will be styled
16554     },{
16555         xtype: 'textfield',
16556         fieldLabel: 'Favorite Color'
16557     }]
16558 });
16559 </code></pre>
16560      */
16561
16562     /**
16563      * @cfg {String} id
16564      * <p>The <b>unique</b> id of this component (defaults to an {@link #getId auto-assigned id}).
16565      * You should assign an id if you need to be able to access the component later and you do
16566      * not have an object reference available (e.g., using {@link Ext}.{@link Ext#getCmp getCmp}).</p>
16567      * <p>Note that this id will also be used as the element id for the containing HTML element
16568      * that is rendered to the page for this component. This allows you to write id-based CSS
16569      * rules to style the specific instance of this component uniquely, and also to select
16570      * sub-elements using this component's id as the parent.</p>
16571      * <p><b>Note</b>: to avoid complications imposed by a unique <tt>id</tt> also see
16572      * <code>{@link #itemId}</code> and <code>{@link #ref}</code>.</p>
16573      * <p><b>Note</b>: to access the container of an item see <code>{@link #ownerCt}</code>.</p>
16574      */
16575     /**
16576      * @cfg {String} itemId
16577      * <p>An <tt>itemId</tt> can be used as an alternative way to get a reference to a component
16578      * when no object reference is available.  Instead of using an <code>{@link #id}</code> with
16579      * {@link Ext}.{@link Ext#getCmp getCmp}, use <code>itemId</code> with
16580      * {@link Ext.Container}.{@link Ext.Container#getComponent getComponent} which will retrieve
16581      * <code>itemId</code>'s or <tt>{@link #id}</tt>'s. Since <code>itemId</code>'s are an index to the
16582      * container's internal MixedCollection, the <code>itemId</code> is scoped locally to the container --
16583      * avoiding potential conflicts with {@link Ext.ComponentMgr} which requires a <b>unique</b>
16584      * <code>{@link #id}</code>.</p>
16585      * <pre><code>
16586 var c = new Ext.Panel({ //
16587     {@link Ext.BoxComponent#height height}: 300,
16588     {@link #renderTo}: document.body,
16589     {@link Ext.Container#layout layout}: 'auto',
16590     {@link Ext.Container#items items}: [
16591         {
16592             itemId: 'p1',
16593             {@link Ext.Panel#title title}: 'Panel 1',
16594             {@link Ext.BoxComponent#height height}: 150
16595         },
16596         {
16597             itemId: 'p2',
16598             {@link Ext.Panel#title title}: 'Panel 2',
16599             {@link Ext.BoxComponent#height height}: 150
16600         }
16601     ]
16602 })
16603 p1 = c.{@link Ext.Container#getComponent getComponent}('p1'); // not the same as {@link Ext#getCmp Ext.getCmp()}
16604 p2 = p1.{@link #ownerCt}.{@link Ext.Container#getComponent getComponent}('p2'); // reference via a sibling
16605      * </code></pre>
16606      * <p>Also see <tt>{@link #id}</tt> and <code>{@link #ref}</code>.</p>
16607      * <p><b>Note</b>: to access the container of an item see <tt>{@link #ownerCt}</tt>.</p>
16608      */
16609     /**
16610      * @cfg {String} xtype
16611      * The registered <tt>xtype</tt> to create. This config option is not used when passing
16612      * a config object into a constructor. This config option is used only when
16613      * lazy instantiation is being used, and a child item of a Container is being
16614      * specified not as a fully instantiated Component, but as a <i>Component config
16615      * object</i>. The <tt>xtype</tt> will be looked up at render time up to determine what
16616      * type of child Component to create.<br><br>
16617      * The predefined xtypes are listed {@link Ext.Component here}.
16618      * <br><br>
16619      * If you subclass Components to create your own Components, you may register
16620      * them using {@link Ext.ComponentMgr#registerType} in order to be able to
16621      * take advantage of lazy instantiation and rendering.
16622      */
16623     /**
16624      * @cfg {String} ptype
16625      * The registered <tt>ptype</tt> to create. This config option is not used when passing
16626      * a config object into a constructor. This config option is used only when
16627      * lazy instantiation is being used, and a Plugin is being
16628      * specified not as a fully instantiated Component, but as a <i>Component config
16629      * object</i>. The <tt>ptype</tt> will be looked up at render time up to determine what
16630      * type of Plugin to create.<br><br>
16631      * If you create your own Plugins, you may register them using
16632      * {@link Ext.ComponentMgr#registerPlugin} in order to be able to
16633      * take advantage of lazy instantiation and rendering.
16634      */
16635     /**
16636      * @cfg {String} cls
16637      * An optional extra CSS class that will be added to this component's Element (defaults to '').  This can be
16638      * useful for adding customized styles to the component or any of its children using standard CSS rules.
16639      */
16640     /**
16641      * @cfg {String} overCls
16642      * An optional extra CSS class that will be added to this component's Element when the mouse moves
16643      * over the Element, and removed when the mouse moves out. (defaults to '').  This can be
16644      * useful for adding customized 'active' or 'hover' styles to the component or any of its children using standard CSS rules.
16645      */
16646     /**
16647      * @cfg {String} style
16648      * A custom style specification to be applied to this component's Element.  Should be a valid argument to
16649      * {@link Ext.Element#applyStyles}.
16650      * <pre><code>
16651 new Ext.Panel({
16652     title: 'Some Title',
16653     renderTo: Ext.getBody(),
16654     width: 400, height: 300,
16655     layout: 'form',
16656     items: [{
16657         xtype: 'textarea',
16658         style: {
16659             width: '95%',
16660             marginBottom: '10px'
16661         }
16662     },
16663         new Ext.Button({
16664             text: 'Send',
16665             minWidth: '100',
16666             style: {
16667                 marginBottom: '10px'
16668             }
16669         })
16670     ]
16671 });
16672      * </code></pre>
16673      */
16674     /**
16675      * @cfg {String} ctCls
16676      * <p>An optional extra CSS class that will be added to this component's container. This can be useful for
16677      * adding customized styles to the container or any of its children using standard CSS rules.  See
16678      * {@link Ext.layout.ContainerLayout}.{@link Ext.layout.ContainerLayout#extraCls extraCls} also.</p>
16679      * <p><b>Note</b>: <tt>ctCls</tt> defaults to <tt>''</tt> except for the following class
16680      * which assigns a value by default:
16681      * <div class="mdetail-params"><ul>
16682      * <li>{@link Ext.layout.Box Box Layout} : <tt>'x-box-layout-ct'</tt></li>
16683      * </ul></div>
16684      * To configure the above Class with an extra CSS class append to the default.  For example,
16685      * for BoxLayout (Hbox and Vbox):<pre><code>
16686      * ctCls: 'x-box-layout-ct custom-class'
16687      * </code></pre>
16688      * </p>
16689      */
16690     /**
16691      * @cfg {Boolean} disabled
16692      * Render this component disabled (default is false).
16693      */
16694     disabled : false,
16695     /**
16696      * @cfg {Boolean} hidden
16697      * Render this component hidden (default is false). If <tt>true</tt>, the
16698      * {@link #hide} method will be called internally.
16699      */
16700     hidden : false,
16701     /**
16702      * @cfg {Object/Array} plugins
16703      * An object or array of objects that will provide custom functionality for this component.  The only
16704      * requirement for a valid plugin is that it contain an init method that accepts a reference of type Ext.Component.
16705      * When a component is created, if any plugins are available, the component will call the init method on each
16706      * plugin, passing a reference to itself.  Each plugin can then call methods or respond to events on the
16707      * component as needed to provide its functionality.
16708      */
16709     /**
16710      * @cfg {Mixed} applyTo
16711      * <p>Specify the id of the element, a DOM element or an existing Element corresponding to a DIV
16712      * that is already present in the document that specifies some structural markup for this
16713      * component.</p><div><ul>
16714      * <li><b>Description</b> : <ul>
16715      * <div class="sub-desc">When <tt>applyTo</tt> is used, constituent parts of the component can also be specified
16716      * by id or CSS class name within the main element, and the component being created may attempt
16717      * to create its subcomponents from that markup if applicable.</div>
16718      * </ul></li>
16719      * <li><b>Notes</b> : <ul>
16720      * <div class="sub-desc">When using this config, a call to render() is not required.</div>
16721      * <div class="sub-desc">If applyTo is specified, any value passed for {@link #renderTo} will be ignored and the target
16722      * element's parent node will automatically be used as the component's container.</div>
16723      * </ul></li>
16724      * </ul></div>
16725      */
16726     /**
16727      * @cfg {Mixed} renderTo
16728      * <p>Specify the id of the element, a DOM element or an existing Element that this component
16729      * will be rendered into.</p><div><ul>
16730      * <li><b>Notes</b> : <ul>
16731      * <div class="sub-desc">Do <u>not</u> use this option if the Component is to be a child item of
16732      * a {@link Ext.Container Container}. It is the responsibility of the
16733      * {@link Ext.Container Container}'s {@link Ext.Container#layout layout manager}
16734      * to render and manage its child items.</div>
16735      * <div class="sub-desc">When using this config, a call to render() is not required.</div>
16736      * </ul></li>
16737      * </ul></div>
16738      * <p>See <tt>{@link #render}</tt> also.</p>
16739      */
16740     /**
16741      * @cfg {Boolean} stateful
16742      * <p>A flag which causes the Component to attempt to restore the state of
16743      * internal properties from a saved state on startup. The component must have
16744      * either a <code>{@link #stateId}</code> or <code>{@link #id}</code> assigned
16745      * for state to be managed. Auto-generated ids are not guaranteed to be stable
16746      * across page loads and cannot be relied upon to save and restore the same
16747      * state for a component.<p>
16748      * <p>For state saving to work, the state manager's provider must have been
16749      * set to an implementation of {@link Ext.state.Provider} which overrides the
16750      * {@link Ext.state.Provider#set set} and {@link Ext.state.Provider#get get}
16751      * methods to save and recall name/value pairs. A built-in implementation,
16752      * {@link Ext.state.CookieProvider} is available.</p>
16753      * <p>To set the state provider for the current page:</p>
16754      * <pre><code>
16755 Ext.state.Manager.setProvider(new Ext.state.CookieProvider({
16756     expires: new Date(new Date().getTime()+(1000*60*60*24*7)), //7 days from now
16757 }));
16758      * </code></pre>
16759      * <p>A stateful Component attempts to save state when one of the events
16760      * listed in the <code>{@link #stateEvents}</code> configuration fires.</p>
16761      * <p>To save state, a stateful Component first serializes its state by
16762      * calling <b><code>getState</code></b>. By default, this function does
16763      * nothing. The developer must provide an implementation which returns an
16764      * object hash which represents the Component's restorable state.</p>
16765      * <p>The value yielded by getState is passed to {@link Ext.state.Manager#set}
16766      * which uses the configured {@link Ext.state.Provider} to save the object
16767      * keyed by the Component's <code>{@link stateId}</code>, or, if that is not
16768      * specified, its <code>{@link #id}</code>.</p>
16769      * <p>During construction, a stateful Component attempts to <i>restore</i>
16770      * its state by calling {@link Ext.state.Manager#get} passing the
16771      * <code>{@link #stateId}</code>, or, if that is not specified, the
16772      * <code>{@link #id}</code>.</p>
16773      * <p>The resulting object is passed to <b><code>applyState</code></b>.
16774      * The default implementation of <code>applyState</code> simply copies
16775      * properties into the object, but a developer may override this to support
16776      * more behaviour.</p>
16777      * <p>You can perform extra processing on state save and restore by attaching
16778      * handlers to the {@link #beforestaterestore}, {@link #staterestore},
16779      * {@link #beforestatesave} and {@link #statesave} events.</p>
16780      */
16781     /**
16782      * @cfg {String} stateId
16783      * The unique id for this component to use for state management purposes
16784      * (defaults to the component id if one was set, otherwise null if the
16785      * component is using a generated id).
16786      * <p>See <code>{@link #stateful}</code> for an explanation of saving and
16787      * restoring Component state.</p>
16788      */
16789     /**
16790      * @cfg {Array} stateEvents
16791      * <p>An array of events that, when fired, should trigger this component to
16792      * save its state (defaults to none). <code>stateEvents</code> may be any type
16793      * of event supported by this component, including browser or custom events
16794      * (e.g., <tt>['click', 'customerchange']</tt>).</p>
16795      * <p>See <code>{@link #stateful}</code> for an explanation of saving and
16796      * restoring Component state.</p>
16797      */
16798     /**
16799      * @cfg {Mixed} autoEl
16800      * <p>A tag name or {@link Ext.DomHelper DomHelper} spec used to create the {@link #getEl Element} which will
16801      * encapsulate this Component.</p>
16802      * <p>You do not normally need to specify this. For the base classes {@link Ext.Component}, {@link Ext.BoxComponent},
16803      * and {@link Ext.Container}, this defaults to <b><tt>'div'</tt></b>. The more complex Ext classes use a more complex
16804      * DOM structure created by their own onRender methods.</p>
16805      * <p>This is intended to allow the developer to create application-specific utility Components encapsulated by
16806      * different DOM elements. Example usage:</p><pre><code>
16807 {
16808     xtype: 'box',
16809     autoEl: {
16810         tag: 'img',
16811         src: 'http://www.example.com/example.jpg'
16812     }
16813 }, {
16814     xtype: 'box',
16815     autoEl: {
16816         tag: 'blockquote',
16817         html: 'autoEl is cool!'
16818     }
16819 }, {
16820     xtype: 'container',
16821     autoEl: 'ul',
16822     cls: 'ux-unordered-list',
16823     items: {
16824         xtype: 'box',
16825         autoEl: 'li',
16826         html: 'First list item'
16827     }
16828 }
16829 </code></pre>
16830      */
16831     autoEl : 'div',
16832
16833     /**
16834      * @cfg {String} disabledClass
16835      * CSS class added to the component when it is disabled (defaults to 'x-item-disabled').
16836      */
16837     disabledClass : 'x-item-disabled',
16838     /**
16839      * @cfg {Boolean} allowDomMove
16840      * Whether the component can move the Dom node when rendering (defaults to true).
16841      */
16842     allowDomMove : true,
16843     /**
16844      * @cfg {Boolean} autoShow
16845      * True if the component should check for hidden classes (e.g. 'x-hidden' or 'x-hide-display') and remove
16846      * them on render (defaults to false).
16847      */
16848     autoShow : false,
16849     /**
16850      * @cfg {String} hideMode
16851      * <p>How this component should be hidden. Supported values are <tt>'visibility'</tt>
16852      * (css visibility), <tt>'offsets'</tt> (negative offset position) and <tt>'display'</tt>
16853      * (css display).</p>
16854      * <br><p><b>Note</b>: the default of <tt>'display'</tt> is generally preferred
16855      * since items are automatically laid out when they are first shown (no sizing
16856      * is done while hidden).</p>
16857      */
16858     hideMode : 'display',
16859     /**
16860      * @cfg {Boolean} hideParent
16861      * True to hide and show the component's container when hide/show is called on the component, false to hide
16862      * and show the component itself (defaults to false).  For example, this can be used as a shortcut for a hide
16863      * button on a window by setting hide:true on the button when adding it to its parent container.
16864      */
16865     hideParent : false,
16866     /**
16867      * <p>The {@link Ext.Element} which encapsulates this Component. Read-only.</p>
16868      * <p>This will <i>usually</i> be a &lt;DIV> element created by the class's onRender method, but
16869      * that may be overridden using the <code>{@link #autoEl}</code> config.</p>
16870      * <br><p><b>Note</b>: this element will not be available until this Component has been rendered.</p><br>
16871      * <p>To add listeners for <b>DOM events</b> to this Component (as opposed to listeners
16872      * for this Component's own Observable events), see the {@link Ext.util.Observable#listeners listeners}
16873      * config for a suggestion, or use a render listener directly:</p><pre><code>
16874 new Ext.Panel({
16875     title: 'The Clickable Panel',
16876     listeners: {
16877         render: function(p) {
16878             // Append the Panel to the click handler&#39;s argument list.
16879             p.getEl().on('click', handlePanelClick.createDelegate(null, [p], true));
16880         },
16881         single: true  // Remove the listener after first invocation
16882     }
16883 });
16884 </code></pre>
16885      * <p>See also <tt>{@link #getEl getEl}</p>
16886      * @type Ext.Element
16887      * @property el
16888      */
16889     /**
16890      * This Component's owner {@link Ext.Container Container} (defaults to undefined, and is set automatically when
16891      * this Component is added to a Container).  Read-only.
16892      * <p><b>Note</b>: to access items within the Container see <tt>{@link #itemId}</tt>.</p>
16893      * @type Ext.Container
16894      * @property ownerCt
16895      */
16896     /**
16897      * True if this component is hidden. Read-only.
16898      * @type Boolean
16899      * @property hidden
16900      */
16901     /**
16902      * True if this component is disabled. Read-only.
16903      * @type Boolean
16904      * @property disabled
16905      */
16906     /**
16907      * True if this component has been rendered. Read-only.
16908      * @type Boolean
16909      * @property rendered
16910      */
16911     rendered : false,
16912
16913     /**
16914      * @cfg {String} contentEl
16915      * <p>Optional. Specify an existing HTML element, or the <code>id</code> of an existing HTML element to use as the content
16916      * for this component.</p>
16917      * <ul>
16918      * <li><b>Description</b> :
16919      * <div class="sub-desc">This config option is used to take an existing HTML element and place it in the layout element
16920      * of a new component (it simply moves the specified DOM element <i>after the Component is rendered</i> to use as the content.</div></li>
16921      * <li><b>Notes</b> :
16922      * <div class="sub-desc">The specified HTML element is appended to the layout element of the component <i>after any configured
16923      * {@link #html HTML} has been inserted</i>, and so the document will not contain this element at the time the {@link #render} event is fired.</div>
16924      * <div class="sub-desc">The specified HTML element used will not participate in any <code><b>{@link Ext.Container#layout layout}</b></code>
16925      * scheme that the Component may use. It is just HTML. Layouts operate on child <code><b>{@link Ext.Container#items items}</b></code>.</div>
16926      * <div class="sub-desc">Add either the <code>x-hidden</code> or the <code>x-hide-display</code> CSS class to
16927      * prevent a brief flicker of the content before it is rendered to the panel.</div></li>
16928      * </ul>
16929      */
16930     /**
16931      * @cfg {String/Object} html
16932      * An HTML fragment, or a {@link Ext.DomHelper DomHelper} specification to use as the layout element
16933      * content (defaults to ''). The HTML content is added after the component is rendered,
16934      * so the document will not contain this HTML at the time the {@link #render} event is fired.
16935      * This content is inserted into the body <i>before</i> any configured {@link #contentEl} is appended.
16936      */
16937
16938     /**
16939      * @cfg {Mixed} tpl
16940      * An <bold>{@link Ext.Template}</bold>, <bold>{@link Ext.XTemplate}</bold>
16941      * or an array of strings to form an Ext.XTemplate.
16942      * Used in conjunction with the <code>{@link #data}</code> and
16943      * <code>{@link #tplWriteMode}</code> configurations.
16944      */
16945
16946     /**
16947      * @cfg {String} tplWriteMode The Ext.(X)Template method to use when
16948      * updating the content area of the Component. Defaults to <tt>'overwrite'</tt>
16949      * (see <code>{@link Ext.XTemplate#overwrite}</code>).
16950      */
16951     tplWriteMode : 'overwrite',
16952
16953     /**
16954      * @cfg {Mixed} data
16955      * The initial set of data to apply to the <code>{@link #tpl}</code> to
16956      * update the content area of the Component.
16957      */
16958     
16959     /**
16960      * @cfg {Array} bubbleEvents
16961      * <p>An array of events that, when fired, should be bubbled to any parent container.
16962      * See {@link Ext.util.Observable#enableBubble}.
16963      * Defaults to <tt>[]</tt>.
16964      */
16965     bubbleEvents: [],
16966
16967
16968     // private
16969     ctype : 'Ext.Component',
16970
16971     // private
16972     actionMode : 'el',
16973
16974     // private
16975     getActionEl : function(){
16976         return this[this.actionMode];
16977     },
16978
16979     initPlugin : function(p){
16980         if(p.ptype && !Ext.isFunction(p.init)){
16981             p = Ext.ComponentMgr.createPlugin(p);
16982         }else if(Ext.isString(p)){
16983             p = Ext.ComponentMgr.createPlugin({
16984                 ptype: p
16985             });
16986         }
16987         p.init(this);
16988         return p;
16989     },
16990
16991     /* // protected
16992      * Function to be implemented by Component subclasses to be part of standard component initialization flow (it is empty by default).
16993      * <pre><code>
16994 // Traditional constructor:
16995 Ext.Foo = function(config){
16996     // call superclass constructor:
16997     Ext.Foo.superclass.constructor.call(this, config);
16998
16999     this.addEvents({
17000         // add events
17001     });
17002 };
17003 Ext.extend(Ext.Foo, Ext.Bar, {
17004    // class body
17005 }
17006
17007 // initComponent replaces the constructor:
17008 Ext.Foo = Ext.extend(Ext.Bar, {
17009     initComponent : function(){
17010         // call superclass initComponent
17011         Ext.Container.superclass.initComponent.call(this);
17012
17013         this.addEvents({
17014             // add events
17015         });
17016     }
17017 }
17018 </code></pre>
17019      */
17020     initComponent : function(){
17021         /*
17022          * this is double processing, however it allows people to be able to do
17023          * Ext.apply(this, {
17024          *     listeners: {
17025          *         //here
17026          *     }
17027          * });
17028          * MyClass.superclass.initComponent.call(this);
17029          */
17030         if(this.listeners){
17031             this.on(this.listeners);
17032             delete this.listeners;
17033         }
17034         this.enableBubble(this.bubbleEvents);
17035     },
17036
17037     /**
17038      * <p>Render this Component into the passed HTML element.</p>
17039      * <p><b>If you are using a {@link Ext.Container Container} object to house this Component, then
17040      * do not use the render method.</b></p>
17041      * <p>A Container's child Components are rendered by that Container's
17042      * {@link Ext.Container#layout layout} manager when the Container is first rendered.</p>
17043      * <p>Certain layout managers allow dynamic addition of child components. Those that do
17044      * include {@link Ext.layout.CardLayout}, {@link Ext.layout.AnchorLayout},
17045      * {@link Ext.layout.FormLayout}, {@link Ext.layout.TableLayout}.</p>
17046      * <p>If the Container is already rendered when a new child Component is added, you may need to call
17047      * the Container's {@link Ext.Container#doLayout doLayout} to refresh the view which causes any
17048      * unrendered child Components to be rendered. This is required so that you can add multiple
17049      * child components if needed while only refreshing the layout once.</p>
17050      * <p>When creating complex UIs, it is important to remember that sizing and positioning
17051      * of child items is the responsibility of the Container's {@link Ext.Container#layout layout} manager.
17052      * If you expect child items to be sized in response to user interactions, you must
17053      * configure the Container with a layout manager which creates and manages the type of layout you
17054      * have in mind.</p>
17055      * <p><b>Omitting the Container's {@link Ext.Container#layout layout} config means that a basic
17056      * layout manager is used which does nothing but render child components sequentially into the
17057      * Container. No sizing or positioning will be performed in this situation.</b></p>
17058      * @param {Element/HTMLElement/String} container (optional) The element this Component should be
17059      * rendered into. If it is being created from existing markup, this should be omitted.
17060      * @param {String/Number} position (optional) The element ID or DOM node index within the container <b>before</b>
17061      * which this component will be inserted (defaults to appending to the end of the container)
17062      */
17063     render : function(container, position){
17064         if(!this.rendered && this.fireEvent('beforerender', this) !== false){
17065             if(!container && this.el){
17066                 this.el = Ext.get(this.el);
17067                 container = this.el.dom.parentNode;
17068                 this.allowDomMove = false;
17069             }
17070             this.container = Ext.get(container);
17071             if(this.ctCls){
17072                 this.container.addClass(this.ctCls);
17073             }
17074             this.rendered = true;
17075             if(position !== undefined){
17076                 if(Ext.isNumber(position)){
17077                     position = this.container.dom.childNodes[position];
17078                 }else{
17079                     position = Ext.getDom(position);
17080                 }
17081             }
17082             this.onRender(this.container, position || null);
17083             if(this.autoShow){
17084                 this.el.removeClass(['x-hidden','x-hide-' + this.hideMode]);
17085             }
17086             if(this.cls){
17087                 this.el.addClass(this.cls);
17088                 delete this.cls;
17089             }
17090             if(this.style){
17091                 this.el.applyStyles(this.style);
17092                 delete this.style;
17093             }
17094             if(this.overCls){
17095                 this.el.addClassOnOver(this.overCls);
17096             }
17097             this.fireEvent('render', this);
17098
17099
17100             // Populate content of the component with html, contentEl or
17101             // a tpl.
17102             var contentTarget = this.getContentTarget();
17103             if (this.html){
17104                 contentTarget.update(Ext.DomHelper.markup(this.html));
17105                 delete this.html;
17106             }
17107             if (this.contentEl){
17108                 var ce = Ext.getDom(this.contentEl);
17109                 Ext.fly(ce).removeClass(['x-hidden', 'x-hide-display']);
17110                 contentTarget.appendChild(ce);
17111             }
17112             if (this.tpl) {
17113                 if (!this.tpl.compile) {
17114                     this.tpl = new Ext.XTemplate(this.tpl);
17115                 }
17116                 if (this.data) {
17117                     this.tpl[this.tplWriteMode](contentTarget, this.data);
17118                     delete this.data;
17119                 }
17120             }
17121             this.afterRender(this.container);
17122
17123
17124             if(this.hidden){
17125                 // call this so we don't fire initial hide events.
17126                 this.doHide();
17127             }
17128             if(this.disabled){
17129                 // pass silent so the event doesn't fire the first time.
17130                 this.disable(true);
17131             }
17132
17133             if(this.stateful !== false){
17134                 this.initStateEvents();
17135             }
17136             this.fireEvent('afterrender', this);
17137         }
17138         return this;
17139     },
17140
17141
17142     /**
17143      * Update the content area of a component.
17144      * @param {Mixed} htmlOrData
17145      * If this component has been configured with a template via the tpl config
17146      * then it will use this argument as data to populate the template.
17147      * If this component was not configured with a template, the components
17148      * content area will be updated via Ext.Element update
17149      * @param {Boolean} loadScripts
17150      * (optional) Only legitimate when using the html configuration. Defaults to false
17151      * @param {Function} callback
17152      * (optional) Only legitimate when using the html configuration. Callback to execute when scripts have finished loading
17153      */
17154     update: function(htmlOrData, loadScripts, cb) {
17155         var contentTarget = this.getContentTarget();
17156         if (this.tpl && typeof htmlOrData !== "string") {
17157             this.tpl[this.tplWriteMode](contentTarget, htmlOrData || {});
17158         } else {
17159             var html = Ext.isObject(htmlOrData) ? Ext.DomHelper.markup(htmlOrData) : htmlOrData;
17160             contentTarget.update(html, loadScripts, cb);
17161         }
17162     },
17163
17164
17165     /**
17166      * @private
17167      * Method to manage awareness of when components are added to their
17168      * respective Container, firing an added event.
17169      * References are established at add time rather than at render time.
17170      * @param {Ext.Container} container Container which holds the component
17171      * @param {number} pos Position at which the component was added
17172      */
17173     onAdded : function(container, pos) {
17174         this.ownerCt = container;
17175         this.initRef();
17176         this.fireEvent('added', this, container, pos);
17177     },
17178
17179     /**
17180      * @private
17181      * Method to manage awareness of when components are removed from their
17182      * respective Container, firing an removed event. References are properly
17183      * cleaned up after removing a component from its owning container.
17184      */
17185     onRemoved : function() {
17186         this.removeRef();
17187         this.fireEvent('removed', this, this.ownerCt);
17188         delete this.ownerCt;
17189     },
17190
17191     /**
17192      * @private
17193      * Method to establish a reference to a component.
17194      */
17195     initRef : function() {
17196         /**
17197          * @cfg {String} ref
17198          * <p>A path specification, relative to the Component's <code>{@link #ownerCt}</code>
17199          * specifying into which ancestor Container to place a named reference to this Component.</p>
17200          * <p>The ancestor axis can be traversed by using '/' characters in the path.
17201          * For example, to put a reference to a Toolbar Button into <i>the Panel which owns the Toolbar</i>:</p><pre><code>
17202 var myGrid = new Ext.grid.EditorGridPanel({
17203     title: 'My EditorGridPanel',
17204     store: myStore,
17205     colModel: myColModel,
17206     tbar: [{
17207         text: 'Save',
17208         handler: saveChanges,
17209         disabled: true,
17210         ref: '../saveButton'
17211     }],
17212     listeners: {
17213         afteredit: function() {
17214 //          The button reference is in the GridPanel
17215             myGrid.saveButton.enable();
17216         }
17217     }
17218 });
17219 </code></pre>
17220          * <p>In the code above, if the <code>ref</code> had been <code>'saveButton'</code>
17221          * the reference would have been placed into the Toolbar. Each '/' in the <code>ref</code>
17222          * moves up one level from the Component's <code>{@link #ownerCt}</code>.</p>
17223          * <p>Also see the <code>{@link #added}</code> and <code>{@link #removed}</code> events.</p>
17224          */
17225         if(this.ref && !this.refOwner){
17226             var levels = this.ref.split('/'),
17227                 last = levels.length,
17228                 i = 0,
17229                 t = this;
17230
17231             while(t && i < last){
17232                 t = t.ownerCt;
17233                 ++i;
17234             }
17235             if(t){
17236                 t[this.refName = levels[--i]] = this;
17237                 /**
17238                  * @type Ext.Container
17239                  * @property refOwner
17240                  * The ancestor Container into which the {@link #ref} reference was inserted if this Component
17241                  * is a child of a Container, and has been configured with a <code>ref</code>.
17242                  */
17243                 this.refOwner = t;
17244             }
17245         }
17246     },
17247
17248     removeRef : function() {
17249         if (this.refOwner && this.refName) {
17250             delete this.refOwner[this.refName];
17251             delete this.refOwner;
17252         }
17253     },
17254
17255     // private
17256     initState : function(){
17257         if(Ext.state.Manager){
17258             var id = this.getStateId();
17259             if(id){
17260                 var state = Ext.state.Manager.get(id);
17261                 if(state){
17262                     if(this.fireEvent('beforestaterestore', this, state) !== false){
17263                         this.applyState(Ext.apply({}, state));
17264                         this.fireEvent('staterestore', this, state);
17265                     }
17266                 }
17267             }
17268         }
17269     },
17270
17271     // private
17272     getStateId : function(){
17273         return this.stateId || ((/^(ext-comp-|ext-gen)/).test(String(this.id)) ? null : this.id);
17274     },
17275
17276     // private
17277     initStateEvents : function(){
17278         if(this.stateEvents){
17279             for(var i = 0, e; e = this.stateEvents[i]; i++){
17280                 this.on(e, this.saveState, this, {delay:100});
17281             }
17282         }
17283     },
17284
17285     // private
17286     applyState : function(state){
17287         if(state){
17288             Ext.apply(this, state);
17289         }
17290     },
17291
17292     // private
17293     getState : function(){
17294         return null;
17295     },
17296
17297     // private
17298     saveState : function(){
17299         if(Ext.state.Manager && this.stateful !== false){
17300             var id = this.getStateId();
17301             if(id){
17302                 var state = this.getState();
17303                 if(this.fireEvent('beforestatesave', this, state) !== false){
17304                     Ext.state.Manager.set(id, state);
17305                     this.fireEvent('statesave', this, state);
17306                 }
17307             }
17308         }
17309     },
17310
17311     /**
17312      * Apply this component to existing markup that is valid. With this function, no call to render() is required.
17313      * @param {String/HTMLElement} el
17314      */
17315     applyToMarkup : function(el){
17316         this.allowDomMove = false;
17317         this.el = Ext.get(el);
17318         this.render(this.el.dom.parentNode);
17319     },
17320
17321     /**
17322      * Adds a CSS class to the component's underlying element.
17323      * @param {string} cls The CSS class name to add
17324      * @return {Ext.Component} this
17325      */
17326     addClass : function(cls){
17327         if(this.el){
17328             this.el.addClass(cls);
17329         }else{
17330             this.cls = this.cls ? this.cls + ' ' + cls : cls;
17331         }
17332         return this;
17333     },
17334
17335     /**
17336      * Removes a CSS class from the component's underlying element.
17337      * @param {string} cls The CSS class name to remove
17338      * @return {Ext.Component} this
17339      */
17340     removeClass : function(cls){
17341         if(this.el){
17342             this.el.removeClass(cls);
17343         }else if(this.cls){
17344             this.cls = this.cls.split(' ').remove(cls).join(' ');
17345         }
17346         return this;
17347     },
17348
17349     // private
17350     // default function is not really useful
17351     onRender : function(ct, position){
17352         if(!this.el && this.autoEl){
17353             if(Ext.isString(this.autoEl)){
17354                 this.el = document.createElement(this.autoEl);
17355             }else{
17356                 var div = document.createElement('div');
17357                 Ext.DomHelper.overwrite(div, this.autoEl);
17358                 this.el = div.firstChild;
17359             }
17360             if (!this.el.id) {
17361                 this.el.id = this.getId();
17362             }
17363         }
17364         if(this.el){
17365             this.el = Ext.get(this.el);
17366             if(this.allowDomMove !== false){
17367                 ct.dom.insertBefore(this.el.dom, position);
17368                 if (div) {
17369                     Ext.removeNode(div);
17370                     div = null;
17371                 }
17372             }
17373         }
17374     },
17375
17376     // private
17377     getAutoCreate : function(){
17378         var cfg = Ext.isObject(this.autoCreate) ?
17379                       this.autoCreate : Ext.apply({}, this.defaultAutoCreate);
17380         if(this.id && !cfg.id){
17381             cfg.id = this.id;
17382         }
17383         return cfg;
17384     },
17385
17386     // private
17387     afterRender : Ext.emptyFn,
17388
17389     /**
17390      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
17391      * removing the component from its {@link Ext.Container} (if applicable) and unregistering it from
17392      * {@link Ext.ComponentMgr}.  Destruction is generally handled automatically by the framework and this method
17393      * should usually not need to be called directly.
17394      *
17395      */
17396     destroy : function(){
17397         if(!this.isDestroyed){
17398             if(this.fireEvent('beforedestroy', this) !== false){
17399                 this.destroying = true;
17400                 this.beforeDestroy();
17401                 if(this.ownerCt && this.ownerCt.remove){
17402                     this.ownerCt.remove(this, false);
17403                 }
17404                 if(this.rendered){
17405                     this.el.remove();
17406                     if(this.actionMode == 'container' || this.removeMode == 'container'){
17407                         this.container.remove();
17408                     }
17409                 }
17410                 // Stop any buffered tasks
17411                 if(this.focusTask && this.focusTask.cancel){
17412                     this.focusTask.cancel();
17413                 }
17414                 this.onDestroy();
17415                 Ext.ComponentMgr.unregister(this);
17416                 this.fireEvent('destroy', this);
17417                 this.purgeListeners();
17418                 this.destroying = false;
17419                 this.isDestroyed = true;
17420             }
17421         }
17422     },
17423
17424     deleteMembers : function(){
17425         var args = arguments;
17426         for(var i = 0, len = args.length; i < len; ++i){
17427             delete this[args[i]];
17428         }
17429     },
17430
17431     // private
17432     beforeDestroy : Ext.emptyFn,
17433
17434     // private
17435     onDestroy  : Ext.emptyFn,
17436
17437     /**
17438      * <p>Returns the {@link Ext.Element} which encapsulates this Component.</p>
17439      * <p>This will <i>usually</i> be a &lt;DIV> element created by the class's onRender method, but
17440      * that may be overridden using the {@link #autoEl} config.</p>
17441      * <br><p><b>Note</b>: this element will not be available until this Component has been rendered.</p><br>
17442      * <p>To add listeners for <b>DOM events</b> to this Component (as opposed to listeners
17443      * for this Component's own Observable events), see the {@link #listeners} config for a suggestion,
17444      * or use a render listener directly:</p><pre><code>
17445 new Ext.Panel({
17446     title: 'The Clickable Panel',
17447     listeners: {
17448         render: function(p) {
17449             // Append the Panel to the click handler&#39;s argument list.
17450             p.getEl().on('click', handlePanelClick.createDelegate(null, [p], true));
17451         },
17452         single: true  // Remove the listener after first invocation
17453     }
17454 });
17455 </code></pre>
17456      * @return {Ext.Element} The Element which encapsulates this Component.
17457      */
17458     getEl : function(){
17459         return this.el;
17460     },
17461
17462     // private
17463     getContentTarget : function(){
17464         return this.el;
17465     },
17466
17467     /**
17468      * Returns the <code>id</code> of this component or automatically generates and
17469      * returns an <code>id</code> if an <code>id</code> is not defined yet:<pre><code>
17470      * 'ext-comp-' + (++Ext.Component.AUTO_ID)
17471      * </code></pre>
17472      * @return {String} id
17473      */
17474     getId : function(){
17475         return this.id || (this.id = 'ext-comp-' + (++Ext.Component.AUTO_ID));
17476     },
17477
17478     /**
17479      * Returns the <code>{@link #itemId}</code> of this component.  If an
17480      * <code>{@link #itemId}</code> was not assigned through configuration the
17481      * <code>id</code> is returned using <code>{@link #getId}</code>.
17482      * @return {String}
17483      */
17484     getItemId : function(){
17485         return this.itemId || this.getId();
17486     },
17487
17488     /**
17489      * Try to focus this component.
17490      * @param {Boolean} selectText (optional) If applicable, true to also select the text in this component
17491      * @param {Boolean/Number} delay (optional) Delay the focus this number of milliseconds (true for 10 milliseconds)
17492      * @return {Ext.Component} this
17493      */
17494     focus : function(selectText, delay){
17495         if(delay){
17496             this.focusTask = new Ext.util.DelayedTask(this.focus, this, [selectText, false]);
17497             this.focusTask.delay(Ext.isNumber(delay) ? delay : 10);
17498             return this;
17499         }
17500         if(this.rendered && !this.isDestroyed){
17501             this.el.focus();
17502             if(selectText === true){
17503                 this.el.dom.select();
17504             }
17505         }
17506         return this;
17507     },
17508
17509     // private
17510     blur : function(){
17511         if(this.rendered){
17512             this.el.blur();
17513         }
17514         return this;
17515     },
17516
17517     /**
17518      * Disable this component and fire the 'disable' event.
17519      * @return {Ext.Component} this
17520      */
17521     disable : function(/* private */ silent){
17522         if(this.rendered){
17523             this.onDisable();
17524         }
17525         this.disabled = true;
17526         if(silent !== true){
17527             this.fireEvent('disable', this);
17528         }
17529         return this;
17530     },
17531
17532     // private
17533     onDisable : function(){
17534         this.getActionEl().addClass(this.disabledClass);
17535         this.el.dom.disabled = true;
17536     },
17537
17538     /**
17539      * Enable this component and fire the 'enable' event.
17540      * @return {Ext.Component} this
17541      */
17542     enable : function(){
17543         if(this.rendered){
17544             this.onEnable();
17545         }
17546         this.disabled = false;
17547         this.fireEvent('enable', this);
17548         return this;
17549     },
17550
17551     // private
17552     onEnable : function(){
17553         this.getActionEl().removeClass(this.disabledClass);
17554         this.el.dom.disabled = false;
17555     },
17556
17557     /**
17558      * Convenience function for setting disabled/enabled by boolean.
17559      * @param {Boolean} disabled
17560      * @return {Ext.Component} this
17561      */
17562     setDisabled : function(disabled){
17563         return this[disabled ? 'disable' : 'enable']();
17564     },
17565
17566     /**
17567      * Show this component.  Listen to the '{@link #beforeshow}' event and return
17568      * <tt>false</tt> to cancel showing the component.  Fires the '{@link #show}'
17569      * event after showing the component.
17570      * @return {Ext.Component} this
17571      */
17572     show : function(){
17573         if(this.fireEvent('beforeshow', this) !== false){
17574             this.hidden = false;
17575             if(this.autoRender){
17576                 this.render(Ext.isBoolean(this.autoRender) ? Ext.getBody() : this.autoRender);
17577             }
17578             if(this.rendered){
17579                 this.onShow();
17580             }
17581             this.fireEvent('show', this);
17582         }
17583         return this;
17584     },
17585
17586     // private
17587     onShow : function(){
17588         this.getVisibilityEl().removeClass('x-hide-' + this.hideMode);
17589     },
17590
17591     /**
17592      * Hide this component.  Listen to the '{@link #beforehide}' event and return
17593      * <tt>false</tt> to cancel hiding the component.  Fires the '{@link #hide}'
17594      * event after hiding the component. Note this method is called internally if
17595      * the component is configured to be <code>{@link #hidden}</code>.
17596      * @return {Ext.Component} this
17597      */
17598     hide : function(){
17599         if(this.fireEvent('beforehide', this) !== false){
17600             this.doHide();
17601             this.fireEvent('hide', this);
17602         }
17603         return this;
17604     },
17605
17606     // private
17607     doHide: function(){
17608         this.hidden = true;
17609         if(this.rendered){
17610             this.onHide();
17611         }
17612     },
17613
17614     // private
17615     onHide : function(){
17616         this.getVisibilityEl().addClass('x-hide-' + this.hideMode);
17617     },
17618
17619     // private
17620     getVisibilityEl : function(){
17621         return this.hideParent ? this.container : this.getActionEl();
17622     },
17623
17624     /**
17625      * Convenience function to hide or show this component by boolean.
17626      * @param {Boolean} visible True to show, false to hide
17627      * @return {Ext.Component} this
17628      */
17629     setVisible : function(visible){
17630         return this[visible ? 'show' : 'hide']();
17631     },
17632
17633     /**
17634      * Returns true if this component is visible.
17635      * @return {Boolean} True if this component is visible, false otherwise.
17636      */
17637     isVisible : function(){
17638         return this.rendered && this.getVisibilityEl().isVisible();
17639     },
17640
17641     /**
17642      * Clone the current component using the original config values passed into this instance by default.
17643      * @param {Object} overrides A new config containing any properties to override in the cloned version.
17644      * An id property can be passed on this object, otherwise one will be generated to avoid duplicates.
17645      * @return {Ext.Component} clone The cloned copy of this component
17646      */
17647     cloneConfig : function(overrides){
17648         overrides = overrides || {};
17649         var id = overrides.id || Ext.id();
17650         var cfg = Ext.applyIf(overrides, this.initialConfig);
17651         cfg.id = id; // prevent dup id
17652         return new this.constructor(cfg);
17653     },
17654
17655     /**
17656      * Gets the xtype for this component as registered with {@link Ext.ComponentMgr}. For a list of all
17657      * available xtypes, see the {@link Ext.Component} header. Example usage:
17658      * <pre><code>
17659 var t = new Ext.form.TextField();
17660 alert(t.getXType());  // alerts 'textfield'
17661 </code></pre>
17662      * @return {String} The xtype
17663      */
17664     getXType : function(){
17665         return this.constructor.xtype;
17666     },
17667
17668     /**
17669      * <p>Tests whether or not this Component is of a specific xtype. This can test whether this Component is descended
17670      * from the xtype (default) or whether it is directly of the xtype specified (shallow = true).</p>
17671      * <p><b>If using your own subclasses, be aware that a Component must register its own xtype
17672      * to participate in determination of inherited xtypes.</b></p>
17673      * <p>For a list of all available xtypes, see the {@link Ext.Component} header.</p>
17674      * <p>Example usage:</p>
17675      * <pre><code>
17676 var t = new Ext.form.TextField();
17677 var isText = t.isXType('textfield');        // true
17678 var isBoxSubclass = t.isXType('box');       // true, descended from BoxComponent
17679 var isBoxInstance = t.isXType('box', true); // false, not a direct BoxComponent instance
17680 </code></pre>
17681      * @param {String/Ext.Component/Class} xtype The xtype to check for this Component. Note that the the component can either be an instance
17682      * or a component class:
17683      * <pre><code>
17684 var c = new Ext.Component();
17685 console.log(c.isXType(c));
17686 console.log(c.isXType(Ext.Component)); 
17687 </code></pre>
17688      * @param {Boolean} shallow (optional) False to check whether this Component is descended from the xtype (this is
17689      * the default), or true to check whether this Component is directly of the specified xtype.
17690      * @return {Boolean} True if this component descends from the specified xtype, false otherwise.
17691      */
17692     isXType : function(xtype, shallow){
17693         //assume a string by default
17694         if (Ext.isFunction(xtype)){
17695             xtype = xtype.xtype; //handle being passed the class, e.g. Ext.Component
17696         }else if (Ext.isObject(xtype)){
17697             xtype = xtype.constructor.xtype; //handle being passed an instance
17698         }
17699
17700         return !shallow ? ('/' + this.getXTypes() + '/').indexOf('/' + xtype + '/') != -1 : this.constructor.xtype == xtype;
17701     },
17702
17703     /**
17704      * <p>Returns this Component's xtype hierarchy as a slash-delimited string. For a list of all
17705      * available xtypes, see the {@link Ext.Component} header.</p>
17706      * <p><b>If using your own subclasses, be aware that a Component must register its own xtype
17707      * to participate in determination of inherited xtypes.</b></p>
17708      * <p>Example usage:</p>
17709      * <pre><code>
17710 var t = new Ext.form.TextField();
17711 alert(t.getXTypes());  // alerts 'component/box/field/textfield'
17712 </code></pre>
17713      * @return {String} The xtype hierarchy string
17714      */
17715     getXTypes : function(){
17716         var tc = this.constructor;
17717         if(!tc.xtypes){
17718             var c = [], sc = this;
17719             while(sc && sc.constructor.xtype){
17720                 c.unshift(sc.constructor.xtype);
17721                 sc = sc.constructor.superclass;
17722             }
17723             tc.xtypeChain = c;
17724             tc.xtypes = c.join('/');
17725         }
17726         return tc.xtypes;
17727     },
17728
17729     /**
17730      * Find a container above this component at any level by a custom function. If the passed function returns
17731      * true, the container will be returned.
17732      * @param {Function} fn The custom function to call with the arguments (container, this component).
17733      * @return {Ext.Container} The first Container for which the custom function returns true
17734      */
17735     findParentBy : function(fn) {
17736         for (var p = this.ownerCt; (p != null) && !fn(p, this); p = p.ownerCt);
17737         return p || null;
17738     },
17739
17740     /**
17741      * Find a container above this component at any level by xtype or class
17742      * @param {String/Ext.Component/Class} xtype The xtype to check for this Component. Note that the the component can either be an instance
17743      * or a component class:
17744      * @param {Boolean} shallow (optional) False to check whether this Component is descended from the xtype (this is
17745      * the default), or true to check whether this Component is directly of the specified xtype.
17746      * @return {Ext.Container} The first Container which matches the given xtype or class
17747      */
17748     findParentByType : function(xtype, shallow){
17749         return this.findParentBy(function(c){
17750             return c.isXType(xtype, shallow);
17751         });
17752     },
17753     
17754     /**
17755      * Bubbles up the component/container heirarchy, calling the specified function with each component. The scope (<i>this</i>) of
17756      * function call will be the scope provided or the current component. The arguments to the function
17757      * will be the args provided or the current component. If the function returns false at any point,
17758      * the bubble is stopped.
17759      * @param {Function} fn The function to call
17760      * @param {Object} scope (optional) The scope of the function (defaults to current node)
17761      * @param {Array} args (optional) The args to call the function with (default to passing the current component)
17762      * @return {Ext.Component} this
17763      */
17764     bubble : function(fn, scope, args){
17765         var p = this;
17766         while(p){
17767             if(fn.apply(scope || p, args || [p]) === false){
17768                 break;
17769             }
17770             p = p.ownerCt;
17771         }
17772         return this;
17773     },
17774
17775     // protected
17776     getPositionEl : function(){
17777         return this.positionEl || this.el;
17778     },
17779
17780     // private
17781     purgeListeners : function(){
17782         Ext.Component.superclass.purgeListeners.call(this);
17783         if(this.mons){
17784             this.on('beforedestroy', this.clearMons, this, {single: true});
17785         }
17786     },
17787
17788     // private
17789     clearMons : function(){
17790         Ext.each(this.mons, function(m){
17791             m.item.un(m.ename, m.fn, m.scope);
17792         }, this);
17793         this.mons = [];
17794     },
17795
17796     // private
17797     createMons: function(){
17798         if(!this.mons){
17799             this.mons = [];
17800             this.on('beforedestroy', this.clearMons, this, {single: true});
17801         }
17802     },
17803
17804     /**
17805      * <p>Adds listeners to any Observable object (or Elements) which are automatically removed when this Component
17806      * is destroyed. Usage:</p><code><pre>
17807 myGridPanel.mon(myGridPanel.getSelectionModel(), 'selectionchange', handleSelectionChange, null, {buffer: 50});
17808 </pre></code>
17809      * <p>or:</p><code><pre>
17810 myGridPanel.mon(myGridPanel.getSelectionModel(), {
17811     selectionchange: handleSelectionChange,
17812     buffer: 50
17813 });
17814 </pre></code>
17815      * @param {Observable|Element} item The item to which to add a listener/listeners.
17816      * @param {Object|String} ename The event name, or an object containing event name properties.
17817      * @param {Function} fn Optional. If the <code>ename</code> parameter was an event name, this
17818      * is the handler function.
17819      * @param {Object} scope Optional. If the <code>ename</code> parameter was an event name, this
17820      * is the scope (<code>this</code> reference) in which the handler function is executed.
17821      * @param {Object} opt Optional. If the <code>ename</code> parameter was an event name, this
17822      * is the {@link Ext.util.Observable#addListener addListener} options.
17823      */
17824     mon : function(item, ename, fn, scope, opt){
17825         this.createMons();
17826         if(Ext.isObject(ename)){
17827             var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
17828
17829             var o = ename;
17830             for(var e in o){
17831                 if(propRe.test(e)){
17832                     continue;
17833                 }
17834                 if(Ext.isFunction(o[e])){
17835                     // shared options
17836                     this.mons.push({
17837                         item: item, ename: e, fn: o[e], scope: o.scope
17838                     });
17839                     item.on(e, o[e], o.scope, o);
17840                 }else{
17841                     // individual options
17842                     this.mons.push({
17843                         item: item, ename: e, fn: o[e], scope: o.scope
17844                     });
17845                     item.on(e, o[e]);
17846                 }
17847             }
17848             return;
17849         }
17850
17851         this.mons.push({
17852             item: item, ename: ename, fn: fn, scope: scope
17853         });
17854         item.on(ename, fn, scope, opt);
17855     },
17856
17857     /**
17858      * Removes listeners that were added by the {@link #mon} method.
17859      * @param {Observable|Element} item The item from which to remove a listener/listeners.
17860      * @param {Object|String} ename The event name, or an object containing event name properties.
17861      * @param {Function} fn Optional. If the <code>ename</code> parameter was an event name, this
17862      * is the handler function.
17863      * @param {Object} scope Optional. If the <code>ename</code> parameter was an event name, this
17864      * is the scope (<code>this</code> reference) in which the handler function is executed.
17865      */
17866     mun : function(item, ename, fn, scope){
17867         var found, mon;
17868         this.createMons();
17869         for(var i = 0, len = this.mons.length; i < len; ++i){
17870             mon = this.mons[i];
17871             if(item === mon.item && ename == mon.ename && fn === mon.fn && scope === mon.scope){
17872                 this.mons.splice(i, 1);
17873                 item.un(ename, fn, scope);
17874                 found = true;
17875                 break;
17876             }
17877         }
17878         return found;
17879     },
17880
17881     /**
17882      * Returns the next component in the owning container
17883      * @return Ext.Component
17884      */
17885     nextSibling : function(){
17886         if(this.ownerCt){
17887             var index = this.ownerCt.items.indexOf(this);
17888             if(index != -1 && index+1 < this.ownerCt.items.getCount()){
17889                 return this.ownerCt.items.itemAt(index+1);
17890             }
17891         }
17892         return null;
17893     },
17894
17895     /**
17896      * Returns the previous component in the owning container
17897      * @return Ext.Component
17898      */
17899     previousSibling : function(){
17900         if(this.ownerCt){
17901             var index = this.ownerCt.items.indexOf(this);
17902             if(index > 0){
17903                 return this.ownerCt.items.itemAt(index-1);
17904             }
17905         }
17906         return null;
17907     },
17908
17909     /**
17910      * Provides the link for Observable's fireEvent method to bubble up the ownership hierarchy.
17911      * @return {Ext.Container} the Container which owns this Component.
17912      */
17913     getBubbleTarget : function(){
17914         return this.ownerCt;
17915     }
17916 });
17917
17918 Ext.reg('component', Ext.Component);
17919 /**
17920  * @class Ext.Action
17921  * <p>An Action is a piece of reusable functionality that can be abstracted out of any particular component so that it
17922  * can be usefully shared among multiple components.  Actions let you share handlers, configuration options and UI
17923  * updates across any components that support the Action interface (primarily {@link Ext.Toolbar}, {@link Ext.Button}
17924  * and {@link Ext.menu.Menu} components).</p>
17925  * <p>Aside from supporting the config object interface, any component that needs to use Actions must also support
17926  * the following method list, as these will be called as needed by the Action class: setText(string), setIconCls(string),
17927  * setDisabled(boolean), setVisible(boolean) and setHandler(function).</p>
17928  * Example usage:<br>
17929  * <pre><code>
17930 // Define the shared action.  Each component below will have the same
17931 // display text and icon, and will display the same message on click.
17932 var action = new Ext.Action({
17933     {@link #text}: 'Do something',
17934     {@link #handler}: function(){
17935         Ext.Msg.alert('Click', 'You did something.');
17936     },
17937     {@link #iconCls}: 'do-something',
17938     {@link #itemId}: 'myAction'
17939 });
17940
17941 var panel = new Ext.Panel({
17942     title: 'Actions',
17943     width: 500,
17944     height: 300,
17945     tbar: [
17946         // Add the action directly to a toolbar as a menu button
17947         action,
17948         {
17949             text: 'Action Menu',
17950             // Add the action to a menu as a text item
17951             menu: [action]
17952         }
17953     ],
17954     items: [
17955         // Add the action to the panel body as a standard button
17956         new Ext.Button(action)
17957     ],
17958     renderTo: Ext.getBody()
17959 });
17960
17961 // Change the text for all components using the action
17962 action.setText('Something else');
17963
17964 // Reference an action through a container using the itemId
17965 var btn = panel.getComponent('myAction');
17966 var aRef = btn.baseAction;
17967 aRef.setText('New text');
17968 </code></pre>
17969  * @constructor
17970  * @param {Object} config The configuration options
17971  */
17972 Ext.Action = Ext.extend(Object, {
17973     /**
17974      * @cfg {String} text The text to set for all components using this action (defaults to '').
17975      */
17976     /**
17977      * @cfg {String} iconCls
17978      * The CSS class selector that specifies a background image to be used as the header icon for
17979      * all components using this action (defaults to '').
17980      * <p>An example of specifying a custom icon class would be something like:
17981      * </p><pre><code>
17982 // specify the property in the config for the class:
17983      ...
17984      iconCls: 'do-something'
17985
17986 // css class that specifies background image to be used as the icon image:
17987 .do-something { background-image: url(../images/my-icon.gif) 0 6px no-repeat !important; }
17988 </code></pre>
17989      */
17990     /**
17991      * @cfg {Boolean} disabled True to disable all components using this action, false to enable them (defaults to false).
17992      */
17993     /**
17994      * @cfg {Boolean} hidden True to hide all components using this action, false to show them (defaults to false).
17995      */
17996     /**
17997      * @cfg {Function} handler The function that will be invoked by each component tied to this action
17998      * when the component's primary event is triggered (defaults to undefined).
17999      */
18000     /**
18001      * @cfg {String} itemId
18002      * See {@link Ext.Component}.{@link Ext.Component#itemId itemId}.
18003      */
18004     /**
18005      * @cfg {Object} scope The scope (<tt><b>this</b></tt> reference) in which the
18006      * <code>{@link #handler}</code> is executed. Defaults to this Button.
18007      */
18008
18009     constructor : function(config){
18010         this.initialConfig = config;
18011         this.itemId = config.itemId = (config.itemId || config.id || Ext.id());
18012         this.items = [];
18013     },
18014     
18015     // private
18016     isAction : true,
18017
18018     /**
18019      * Sets the text to be displayed by all components using this action.
18020      * @param {String} text The text to display
18021      */
18022     setText : function(text){
18023         this.initialConfig.text = text;
18024         this.callEach('setText', [text]);
18025     },
18026
18027     /**
18028      * Gets the text currently displayed by all components using this action.
18029      */
18030     getText : function(){
18031         return this.initialConfig.text;
18032     },
18033
18034     /**
18035      * Sets the icon CSS class for all components using this action.  The class should supply
18036      * a background image that will be used as the icon image.
18037      * @param {String} cls The CSS class supplying the icon image
18038      */
18039     setIconClass : function(cls){
18040         this.initialConfig.iconCls = cls;
18041         this.callEach('setIconClass', [cls]);
18042     },
18043
18044     /**
18045      * Gets the icon CSS class currently used by all components using this action.
18046      */
18047     getIconClass : function(){
18048         return this.initialConfig.iconCls;
18049     },
18050
18051     /**
18052      * Sets the disabled state of all components using this action.  Shortcut method
18053      * for {@link #enable} and {@link #disable}.
18054      * @param {Boolean} disabled True to disable the component, false to enable it
18055      */
18056     setDisabled : function(v){
18057         this.initialConfig.disabled = v;
18058         this.callEach('setDisabled', [v]);
18059     },
18060
18061     /**
18062      * Enables all components using this action.
18063      */
18064     enable : function(){
18065         this.setDisabled(false);
18066     },
18067
18068     /**
18069      * Disables all components using this action.
18070      */
18071     disable : function(){
18072         this.setDisabled(true);
18073     },
18074
18075     /**
18076      * Returns true if the components using this action are currently disabled, else returns false.  
18077      */
18078     isDisabled : function(){
18079         return this.initialConfig.disabled;
18080     },
18081
18082     /**
18083      * Sets the hidden state of all components using this action.  Shortcut method
18084      * for <code>{@link #hide}</code> and <code>{@link #show}</code>.
18085      * @param {Boolean} hidden True to hide the component, false to show it
18086      */
18087     setHidden : function(v){
18088         this.initialConfig.hidden = v;
18089         this.callEach('setVisible', [!v]);
18090     },
18091
18092     /**
18093      * Shows all components using this action.
18094      */
18095     show : function(){
18096         this.setHidden(false);
18097     },
18098
18099     /**
18100      * Hides all components using this action.
18101      */
18102     hide : function(){
18103         this.setHidden(true);
18104     },
18105
18106     /**
18107      * Returns true if the components using this action are currently hidden, else returns false.  
18108      */
18109     isHidden : function(){
18110         return this.initialConfig.hidden;
18111     },
18112
18113     /**
18114      * Sets the function that will be called by each Component using this action when its primary event is triggered.
18115      * @param {Function} fn The function that will be invoked by the action's components.  The function
18116      * will be called with no arguments.
18117      * @param {Object} scope The scope (<code>this</code> reference) in which the function is executed. Defaults to the Component firing the event.
18118      */
18119     setHandler : function(fn, scope){
18120         this.initialConfig.handler = fn;
18121         this.initialConfig.scope = scope;
18122         this.callEach('setHandler', [fn, scope]);
18123     },
18124
18125     /**
18126      * Executes the specified function once for each Component currently tied to this action.  The function passed
18127      * in should accept a single argument that will be an object that supports the basic Action config/method interface.
18128      * @param {Function} fn The function to execute for each component
18129      * @param {Object} scope The scope (<code>this</code> reference) in which the function is executed.  Defaults to the Component.
18130      */
18131     each : function(fn, scope){
18132         Ext.each(this.items, fn, scope);
18133     },
18134
18135     // private
18136     callEach : function(fnName, args){
18137         var cs = this.items;
18138         for(var i = 0, len = cs.length; i < len; i++){
18139             cs[i][fnName].apply(cs[i], args);
18140         }
18141     },
18142
18143     // private
18144     addComponent : function(comp){
18145         this.items.push(comp);
18146         comp.on('destroy', this.removeComponent, this);
18147     },
18148
18149     // private
18150     removeComponent : function(comp){
18151         this.items.remove(comp);
18152     },
18153
18154     /**
18155      * Executes this action manually using the handler function specified in the original config object
18156      * or the handler function set with <code>{@link #setHandler}</code>.  Any arguments passed to this
18157      * function will be passed on to the handler function.
18158      * @param {Mixed} arg1 (optional) Variable number of arguments passed to the handler function
18159      * @param {Mixed} arg2 (optional)
18160      * @param {Mixed} etc... (optional)
18161      */
18162     execute : function(){
18163         this.initialConfig.handler.apply(this.initialConfig.scope || window, arguments);
18164     }
18165 });
18166 /**
18167  * @class Ext.Layer
18168  * @extends Ext.Element
18169  * An extended {@link Ext.Element} object that supports a shadow and shim, constrain to viewport and
18170  * automatic maintaining of shadow/shim positions.
18171  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
18172  * @cfg {String/Boolean} shadow True to automatically create an {@link Ext.Shadow}, or a string indicating the
18173  * shadow's display {@link Ext.Shadow#mode}. False to disable the shadow. (defaults to false)
18174  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: 'div', cls: 'x-layer'}).
18175  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
18176  * @cfg {String} cls CSS class to add to the element
18177  * @cfg {Number} zindex Starting z-index (defaults to 11000)
18178  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 4)
18179  * @cfg {Boolean} useDisplay
18180  * Defaults to use css offsets to hide the Layer. Specify <tt>true</tt>
18181  * to use css style <tt>'display:none;'</tt> to hide the Layer.
18182  * @constructor
18183  * @param {Object} config An object with config options.
18184  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
18185  */
18186 (function(){
18187 Ext.Layer = function(config, existingEl){
18188     config = config || {};
18189     var dh = Ext.DomHelper,
18190         cp = config.parentEl, pel = cp ? Ext.getDom(cp) : document.body;
18191         
18192     if (existingEl) {
18193         this.dom = Ext.getDom(existingEl);
18194     }
18195     if(!this.dom){
18196         var o = config.dh || {tag: 'div', cls: 'x-layer'};
18197         this.dom = dh.append(pel, o);
18198     }
18199     if(config.cls){
18200         this.addClass(config.cls);
18201     }
18202     this.constrain = config.constrain !== false;
18203     this.setVisibilityMode(Ext.Element.VISIBILITY);
18204     if(config.id){
18205         this.id = this.dom.id = config.id;
18206     }else{
18207         this.id = Ext.id(this.dom);
18208     }
18209     this.zindex = config.zindex || this.getZIndex();
18210     this.position('absolute', this.zindex);
18211     if(config.shadow){
18212         this.shadowOffset = config.shadowOffset || 4;
18213         this.shadow = new Ext.Shadow({
18214             offset : this.shadowOffset,
18215             mode : config.shadow
18216         });
18217     }else{
18218         this.shadowOffset = 0;
18219     }
18220     this.useShim = config.shim !== false && Ext.useShims;
18221     this.useDisplay = config.useDisplay;
18222     this.hide();
18223 };
18224
18225 var supr = Ext.Element.prototype;
18226
18227 // shims are shared among layer to keep from having 100 iframes
18228 var shims = [];
18229
18230 Ext.extend(Ext.Layer, Ext.Element, {
18231
18232     getZIndex : function(){
18233         return this.zindex || parseInt((this.getShim() || this).getStyle('z-index'), 10) || 11000;
18234     },
18235
18236     getShim : function(){
18237         if(!this.useShim){
18238             return null;
18239         }
18240         if(this.shim){
18241             return this.shim;
18242         }
18243         var shim = shims.shift();
18244         if(!shim){
18245             shim = this.createShim();
18246             shim.enableDisplayMode('block');
18247             shim.dom.style.display = 'none';
18248             shim.dom.style.visibility = 'visible';
18249         }
18250         var pn = this.dom.parentNode;
18251         if(shim.dom.parentNode != pn){
18252             pn.insertBefore(shim.dom, this.dom);
18253         }
18254         shim.setStyle('z-index', this.getZIndex()-2);
18255         this.shim = shim;
18256         return shim;
18257     },
18258
18259     hideShim : function(){
18260         if(this.shim){
18261             this.shim.setDisplayed(false);
18262             shims.push(this.shim);
18263             delete this.shim;
18264         }
18265     },
18266
18267     disableShadow : function(){
18268         if(this.shadow){
18269             this.shadowDisabled = true;
18270             this.shadow.hide();
18271             this.lastShadowOffset = this.shadowOffset;
18272             this.shadowOffset = 0;
18273         }
18274     },
18275
18276     enableShadow : function(show){
18277         if(this.shadow){
18278             this.shadowDisabled = false;
18279             this.shadowOffset = this.lastShadowOffset;
18280             delete this.lastShadowOffset;
18281             if(show){
18282                 this.sync(true);
18283             }
18284         }
18285     },
18286
18287     // private
18288     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
18289     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
18290     sync : function(doShow){
18291         var shadow = this.shadow;
18292         if(!this.updating && this.isVisible() && (shadow || this.useShim)){
18293             var shim = this.getShim(),
18294                 w = this.getWidth(),
18295                 h = this.getHeight(),
18296                 l = this.getLeft(true),
18297                 t = this.getTop(true);
18298
18299             if(shadow && !this.shadowDisabled){
18300                 if(doShow && !shadow.isVisible()){
18301                     shadow.show(this);
18302                 }else{
18303                     shadow.realign(l, t, w, h);
18304                 }
18305                 if(shim){
18306                     if(doShow){
18307                        shim.show();
18308                     }
18309                     // fit the shim behind the shadow, so it is shimmed too
18310                     var shadowAdj = shadow.el.getXY(), shimStyle = shim.dom.style,
18311                         shadowSize = shadow.el.getSize();
18312                     shimStyle.left = (shadowAdj[0])+'px';
18313                     shimStyle.top = (shadowAdj[1])+'px';
18314                     shimStyle.width = (shadowSize.width)+'px';
18315                     shimStyle.height = (shadowSize.height)+'px';
18316                 }
18317             }else if(shim){
18318                 if(doShow){
18319                    shim.show();
18320                 }
18321                 shim.setSize(w, h);
18322                 shim.setLeftTop(l, t);
18323             }
18324         }
18325     },
18326
18327     // private
18328     destroy : function(){
18329         this.hideShim();
18330         if(this.shadow){
18331             this.shadow.hide();
18332         }
18333         this.removeAllListeners();
18334         Ext.removeNode(this.dom);
18335         delete this.dom;
18336     },
18337
18338     remove : function(){
18339         this.destroy();
18340     },
18341
18342     // private
18343     beginUpdate : function(){
18344         this.updating = true;
18345     },
18346
18347     // private
18348     endUpdate : function(){
18349         this.updating = false;
18350         this.sync(true);
18351     },
18352
18353     // private
18354     hideUnders : function(negOffset){
18355         if(this.shadow){
18356             this.shadow.hide();
18357         }
18358         this.hideShim();
18359     },
18360
18361     // private
18362     constrainXY : function(){
18363         if(this.constrain){
18364             var vw = Ext.lib.Dom.getViewWidth(),
18365                 vh = Ext.lib.Dom.getViewHeight();
18366             var s = Ext.getDoc().getScroll();
18367
18368             var xy = this.getXY();
18369             var x = xy[0], y = xy[1];
18370             var so = this.shadowOffset;
18371             var w = this.dom.offsetWidth+so, h = this.dom.offsetHeight+so;
18372             // only move it if it needs it
18373             var moved = false;
18374             // first validate right/bottom
18375             if((x + w) > vw+s.left){
18376                 x = vw - w - so;
18377                 moved = true;
18378             }
18379             if((y + h) > vh+s.top){
18380                 y = vh - h - so;
18381                 moved = true;
18382             }
18383             // then make sure top/left isn't negative
18384             if(x < s.left){
18385                 x = s.left;
18386                 moved = true;
18387             }
18388             if(y < s.top){
18389                 y = s.top;
18390                 moved = true;
18391             }
18392             if(moved){
18393                 if(this.avoidY){
18394                     var ay = this.avoidY;
18395                     if(y <= ay && (y+h) >= ay){
18396                         y = ay-h-5;
18397                     }
18398                 }
18399                 xy = [x, y];
18400                 this.storeXY(xy);
18401                 supr.setXY.call(this, xy);
18402                 this.sync();
18403             }
18404         }
18405         return this;
18406     },
18407     
18408     getConstrainOffset : function(){
18409         return this.shadowOffset;    
18410     },
18411
18412     isVisible : function(){
18413         return this.visible;
18414     },
18415
18416     // private
18417     showAction : function(){
18418         this.visible = true; // track visibility to prevent getStyle calls
18419         if(this.useDisplay === true){
18420             this.setDisplayed('');
18421         }else if(this.lastXY){
18422             supr.setXY.call(this, this.lastXY);
18423         }else if(this.lastLT){
18424             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
18425         }
18426     },
18427
18428     // private
18429     hideAction : function(){
18430         this.visible = false;
18431         if(this.useDisplay === true){
18432             this.setDisplayed(false);
18433         }else{
18434             this.setLeftTop(-10000,-10000);
18435         }
18436     },
18437
18438     // overridden Element method
18439     setVisible : function(v, a, d, c, e){
18440         if(v){
18441             this.showAction();
18442         }
18443         if(a && v){
18444             var cb = function(){
18445                 this.sync(true);
18446                 if(c){
18447                     c();
18448                 }
18449             }.createDelegate(this);
18450             supr.setVisible.call(this, true, true, d, cb, e);
18451         }else{
18452             if(!v){
18453                 this.hideUnders(true);
18454             }
18455             var cb = c;
18456             if(a){
18457                 cb = function(){
18458                     this.hideAction();
18459                     if(c){
18460                         c();
18461                     }
18462                 }.createDelegate(this);
18463             }
18464             supr.setVisible.call(this, v, a, d, cb, e);
18465             if(v){
18466                 this.sync(true);
18467             }else if(!a){
18468                 this.hideAction();
18469             }
18470         }
18471         return this;
18472     },
18473
18474     storeXY : function(xy){
18475         delete this.lastLT;
18476         this.lastXY = xy;
18477     },
18478
18479     storeLeftTop : function(left, top){
18480         delete this.lastXY;
18481         this.lastLT = [left, top];
18482     },
18483
18484     // private
18485     beforeFx : function(){
18486         this.beforeAction();
18487         return Ext.Layer.superclass.beforeFx.apply(this, arguments);
18488     },
18489
18490     // private
18491     afterFx : function(){
18492         Ext.Layer.superclass.afterFx.apply(this, arguments);
18493         this.sync(this.isVisible());
18494     },
18495
18496     // private
18497     beforeAction : function(){
18498         if(!this.updating && this.shadow){
18499             this.shadow.hide();
18500         }
18501     },
18502
18503     // overridden Element method
18504     setLeft : function(left){
18505         this.storeLeftTop(left, this.getTop(true));
18506         supr.setLeft.apply(this, arguments);
18507         this.sync();
18508         return this;
18509     },
18510
18511     setTop : function(top){
18512         this.storeLeftTop(this.getLeft(true), top);
18513         supr.setTop.apply(this, arguments);
18514         this.sync();
18515         return this;
18516     },
18517
18518     setLeftTop : function(left, top){
18519         this.storeLeftTop(left, top);
18520         supr.setLeftTop.apply(this, arguments);
18521         this.sync();
18522         return this;
18523     },
18524
18525     setXY : function(xy, a, d, c, e){
18526         this.fixDisplay();
18527         this.beforeAction();
18528         this.storeXY(xy);
18529         var cb = this.createCB(c);
18530         supr.setXY.call(this, xy, a, d, cb, e);
18531         if(!a){
18532             cb();
18533         }
18534         return this;
18535     },
18536
18537     // private
18538     createCB : function(c){
18539         var el = this;
18540         return function(){
18541             el.constrainXY();
18542             el.sync(true);
18543             if(c){
18544                 c();
18545             }
18546         };
18547     },
18548
18549     // overridden Element method
18550     setX : function(x, a, d, c, e){
18551         this.setXY([x, this.getY()], a, d, c, e);
18552         return this;
18553     },
18554
18555     // overridden Element method
18556     setY : function(y, a, d, c, e){
18557         this.setXY([this.getX(), y], a, d, c, e);
18558         return this;
18559     },
18560
18561     // overridden Element method
18562     setSize : function(w, h, a, d, c, e){
18563         this.beforeAction();
18564         var cb = this.createCB(c);
18565         supr.setSize.call(this, w, h, a, d, cb, e);
18566         if(!a){
18567             cb();
18568         }
18569         return this;
18570     },
18571
18572     // overridden Element method
18573     setWidth : function(w, a, d, c, e){
18574         this.beforeAction();
18575         var cb = this.createCB(c);
18576         supr.setWidth.call(this, w, a, d, cb, e);
18577         if(!a){
18578             cb();
18579         }
18580         return this;
18581     },
18582
18583     // overridden Element method
18584     setHeight : function(h, a, d, c, e){
18585         this.beforeAction();
18586         var cb = this.createCB(c);
18587         supr.setHeight.call(this, h, a, d, cb, e);
18588         if(!a){
18589             cb();
18590         }
18591         return this;
18592     },
18593
18594     // overridden Element method
18595     setBounds : function(x, y, w, h, a, d, c, e){
18596         this.beforeAction();
18597         var cb = this.createCB(c);
18598         if(!a){
18599             this.storeXY([x, y]);
18600             supr.setXY.call(this, [x, y]);
18601             supr.setSize.call(this, w, h, a, d, cb, e);
18602             cb();
18603         }else{
18604             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
18605         }
18606         return this;
18607     },
18608
18609     /**
18610      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
18611      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
18612      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
18613      * @param {Number} zindex The new z-index to set
18614      * @return {this} The Layer
18615      */
18616     setZIndex : function(zindex){
18617         this.zindex = zindex;
18618         this.setStyle('z-index', zindex + 2);
18619         if(this.shadow){
18620             this.shadow.setZIndex(zindex + 1);
18621         }
18622         if(this.shim){
18623             this.shim.setStyle('z-index', zindex);
18624         }
18625         return this;
18626     }
18627 });
18628 })();
18629 /**
18630  * @class Ext.Shadow
18631  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
18632  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
18633  * functionality that can also provide the same shadow effect, see the {@link Ext.Layer} class.
18634  * @constructor
18635  * Create a new Shadow
18636  * @param {Object} config The config object
18637  */
18638 Ext.Shadow = function(config) {
18639     Ext.apply(this, config);
18640     if (typeof this.mode != "string") {
18641         this.mode = this.defaultMode;
18642     }
18643     var o = this.offset,
18644         a = {
18645             h: 0
18646         },
18647         rad = Math.floor(this.offset / 2);
18648     switch (this.mode.toLowerCase()) {
18649         // all this hideous nonsense calculates the various offsets for shadows
18650         case "drop":
18651             a.w = 0;
18652             a.l = a.t = o;
18653             a.t -= 1;
18654             if (Ext.isIE) {
18655                 a.l -= this.offset + rad;
18656                 a.t -= this.offset + rad;
18657                 a.w -= rad;
18658                 a.h -= rad;
18659                 a.t += 1;
18660             }
18661         break;
18662         case "sides":
18663             a.w = (o * 2);
18664             a.l = -o;
18665             a.t = o - 1;
18666             if (Ext.isIE) {
18667                 a.l -= (this.offset - rad);
18668                 a.t -= this.offset + rad;
18669                 a.l += 1;
18670                 a.w -= (this.offset - rad) * 2;
18671                 a.w -= rad + 1;
18672                 a.h -= 1;
18673             }
18674         break;
18675         case "frame":
18676             a.w = a.h = (o * 2);
18677             a.l = a.t = -o;
18678             a.t += 1;
18679             a.h -= 2;
18680             if (Ext.isIE) {
18681                 a.l -= (this.offset - rad);
18682                 a.t -= (this.offset - rad);
18683                 a.l += 1;
18684                 a.w -= (this.offset + rad + 1);
18685                 a.h -= (this.offset + rad);
18686                 a.h += 1;
18687             }
18688         break;
18689     };
18690
18691     this.adjusts = a;
18692 };
18693
18694 Ext.Shadow.prototype = {
18695     /**
18696      * @cfg {String} mode
18697      * The shadow display mode.  Supports the following options:<div class="mdetail-params"><ul>
18698      * <li><b><tt>sides</tt></b> : Shadow displays on both sides and bottom only</li>
18699      * <li><b><tt>frame</tt></b> : Shadow displays equally on all four sides</li>
18700      * <li><b><tt>drop</tt></b> : Traditional bottom-right drop shadow</li>
18701      * </ul></div>
18702      */
18703     /**
18704      * @cfg {String} offset
18705      * The number of pixels to offset the shadow from the element (defaults to <tt>4</tt>)
18706      */
18707     offset: 4,
18708
18709     // private
18710     defaultMode: "drop",
18711
18712     /**
18713      * Displays the shadow under the target element
18714      * @param {Mixed} targetEl The id or element under which the shadow should display
18715      */
18716     show: function(target) {
18717         target = Ext.get(target);
18718         if (!this.el) {
18719             this.el = Ext.Shadow.Pool.pull();
18720             if (this.el.dom.nextSibling != target.dom) {
18721                 this.el.insertBefore(target);
18722             }
18723         }
18724         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10) - 1);
18725         if (Ext.isIE) {
18726             this.el.dom.style.filter = "progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius=" + (this.offset) + ")";
18727         }
18728         this.realign(
18729         target.getLeft(true),
18730         target.getTop(true),
18731         target.getWidth(),
18732         target.getHeight()
18733         );
18734         this.el.dom.style.display = "block";
18735     },
18736
18737     /**
18738      * Returns true if the shadow is visible, else false
18739      */
18740     isVisible: function() {
18741         return this.el ? true: false;
18742     },
18743
18744     /**
18745      * Direct alignment when values are already available. Show must be called at least once before
18746      * calling this method to ensure it is initialized.
18747      * @param {Number} left The target element left position
18748      * @param {Number} top The target element top position
18749      * @param {Number} width The target element width
18750      * @param {Number} height The target element height
18751      */
18752     realign: function(l, t, w, h) {
18753         if (!this.el) {
18754             return;
18755         }
18756         var a = this.adjusts,
18757             d = this.el.dom,
18758             s = d.style,
18759             iea = 0,
18760             sw = (w + a.w),
18761             sh = (h + a.h),
18762             sws = sw + "px",
18763             shs = sh + "px",
18764             cn,
18765             sww;
18766         s.left = (l + a.l) + "px";
18767         s.top = (t + a.t) + "px";
18768         if (s.width != sws || s.height != shs) {
18769             s.width = sws;
18770             s.height = shs;
18771             if (!Ext.isIE) {
18772                 cn = d.childNodes;
18773                 sww = Math.max(0, (sw - 12)) + "px";
18774                 cn[0].childNodes[1].style.width = sww;
18775                 cn[1].childNodes[1].style.width = sww;
18776                 cn[2].childNodes[1].style.width = sww;
18777                 cn[1].style.height = Math.max(0, (sh - 12)) + "px";
18778             }
18779         }
18780     },
18781
18782     /**
18783      * Hides this shadow
18784      */
18785     hide: function() {
18786         if (this.el) {
18787             this.el.dom.style.display = "none";
18788             Ext.Shadow.Pool.push(this.el);
18789             delete this.el;
18790         }
18791     },
18792
18793     /**
18794      * Adjust the z-index of this shadow
18795      * @param {Number} zindex The new z-index
18796      */
18797     setZIndex: function(z) {
18798         this.zIndex = z;
18799         if (this.el) {
18800             this.el.setStyle("z-index", z);
18801         }
18802     }
18803 };
18804
18805 // Private utility class that manages the internal Shadow cache
18806 Ext.Shadow.Pool = function() {
18807     var p = [],
18808         markup = Ext.isIE ?
18809             '<div class="x-ie-shadow"></div>':
18810             '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
18811     return {
18812         pull: function() {
18813             var sh = p.shift();
18814             if (!sh) {
18815                 sh = Ext.get(Ext.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
18816                 sh.autoBoxAdjust = false;
18817             }
18818             return sh;
18819         },
18820
18821         push: function(sh) {
18822             p.push(sh);
18823         }
18824     };
18825 }();/**
18826  * @class Ext.BoxComponent
18827  * @extends Ext.Component
18828  * <p>Base class for any {@link Ext.Component Component} that is to be sized as a box, using width and height.</p>
18829  * <p>BoxComponent provides automatic box model adjustments for sizing and positioning and will work correctly
18830  * within the Component rendering model.</p>
18831  * <p>A BoxComponent may be created as a custom Component which encapsulates any HTML element, either a pre-existing
18832  * element, or one that is created to your specifications at render time. Usually, to participate in layouts,
18833  * a Component will need to be a <b>Box</b>Component in order to have its width and height managed.</p>
18834  * <p>To use a pre-existing element as a BoxComponent, configure it so that you preset the <b>el</b> property to the
18835  * element to reference:<pre><code>
18836 var pageHeader = new Ext.BoxComponent({
18837     el: 'my-header-div'
18838 });</code></pre>
18839  * This may then be {@link Ext.Container#add added} to a {@link Ext.Container Container} as a child item.</p>
18840  * <p>To create a BoxComponent based around a HTML element to be created at render time, use the
18841  * {@link Ext.Component#autoEl autoEl} config option which takes the form of a
18842  * {@link Ext.DomHelper DomHelper} specification:<pre><code>
18843 var myImage = new Ext.BoxComponent({
18844     autoEl: {
18845         tag: 'img',
18846         src: '/images/my-image.jpg'
18847     }
18848 });</code></pre></p>
18849  * @constructor
18850  * @param {Ext.Element/String/Object} config The configuration options.
18851  * @xtype box
18852  */
18853 Ext.BoxComponent = Ext.extend(Ext.Component, {
18854
18855     // Configs below are used for all Components when rendered by BoxLayout.
18856     /**
18857      * @cfg {Number} flex
18858      * <p><b>Note</b>: this config is only used when this Component is rendered
18859      * by a Container which has been configured to use a <b>{@link Ext.layout.BoxLayout BoxLayout}.</b>
18860      * Each child Component with a <code>flex</code> property will be flexed either vertically (by a VBoxLayout)
18861      * or horizontally (by an HBoxLayout) according to the item's <b>relative</b> <code>flex</code> value
18862      * compared to the sum of all Components with <code>flex</flex> value specified. Any child items that have
18863      * either a <code>flex = 0</code> or <code>flex = undefined</code> will not be 'flexed' (the initial size will not be changed).
18864      */
18865     // Configs below are used for all Components when rendered by AnchorLayout.
18866     /**
18867      * @cfg {String} anchor <p><b>Note</b>: this config is only used when this Component is rendered
18868      * by a Container which has been configured to use an <b>{@link Ext.layout.AnchorLayout AnchorLayout} (or subclass thereof).</b>
18869      * based layout manager, for example:<div class="mdetail-params"><ul>
18870      * <li>{@link Ext.form.FormPanel}</li>
18871      * <li>specifying <code>layout: 'anchor' // or 'form', or 'absolute'</code></li>
18872      * </ul></div></p>
18873      * <p>See {@link Ext.layout.AnchorLayout}.{@link Ext.layout.AnchorLayout#anchor anchor} also.</p>
18874      */
18875     // tabTip config is used when a BoxComponent is a child of a TabPanel
18876     /**
18877      * @cfg {String} tabTip
18878      * <p><b>Note</b>: this config is only used when this BoxComponent is a child item of a TabPanel.</p>
18879      * A string to be used as innerHTML (html tags are accepted) to show in a tooltip when mousing over
18880      * the associated tab selector element. {@link Ext.QuickTips}.init()
18881      * must be called in order for the tips to render.
18882      */
18883     // Configs below are used for all Components when rendered by BorderLayout.
18884     /**
18885      * @cfg {String} region <p><b>Note</b>: this config is only used when this BoxComponent is rendered
18886      * by a Container which has been configured to use the <b>{@link Ext.layout.BorderLayout BorderLayout}</b>
18887      * layout manager (e.g. specifying <tt>layout:'border'</tt>).</p><br>
18888      * <p>See {@link Ext.layout.BorderLayout} also.</p>
18889      */
18890     // margins config is used when a BoxComponent is rendered by BorderLayout or BoxLayout.
18891     /**
18892      * @cfg {Object} margins <p><b>Note</b>: this config is only used when this BoxComponent is rendered
18893      * by a Container which has been configured to use the <b>{@link Ext.layout.BorderLayout BorderLayout}</b>
18894      * or one of the two <b>{@link Ext.layout.BoxLayout BoxLayout} subclasses.</b></p>
18895      * <p>An object containing margins to apply to this BoxComponent in the
18896      * format:</p><pre><code>
18897 {
18898     top: (top margin),
18899     right: (right margin),
18900     bottom: (bottom margin),
18901     left: (left margin)
18902 }</code></pre>
18903      * <p>May also be a string containing space-separated, numeric margin values. The order of the
18904      * sides associated with each value matches the way CSS processes margin values:</p>
18905      * <p><div class="mdetail-params"><ul>
18906      * <li>If there is only one value, it applies to all sides.</li>
18907      * <li>If there are two values, the top and bottom borders are set to the first value and the
18908      * right and left are set to the second.</li>
18909      * <li>If there are three values, the top is set to the first value, the left and right are set
18910      * to the second, and the bottom is set to the third.</li>
18911      * <li>If there are four values, they apply to the top, right, bottom, and left, respectively.</li>
18912      * </ul></div></p>
18913      * <p>Defaults to:</p><pre><code>
18914      * {top:0, right:0, bottom:0, left:0}
18915      * </code></pre>
18916      */
18917     /**
18918      * @cfg {Number} x
18919      * The local x (left) coordinate for this component if contained within a positioning container.
18920      */
18921     /**
18922      * @cfg {Number} y
18923      * The local y (top) coordinate for this component if contained within a positioning container.
18924      */
18925     /**
18926      * @cfg {Number} pageX
18927      * The page level x coordinate for this component if contained within a positioning container.
18928      */
18929     /**
18930      * @cfg {Number} pageY
18931      * The page level y coordinate for this component if contained within a positioning container.
18932      */
18933     /**
18934      * @cfg {Number} height
18935      * The height of this component in pixels (defaults to auto).
18936      * <b>Note</b> to express this dimension as a percentage or offset see {@link Ext.Component#anchor}.
18937      */
18938     /**
18939      * @cfg {Number} width
18940      * The width of this component in pixels (defaults to auto).
18941      * <b>Note</b> to express this dimension as a percentage or offset see {@link Ext.Component#anchor}.
18942      */
18943     /**
18944      * @cfg {Number} boxMinHeight
18945      * <p>The minimum value in pixels which this BoxComponent will set its height to.</p>
18946      * <p><b>Warning:</b> This will override any size management applied by layout managers.</p>
18947      */
18948     /**
18949      * @cfg {Number} boxMinWidth
18950      * <p>The minimum value in pixels which this BoxComponent will set its width to.</p>
18951      * <p><b>Warning:</b> This will override any size management applied by layout managers.</p>
18952      */
18953     /**
18954      * @cfg {Number} boxMaxHeight
18955      * <p>The maximum value in pixels which this BoxComponent will set its height to.</p>
18956      * <p><b>Warning:</b> This will override any size management applied by layout managers.</p>
18957      */
18958     /**
18959      * @cfg {Number} boxMaxWidth
18960      * <p>The maximum value in pixels which this BoxComponent will set its width to.</p>
18961      * <p><b>Warning:</b> This will override any size management applied by layout managers.</p>
18962      */
18963     /**
18964      * @cfg {Boolean} autoHeight
18965      * <p>True to use height:'auto', false to use fixed height (or allow it to be managed by its parent
18966      * Container's {@link Ext.Container#layout layout manager}. Defaults to false.</p>
18967      * <p><b>Note</b>: Although many components inherit this config option, not all will
18968      * function as expected with a height of 'auto'. Setting autoHeight:true means that the
18969      * browser will manage height based on the element's contents, and that Ext will not manage it at all.</p>
18970      * <p>If the <i>browser</i> is managing the height, be aware that resizes performed by the browser in response
18971      * to changes within the structure of the Component cannot be detected. Therefore changes to the height might
18972      * result in elements needing to be synchronized with the new height. Example:</p><pre><code>
18973 var w = new Ext.Window({
18974     title: 'Window',
18975     width: 600,
18976     autoHeight: true,
18977     items: {
18978         title: 'Collapse Me',
18979         height: 400,
18980         collapsible: true,
18981         border: false,
18982         listeners: {
18983             beforecollapse: function() {
18984                 w.el.shadow.hide();
18985             },
18986             beforeexpand: function() {
18987                 w.el.shadow.hide();
18988             },
18989             collapse: function() {
18990                 w.syncShadow();
18991             },
18992             expand: function() {
18993                 w.syncShadow();
18994             }
18995         }
18996     }
18997 }).show();
18998 </code></pre>
18999      */
19000     /**
19001      * @cfg {Boolean} autoWidth
19002      * <p>True to use width:'auto', false to use fixed width (or allow it to be managed by its parent
19003      * Container's {@link Ext.Container#layout layout manager}. Defaults to false.</p>
19004      * <p><b>Note</b>: Although many components  inherit this config option, not all will
19005      * function as expected with a width of 'auto'. Setting autoWidth:true means that the
19006      * browser will manage width based on the element's contents, and that Ext will not manage it at all.</p>
19007      * <p>If the <i>browser</i> is managing the width, be aware that resizes performed by the browser in response
19008      * to changes within the structure of the Component cannot be detected. Therefore changes to the width might
19009      * result in elements needing to be synchronized with the new width. For example, where the target element is:</p><pre><code>
19010 &lt;div id='grid-container' style='margin-left:25%;width:50%'>&lt;/div>
19011 </code></pre>
19012      * A Panel rendered into that target element must listen for browser window resize in order to relay its
19013       * child items when the browser changes its width:<pre><code>
19014 var myPanel = new Ext.Panel({
19015     renderTo: 'grid-container',
19016     monitorResize: true, // relay on browser resize
19017     title: 'Panel',
19018     height: 400,
19019     autoWidth: true,
19020     layout: 'hbox',
19021     layoutConfig: {
19022         align: 'stretch'
19023     },
19024     defaults: {
19025         flex: 1
19026     },
19027     items: [{
19028         title: 'Box 1',
19029     }, {
19030         title: 'Box 2'
19031     }, {
19032         title: 'Box 3'
19033     }],
19034 });
19035 </code></pre>
19036      */
19037     /**
19038      * @cfg {Boolean} autoScroll
19039      * <code>true</code> to use overflow:'auto' on the components layout element and show scroll bars automatically when
19040      * necessary, <code>false</code> to clip any overflowing content (defaults to <code>false</code>).
19041      */
19042
19043     /* // private internal config
19044      * {Boolean} deferHeight
19045      * True to defer height calculations to an external component, false to allow this component to set its own
19046      * height (defaults to false).
19047      */
19048
19049     // private
19050     initComponent : function(){
19051         Ext.BoxComponent.superclass.initComponent.call(this);
19052         this.addEvents(
19053             /**
19054              * @event resize
19055              * Fires after the component is resized.
19056              * @param {Ext.Component} this
19057              * @param {Number} adjWidth The box-adjusted width that was set
19058              * @param {Number} adjHeight The box-adjusted height that was set
19059              * @param {Number} rawWidth The width that was originally specified
19060              * @param {Number} rawHeight The height that was originally specified
19061              */
19062             'resize',
19063             /**
19064              * @event move
19065              * Fires after the component is moved.
19066              * @param {Ext.Component} this
19067              * @param {Number} x The new x position
19068              * @param {Number} y The new y position
19069              */
19070             'move'
19071         );
19072     },
19073
19074     // private, set in afterRender to signify that the component has been rendered
19075     boxReady : false,
19076     // private, used to defer height settings to subclasses
19077     deferHeight: false,
19078
19079     /**
19080      * Sets the width and height of this BoxComponent. This method fires the {@link #resize} event. This method can accept
19081      * either width and height as separate arguments, or you can pass a size object like <code>{width:10, height:20}</code>.
19082      * @param {Mixed} width The new width to set. This may be one of:<div class="mdetail-params"><ul>
19083      * <li>A Number specifying the new width in the {@link #getEl Element}'s {@link Ext.Element#defaultUnit}s (by default, pixels).</li>
19084      * <li>A String used to set the CSS width style.</li>
19085      * <li>A size object in the format <code>{width: widthValue, height: heightValue}</code>.</li>
19086      * <li><code>undefined</code> to leave the width unchanged.</li>
19087      * </ul></div>
19088      * @param {Mixed} height The new height to set (not required if a size object is passed as the first arg).
19089      * This may be one of:<div class="mdetail-params"><ul>
19090      * <li>A Number specifying the new height in the {@link #getEl Element}'s {@link Ext.Element#defaultUnit}s (by default, pixels).</li>
19091      * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
19092      * <li><code>undefined</code> to leave the height unchanged.</li>
19093      * </ul></div>
19094      * @return {Ext.BoxComponent} this
19095      */
19096     setSize : function(w, h){
19097
19098         // support for standard size objects
19099         if(typeof w == 'object'){
19100             h = w.height;
19101             w = w.width;
19102         }
19103         if (Ext.isDefined(w) && Ext.isDefined(this.boxMinWidth) && (w < this.boxMinWidth)) {
19104             w = this.boxMinWidth;
19105         }
19106         if (Ext.isDefined(h) && Ext.isDefined(this.boxMinHeight) && (h < this.boxMinHeight)) {
19107             h = this.boxMinHeight;
19108         }
19109         if (Ext.isDefined(w) && Ext.isDefined(this.boxMaxWidth) && (w > this.boxMaxWidth)) {
19110             w = this.boxMaxWidth;
19111         }
19112         if (Ext.isDefined(h) && Ext.isDefined(this.boxMaxHeight) && (h > this.boxMaxHeight)) {
19113             h = this.boxMaxHeight;
19114         }
19115         // not rendered
19116         if(!this.boxReady){
19117             this.width  = w;
19118             this.height = h;
19119             return this;
19120         }
19121
19122         // prevent recalcs when not needed
19123         if(this.cacheSizes !== false && this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
19124             return this;
19125         }
19126         this.lastSize = {width: w, height: h};
19127         var adj = this.adjustSize(w, h),
19128             aw = adj.width,
19129             ah = adj.height,
19130             rz;
19131         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
19132             rz = this.getResizeEl();
19133             if(!this.deferHeight && aw !== undefined && ah !== undefined){
19134                 rz.setSize(aw, ah);
19135             }else if(!this.deferHeight && ah !== undefined){
19136                 rz.setHeight(ah);
19137             }else if(aw !== undefined){
19138                 rz.setWidth(aw);
19139             }
19140             this.onResize(aw, ah, w, h);
19141             this.fireEvent('resize', this, aw, ah, w, h);
19142         }
19143         return this;
19144     },
19145
19146     /**
19147      * Sets the width of the component.  This method fires the {@link #resize} event.
19148      * @param {Mixed} width The new width to set. This may be one of:<div class="mdetail-params"><ul>
19149      * <li>A Number specifying the new width in the {@link #getEl Element}'s {@link Ext.Element#defaultUnit defaultUnit}s (by default, pixels).</li>
19150      * <li>A String used to set the CSS width style.</li>
19151      * </ul></div>
19152      * @return {Ext.BoxComponent} this
19153      */
19154     setWidth : function(width){
19155         return this.setSize(width);
19156     },
19157
19158     /**
19159      * Sets the height of the component.  This method fires the {@link #resize} event.
19160      * @param {Mixed} height The new height to set. This may be one of:<div class="mdetail-params"><ul>
19161      * <li>A Number specifying the new height in the {@link #getEl Element}'s {@link Ext.Element#defaultUnit defaultUnit}s (by default, pixels).</li>
19162      * <li>A String used to set the CSS height style.</li>
19163      * <li><i>undefined</i> to leave the height unchanged.</li>
19164      * </ul></div>
19165      * @return {Ext.BoxComponent} this
19166      */
19167     setHeight : function(height){
19168         return this.setSize(undefined, height);
19169     },
19170
19171     /**
19172      * Gets the current size of the component's underlying element.
19173      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
19174      */
19175     getSize : function(){
19176         return this.getResizeEl().getSize();
19177     },
19178
19179     /**
19180      * Gets the current width of the component's underlying element.
19181      * @return {Number}
19182      */
19183     getWidth : function(){
19184         return this.getResizeEl().getWidth();
19185     },
19186
19187     /**
19188      * Gets the current height of the component's underlying element.
19189      * @return {Number}
19190      */
19191     getHeight : function(){
19192         return this.getResizeEl().getHeight();
19193     },
19194
19195     /**
19196      * Gets the current size of the component's underlying element, including space taken by its margins.
19197      * @return {Object} An object containing the element's size {width: (element width + left/right margins), height: (element height + top/bottom margins)}
19198      */
19199     getOuterSize : function(){
19200         var el = this.getResizeEl();
19201         return {width: el.getWidth() + el.getMargins('lr'),
19202                 height: el.getHeight() + el.getMargins('tb')};
19203     },
19204
19205     /**
19206      * Gets the current XY position of the component's underlying element.
19207      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
19208      * @return {Array} The XY position of the element (e.g., [100, 200])
19209      */
19210     getPosition : function(local){
19211         var el = this.getPositionEl();
19212         if(local === true){
19213             return [el.getLeft(true), el.getTop(true)];
19214         }
19215         return this.xy || el.getXY();
19216     },
19217
19218     /**
19219      * Gets the current box measurements of the component's underlying element.
19220      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
19221      * @return {Object} box An object in the format {x, y, width, height}
19222      */
19223     getBox : function(local){
19224         var pos = this.getPosition(local);
19225         var s = this.getSize();
19226         s.x = pos[0];
19227         s.y = pos[1];
19228         return s;
19229     },
19230
19231     /**
19232      * Sets the current box measurements of the component's underlying element.
19233      * @param {Object} box An object in the format {x, y, width, height}
19234      * @return {Ext.BoxComponent} this
19235      */
19236     updateBox : function(box){
19237         this.setSize(box.width, box.height);
19238         this.setPagePosition(box.x, box.y);
19239         return this;
19240     },
19241
19242     /**
19243      * <p>Returns the outermost Element of this Component which defines the Components overall size.</p>
19244      * <p><i>Usually</i> this will return the same Element as <code>{@link #getEl}</code>,
19245      * but in some cases, a Component may have some more wrapping Elements around its main
19246      * active Element.</p>
19247      * <p>An example is a ComboBox. It is encased in a <i>wrapping</i> Element which
19248      * contains both the <code>&lt;input></code> Element (which is what would be returned
19249      * by its <code>{@link #getEl}</code> method, <i>and</i> the trigger button Element.
19250      * This Element is returned as the <code>resizeEl</code>.
19251      * @return {Ext.Element} The Element which is to be resized by size managing layouts.
19252      */
19253     getResizeEl : function(){
19254         return this.resizeEl || this.el;
19255     },
19256
19257     /**
19258      * Sets the overflow on the content element of the component.
19259      * @param {Boolean} scroll True to allow the Component to auto scroll.
19260      * @return {Ext.BoxComponent} this
19261      */
19262     setAutoScroll : function(scroll){
19263         if(this.rendered){
19264             this.getContentTarget().setOverflow(scroll ? 'auto' : '');
19265         }
19266         this.autoScroll = scroll;
19267         return this;
19268     },
19269
19270     /**
19271      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
19272      * This method fires the {@link #move} event.
19273      * @param {Number} left The new left
19274      * @param {Number} top The new top
19275      * @return {Ext.BoxComponent} this
19276      */
19277     setPosition : function(x, y){
19278         if(x && typeof x[1] == 'number'){
19279             y = x[1];
19280             x = x[0];
19281         }
19282         this.x = x;
19283         this.y = y;
19284         if(!this.boxReady){
19285             return this;
19286         }
19287         var adj = this.adjustPosition(x, y);
19288         var ax = adj.x, ay = adj.y;
19289
19290         var el = this.getPositionEl();
19291         if(ax !== undefined || ay !== undefined){
19292             if(ax !== undefined && ay !== undefined){
19293                 el.setLeftTop(ax, ay);
19294             }else if(ax !== undefined){
19295                 el.setLeft(ax);
19296             }else if(ay !== undefined){
19297                 el.setTop(ay);
19298             }
19299             this.onPosition(ax, ay);
19300             this.fireEvent('move', this, ax, ay);
19301         }
19302         return this;
19303     },
19304
19305     /**
19306      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
19307      * This method fires the {@link #move} event.
19308      * @param {Number} x The new x position
19309      * @param {Number} y The new y position
19310      * @return {Ext.BoxComponent} this
19311      */
19312     setPagePosition : function(x, y){
19313         if(x && typeof x[1] == 'number'){
19314             y = x[1];
19315             x = x[0];
19316         }
19317         this.pageX = x;
19318         this.pageY = y;
19319         if(!this.boxReady){
19320             return;
19321         }
19322         if(x === undefined || y === undefined){ // cannot translate undefined points
19323             return;
19324         }
19325         var p = this.getPositionEl().translatePoints(x, y);
19326         this.setPosition(p.left, p.top);
19327         return this;
19328     },
19329
19330     // private
19331     afterRender : function(){
19332         Ext.BoxComponent.superclass.afterRender.call(this);
19333         if(this.resizeEl){
19334             this.resizeEl = Ext.get(this.resizeEl);
19335         }
19336         if(this.positionEl){
19337             this.positionEl = Ext.get(this.positionEl);
19338         }
19339         this.boxReady = true;
19340         Ext.isDefined(this.autoScroll) && this.setAutoScroll(this.autoScroll);
19341         this.setSize(this.width, this.height);
19342         if(this.x || this.y){
19343             this.setPosition(this.x, this.y);
19344         }else if(this.pageX || this.pageY){
19345             this.setPagePosition(this.pageX, this.pageY);
19346         }
19347     },
19348
19349     /**
19350      * Force the component's size to recalculate based on the underlying element's current height and width.
19351      * @return {Ext.BoxComponent} this
19352      */
19353     syncSize : function(){
19354         delete this.lastSize;
19355         this.setSize(this.autoWidth ? undefined : this.getResizeEl().getWidth(), this.autoHeight ? undefined : this.getResizeEl().getHeight());
19356         return this;
19357     },
19358
19359     /* // protected
19360      * Called after the component is resized, this method is empty by default but can be implemented by any
19361      * subclass that needs to perform custom logic after a resize occurs.
19362      * @param {Number} adjWidth The box-adjusted width that was set
19363      * @param {Number} adjHeight The box-adjusted height that was set
19364      * @param {Number} rawWidth The width that was originally specified
19365      * @param {Number} rawHeight The height that was originally specified
19366      */
19367     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
19368     },
19369
19370     /* // protected
19371      * Called after the component is moved, this method is empty by default but can be implemented by any
19372      * subclass that needs to perform custom logic after a move occurs.
19373      * @param {Number} x The new x position
19374      * @param {Number} y The new y position
19375      */
19376     onPosition : function(x, y){
19377
19378     },
19379
19380     // private
19381     adjustSize : function(w, h){
19382         if(this.autoWidth){
19383             w = 'auto';
19384         }
19385         if(this.autoHeight){
19386             h = 'auto';
19387         }
19388         return {width : w, height: h};
19389     },
19390
19391     // private
19392     adjustPosition : function(x, y){
19393         return {x : x, y: y};
19394     }
19395 });
19396 Ext.reg('box', Ext.BoxComponent);
19397
19398
19399 /**
19400  * @class Ext.Spacer
19401  * @extends Ext.BoxComponent
19402  * <p>Used to provide a sizable space in a layout.</p>
19403  * @constructor
19404  * @param {Object} config
19405  */
19406 Ext.Spacer = Ext.extend(Ext.BoxComponent, {
19407     autoEl:'div'
19408 });
19409 Ext.reg('spacer', Ext.Spacer);/**
19410  * @class Ext.SplitBar
19411  * @extends Ext.util.Observable
19412  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
19413  * <br><br>
19414  * Usage:
19415  * <pre><code>
19416 var split = new Ext.SplitBar("elementToDrag", "elementToSize",
19417                    Ext.SplitBar.HORIZONTAL, Ext.SplitBar.LEFT);
19418 split.setAdapter(new Ext.SplitBar.AbsoluteLayoutAdapter("container"));
19419 split.minSize = 100;
19420 split.maxSize = 600;
19421 split.animate = true;
19422 split.on('moved', splitterMoved);
19423 </code></pre>
19424  * @constructor
19425  * Create a new SplitBar
19426  * @param {Mixed} dragElement The element to be dragged and act as the SplitBar.
19427  * @param {Mixed} resizingElement The element to be resized based on where the SplitBar element is dragged
19428  * @param {Number} orientation (optional) Either Ext.SplitBar.HORIZONTAL or Ext.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
19429  * @param {Number} placement (optional) Either Ext.SplitBar.LEFT or Ext.SplitBar.RIGHT for horizontal or
19430                         Ext.SplitBar.TOP or Ext.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
19431                         position of the SplitBar).
19432  */
19433 Ext.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
19434
19435     /** @private */
19436     this.el = Ext.get(dragElement, true);
19437     this.el.dom.unselectable = "on";
19438     /** @private */
19439     this.resizingEl = Ext.get(resizingElement, true);
19440
19441     /**
19442      * @private
19443      * The orientation of the split. Either Ext.SplitBar.HORIZONTAL or Ext.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
19444      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
19445      * @type Number
19446      */
19447     this.orientation = orientation || Ext.SplitBar.HORIZONTAL;
19448
19449     /**
19450      * The increment, in pixels by which to move this SplitBar. When <i>undefined</i>, the SplitBar moves smoothly.
19451      * @type Number
19452      * @property tickSize
19453      */
19454     /**
19455      * The minimum size of the resizing element. (Defaults to 0)
19456      * @type Number
19457      */
19458     this.minSize = 0;
19459
19460     /**
19461      * The maximum size of the resizing element. (Defaults to 2000)
19462      * @type Number
19463      */
19464     this.maxSize = 2000;
19465
19466     /**
19467      * Whether to animate the transition to the new size
19468      * @type Boolean
19469      */
19470     this.animate = false;
19471
19472     /**
19473      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
19474      * @type Boolean
19475      */
19476     this.useShim = false;
19477
19478     /** @private */
19479     this.shim = null;
19480
19481     if(!existingProxy){
19482         /** @private */
19483         this.proxy = Ext.SplitBar.createProxy(this.orientation);
19484     }else{
19485         this.proxy = Ext.get(existingProxy).dom;
19486     }
19487     /** @private */
19488     this.dd = new Ext.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
19489
19490     /** @private */
19491     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
19492
19493     /** @private */
19494     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
19495
19496     /** @private */
19497     this.dragSpecs = {};
19498
19499     /**
19500      * @private The adapter to use to positon and resize elements
19501      */
19502     this.adapter = new Ext.SplitBar.BasicLayoutAdapter();
19503     this.adapter.init(this);
19504
19505     if(this.orientation == Ext.SplitBar.HORIZONTAL){
19506         /** @private */
19507         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Ext.SplitBar.LEFT : Ext.SplitBar.RIGHT);
19508         this.el.addClass("x-splitbar-h");
19509     }else{
19510         /** @private */
19511         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Ext.SplitBar.TOP : Ext.SplitBar.BOTTOM);
19512         this.el.addClass("x-splitbar-v");
19513     }
19514
19515     this.addEvents(
19516         /**
19517          * @event resize
19518          * Fires when the splitter is moved (alias for {@link #moved})
19519          * @param {Ext.SplitBar} this
19520          * @param {Number} newSize the new width or height
19521          */
19522         "resize",
19523         /**
19524          * @event moved
19525          * Fires when the splitter is moved
19526          * @param {Ext.SplitBar} this
19527          * @param {Number} newSize the new width or height
19528          */
19529         "moved",
19530         /**
19531          * @event beforeresize
19532          * Fires before the splitter is dragged
19533          * @param {Ext.SplitBar} this
19534          */
19535         "beforeresize",
19536
19537         "beforeapply"
19538     );
19539
19540     Ext.SplitBar.superclass.constructor.call(this);
19541 };
19542
19543 Ext.extend(Ext.SplitBar, Ext.util.Observable, {
19544     onStartProxyDrag : function(x, y){
19545         this.fireEvent("beforeresize", this);
19546         this.overlay =  Ext.DomHelper.append(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
19547         this.overlay.unselectable();
19548         this.overlay.setSize(Ext.lib.Dom.getViewWidth(true), Ext.lib.Dom.getViewHeight(true));
19549         this.overlay.show();
19550         Ext.get(this.proxy).setDisplayed("block");
19551         var size = this.adapter.getElementSize(this);
19552         this.activeMinSize = this.getMinimumSize();
19553         this.activeMaxSize = this.getMaximumSize();
19554         var c1 = size - this.activeMinSize;
19555         var c2 = Math.max(this.activeMaxSize - size, 0);
19556         if(this.orientation == Ext.SplitBar.HORIZONTAL){
19557             this.dd.resetConstraints();
19558             this.dd.setXConstraint(
19559                 this.placement == Ext.SplitBar.LEFT ? c1 : c2,
19560                 this.placement == Ext.SplitBar.LEFT ? c2 : c1,
19561                 this.tickSize
19562             );
19563             this.dd.setYConstraint(0, 0);
19564         }else{
19565             this.dd.resetConstraints();
19566             this.dd.setXConstraint(0, 0);
19567             this.dd.setYConstraint(
19568                 this.placement == Ext.SplitBar.TOP ? c1 : c2,
19569                 this.placement == Ext.SplitBar.TOP ? c2 : c1,
19570                 this.tickSize
19571             );
19572          }
19573         this.dragSpecs.startSize = size;
19574         this.dragSpecs.startPoint = [x, y];
19575         Ext.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
19576     },
19577
19578     /**
19579      * @private Called after the drag operation by the DDProxy
19580      */
19581     onEndProxyDrag : function(e){
19582         Ext.get(this.proxy).setDisplayed(false);
19583         var endPoint = Ext.lib.Event.getXY(e);
19584         if(this.overlay){
19585             Ext.destroy(this.overlay);
19586             delete this.overlay;
19587         }
19588         var newSize;
19589         if(this.orientation == Ext.SplitBar.HORIZONTAL){
19590             newSize = this.dragSpecs.startSize +
19591                 (this.placement == Ext.SplitBar.LEFT ?
19592                     endPoint[0] - this.dragSpecs.startPoint[0] :
19593                     this.dragSpecs.startPoint[0] - endPoint[0]
19594                 );
19595         }else{
19596             newSize = this.dragSpecs.startSize +
19597                 (this.placement == Ext.SplitBar.TOP ?
19598                     endPoint[1] - this.dragSpecs.startPoint[1] :
19599                     this.dragSpecs.startPoint[1] - endPoint[1]
19600                 );
19601         }
19602         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
19603         if(newSize != this.dragSpecs.startSize){
19604             if(this.fireEvent('beforeapply', this, newSize) !== false){
19605                 this.adapter.setElementSize(this, newSize);
19606                 this.fireEvent("moved", this, newSize);
19607                 this.fireEvent("resize", this, newSize);
19608             }
19609         }
19610     },
19611
19612     /**
19613      * Get the adapter this SplitBar uses
19614      * @return The adapter object
19615      */
19616     getAdapter : function(){
19617         return this.adapter;
19618     },
19619
19620     /**
19621      * Set the adapter this SplitBar uses
19622      * @param {Object} adapter A SplitBar adapter object
19623      */
19624     setAdapter : function(adapter){
19625         this.adapter = adapter;
19626         this.adapter.init(this);
19627     },
19628
19629     /**
19630      * Gets the minimum size for the resizing element
19631      * @return {Number} The minimum size
19632      */
19633     getMinimumSize : function(){
19634         return this.minSize;
19635     },
19636
19637     /**
19638      * Sets the minimum size for the resizing element
19639      * @param {Number} minSize The minimum size
19640      */
19641     setMinimumSize : function(minSize){
19642         this.minSize = minSize;
19643     },
19644
19645     /**
19646      * Gets the maximum size for the resizing element
19647      * @return {Number} The maximum size
19648      */
19649     getMaximumSize : function(){
19650         return this.maxSize;
19651     },
19652
19653     /**
19654      * Sets the maximum size for the resizing element
19655      * @param {Number} maxSize The maximum size
19656      */
19657     setMaximumSize : function(maxSize){
19658         this.maxSize = maxSize;
19659     },
19660
19661     /**
19662      * Sets the initialize size for the resizing element
19663      * @param {Number} size The initial size
19664      */
19665     setCurrentSize : function(size){
19666         var oldAnimate = this.animate;
19667         this.animate = false;
19668         this.adapter.setElementSize(this, size);
19669         this.animate = oldAnimate;
19670     },
19671
19672     /**
19673      * Destroy this splitbar.
19674      * @param {Boolean} removeEl True to remove the element
19675      */
19676     destroy : function(removeEl){
19677         Ext.destroy(this.shim, Ext.get(this.proxy));
19678         this.dd.unreg();
19679         if(removeEl){
19680             this.el.remove();
19681         }
19682         this.purgeListeners();
19683     }
19684 });
19685
19686 /**
19687  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
19688  */
19689 Ext.SplitBar.createProxy = function(dir){
19690     var proxy = new Ext.Element(document.createElement("div"));
19691     document.body.appendChild(proxy.dom);
19692     proxy.unselectable();
19693     var cls = 'x-splitbar-proxy';
19694     proxy.addClass(cls + ' ' + (dir == Ext.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
19695     return proxy.dom;
19696 };
19697
19698 /**
19699  * @class Ext.SplitBar.BasicLayoutAdapter
19700  * Default Adapter. It assumes the splitter and resizing element are not positioned
19701  * elements and only gets/sets the width of the element. Generally used for table based layouts.
19702  */
19703 Ext.SplitBar.BasicLayoutAdapter = function(){
19704 };
19705
19706 Ext.SplitBar.BasicLayoutAdapter.prototype = {
19707     // do nothing for now
19708     init : function(s){
19709
19710     },
19711     /**
19712      * Called before drag operations to get the current size of the resizing element.
19713      * @param {Ext.SplitBar} s The SplitBar using this adapter
19714      */
19715      getElementSize : function(s){
19716         if(s.orientation == Ext.SplitBar.HORIZONTAL){
19717             return s.resizingEl.getWidth();
19718         }else{
19719             return s.resizingEl.getHeight();
19720         }
19721     },
19722
19723     /**
19724      * Called after drag operations to set the size of the resizing element.
19725      * @param {Ext.SplitBar} s The SplitBar using this adapter
19726      * @param {Number} newSize The new size to set
19727      * @param {Function} onComplete A function to be invoked when resizing is complete
19728      */
19729     setElementSize : function(s, newSize, onComplete){
19730         if(s.orientation == Ext.SplitBar.HORIZONTAL){
19731             if(!s.animate){
19732                 s.resizingEl.setWidth(newSize);
19733                 if(onComplete){
19734                     onComplete(s, newSize);
19735                 }
19736             }else{
19737                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
19738             }
19739         }else{
19740
19741             if(!s.animate){
19742                 s.resizingEl.setHeight(newSize);
19743                 if(onComplete){
19744                     onComplete(s, newSize);
19745                 }
19746             }else{
19747                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
19748             }
19749         }
19750     }
19751 };
19752
19753 /**
19754  *@class Ext.SplitBar.AbsoluteLayoutAdapter
19755  * @extends Ext.SplitBar.BasicLayoutAdapter
19756  * Adapter that  moves the splitter element to align with the resized sizing element.
19757  * Used with an absolute positioned SplitBar.
19758  * @param {Mixed} container The container that wraps around the absolute positioned content. If it's
19759  * document.body, make sure you assign an id to the body element.
19760  */
19761 Ext.SplitBar.AbsoluteLayoutAdapter = function(container){
19762     this.basic = new Ext.SplitBar.BasicLayoutAdapter();
19763     this.container = Ext.get(container);
19764 };
19765
19766 Ext.SplitBar.AbsoluteLayoutAdapter.prototype = {
19767     init : function(s){
19768         this.basic.init(s);
19769     },
19770
19771     getElementSize : function(s){
19772         return this.basic.getElementSize(s);
19773     },
19774
19775     setElementSize : function(s, newSize, onComplete){
19776         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
19777     },
19778
19779     moveSplitter : function(s){
19780         var yes = Ext.SplitBar;
19781         switch(s.placement){
19782             case yes.LEFT:
19783                 s.el.setX(s.resizingEl.getRight());
19784                 break;
19785             case yes.RIGHT:
19786                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
19787                 break;
19788             case yes.TOP:
19789                 s.el.setY(s.resizingEl.getBottom());
19790                 break;
19791             case yes.BOTTOM:
19792                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
19793                 break;
19794         }
19795     }
19796 };
19797
19798 /**
19799  * Orientation constant - Create a vertical SplitBar
19800  * @static
19801  * @type Number
19802  */
19803 Ext.SplitBar.VERTICAL = 1;
19804
19805 /**
19806  * Orientation constant - Create a horizontal SplitBar
19807  * @static
19808  * @type Number
19809  */
19810 Ext.SplitBar.HORIZONTAL = 2;
19811
19812 /**
19813  * Placement constant - The resizing element is to the left of the splitter element
19814  * @static
19815  * @type Number
19816  */
19817 Ext.SplitBar.LEFT = 1;
19818
19819 /**
19820  * Placement constant - The resizing element is to the right of the splitter element
19821  * @static
19822  * @type Number
19823  */
19824 Ext.SplitBar.RIGHT = 2;
19825
19826 /**
19827  * Placement constant - The resizing element is positioned above the splitter element
19828  * @static
19829  * @type Number
19830  */
19831 Ext.SplitBar.TOP = 3;
19832
19833 /**
19834  * Placement constant - The resizing element is positioned under splitter element
19835  * @static
19836  * @type Number
19837  */
19838 Ext.SplitBar.BOTTOM = 4;
19839 /**
19840  * @class Ext.Container
19841  * @extends Ext.BoxComponent
19842  * <p>Base class for any {@link Ext.BoxComponent} that may contain other Components. Containers handle the
19843  * basic behavior of containing items, namely adding, inserting and removing items.</p>
19844  *
19845  * <p>The most commonly used Container classes are {@link Ext.Panel}, {@link Ext.Window} and {@link Ext.TabPanel}.
19846  * If you do not need the capabilities offered by the aforementioned classes you can create a lightweight
19847  * Container to be encapsulated by an HTML element to your specifications by using the
19848  * <code><b>{@link Ext.Component#autoEl autoEl}</b></code> config option. This is a useful technique when creating
19849  * embedded {@link Ext.layout.ColumnLayout column} layouts inside {@link Ext.form.FormPanel FormPanels}
19850  * for example.</p>
19851  *
19852  * <p>The code below illustrates both how to explicitly create a Container, and how to implicitly
19853  * create one using the <b><code>'container'</code></b> xtype:<pre><code>
19854 // explicitly create a Container
19855 var embeddedColumns = new Ext.Container({
19856     autoEl: 'div',  // This is the default
19857     layout: 'column',
19858     defaults: {
19859         // implicitly create Container by specifying xtype
19860         xtype: 'container',
19861         autoEl: 'div', // This is the default.
19862         layout: 'form',
19863         columnWidth: 0.5,
19864         style: {
19865             padding: '10px'
19866         }
19867     },
19868 //  The two items below will be Ext.Containers, each encapsulated by a &lt;DIV> element.
19869     items: [{
19870         items: {
19871             xtype: 'datefield',
19872             name: 'startDate',
19873             fieldLabel: 'Start date'
19874         }
19875     }, {
19876         items: {
19877             xtype: 'datefield',
19878             name: 'endDate',
19879             fieldLabel: 'End date'
19880         }
19881     }]
19882 });</code></pre></p>
19883  *
19884  * <p><u><b>Layout</b></u></p>
19885  * <p>Container classes delegate the rendering of child Components to a layout
19886  * manager class which must be configured into the Container using the
19887  * <code><b>{@link #layout}</b></code> configuration property.</p>
19888  * <p>When either specifying child <code>{@link #items}</code> of a Container,
19889  * or dynamically {@link #add adding} Components to a Container, remember to
19890  * consider how you wish the Container to arrange those child elements, and
19891  * whether those child elements need to be sized using one of Ext's built-in
19892  * <b><code>{@link #layout}</code></b> schemes. By default, Containers use the
19893  * {@link Ext.layout.ContainerLayout ContainerLayout} scheme which only
19894  * renders child components, appending them one after the other inside the
19895  * Container, and <b>does not apply any sizing</b> at all.</p>
19896  * <p>A common mistake is when a developer neglects to specify a
19897  * <b><code>{@link #layout}</code></b> (e.g. widgets like GridPanels or
19898  * TreePanels are added to Containers for which no <code><b>{@link #layout}</b></code>
19899  * has been specified). If a Container is left to use the default
19900  * {@link Ext.layout.ContainerLayout ContainerLayout} scheme, none of its
19901  * child components will be resized, or changed in any way when the Container
19902  * is resized.</p>
19903  * <p>Certain layout managers allow dynamic addition of child components.
19904  * Those that do include {@link Ext.layout.CardLayout},
19905  * {@link Ext.layout.AnchorLayout}, {@link Ext.layout.FormLayout}, and
19906  * {@link Ext.layout.TableLayout}. For example:<pre><code>
19907 //  Create the GridPanel.
19908 var myNewGrid = new Ext.grid.GridPanel({
19909     store: myStore,
19910     columns: myColumnModel,
19911     title: 'Results', // the title becomes the title of the tab
19912 });
19913
19914 myTabPanel.add(myNewGrid); // {@link Ext.TabPanel} implicitly uses {@link Ext.layout.CardLayout CardLayout}
19915 myTabPanel.{@link Ext.TabPanel#setActiveTab setActiveTab}(myNewGrid);
19916  * </code></pre></p>
19917  * <p>The example above adds a newly created GridPanel to a TabPanel. Note that
19918  * a TabPanel uses {@link Ext.layout.CardLayout} as its layout manager which
19919  * means all its child items are sized to {@link Ext.layout.FitLayout fit}
19920  * exactly into its client area.
19921  * <p><b><u>Overnesting is a common problem</u></b>.
19922  * An example of overnesting occurs when a GridPanel is added to a TabPanel
19923  * by wrapping the GridPanel <i>inside</i> a wrapping Panel (that has no
19924  * <code><b>{@link #layout}</b></code> specified) and then add that wrapping Panel
19925  * to the TabPanel. The point to realize is that a GridPanel <b>is</b> a
19926  * Component which can be added directly to a Container. If the wrapping Panel
19927  * has no <code><b>{@link #layout}</b></code> configuration, then the overnested
19928  * GridPanel will not be sized as expected.<p>
19929  *
19930  * <p><u><b>Adding via remote configuration</b></u></p>
19931  *
19932  * <p>A server side script can be used to add Components which are generated dynamically on the server.
19933  * An example of adding a GridPanel to a TabPanel where the GridPanel is generated by the server
19934  * based on certain parameters:
19935  * </p><pre><code>
19936 // execute an Ajax request to invoke server side script:
19937 Ext.Ajax.request({
19938     url: 'gen-invoice-grid.php',
19939     // send additional parameters to instruct server script
19940     params: {
19941         startDate: Ext.getCmp('start-date').getValue(),
19942         endDate: Ext.getCmp('end-date').getValue()
19943     },
19944     // process the response object to add it to the TabPanel:
19945     success: function(xhr) {
19946         var newComponent = eval(xhr.responseText); // see discussion below
19947         myTabPanel.add(newComponent); // add the component to the TabPanel
19948         myTabPanel.setActiveTab(newComponent);
19949     },
19950     failure: function() {
19951         Ext.Msg.alert("Grid create failed", "Server communication failure");
19952     }
19953 });
19954 </code></pre>
19955  * <p>The server script needs to return an executable Javascript statement which, when processed
19956  * using <code>eval()</code>, will return either a config object with an {@link Ext.Component#xtype xtype},
19957  * or an instantiated Component. The server might return this for example:</p><pre><code>
19958 (function() {
19959     function formatDate(value){
19960         return value ? value.dateFormat('M d, Y') : '';
19961     };
19962
19963     var store = new Ext.data.Store({
19964         url: 'get-invoice-data.php',
19965         baseParams: {
19966             startDate: '01/01/2008',
19967             endDate: '01/31/2008'
19968         },
19969         reader: new Ext.data.JsonReader({
19970             record: 'transaction',
19971             idProperty: 'id',
19972             totalRecords: 'total'
19973         }, [
19974            'customer',
19975            'invNo',
19976            {name: 'date', type: 'date', dateFormat: 'm/d/Y'},
19977            {name: 'value', type: 'float'}
19978         ])
19979     });
19980
19981     var grid = new Ext.grid.GridPanel({
19982         title: 'Invoice Report',
19983         bbar: new Ext.PagingToolbar(store),
19984         store: store,
19985         columns: [
19986             {header: "Customer", width: 250, dataIndex: 'customer', sortable: true},
19987             {header: "Invoice Number", width: 120, dataIndex: 'invNo', sortable: true},
19988             {header: "Invoice Date", width: 100, dataIndex: 'date', renderer: formatDate, sortable: true},
19989             {header: "Value", width: 120, dataIndex: 'value', renderer: 'usMoney', sortable: true}
19990         ],
19991     });
19992     store.load();
19993     return grid;  // return instantiated component
19994 })();
19995 </code></pre>
19996  * <p>When the above code fragment is passed through the <code>eval</code> function in the success handler
19997  * of the Ajax request, the code is executed by the Javascript processor, and the anonymous function
19998  * runs, and returns the instantiated grid component.</p>
19999  * <p>Note: since the code above is <i>generated</i> by a server script, the <code>baseParams</code> for
20000  * the Store, the metadata to allow generation of the Record layout, and the ColumnModel
20001  * can all be generated into the code since these are all known on the server.</p>
20002  *
20003  * @xtype container
20004  */
20005 Ext.Container = Ext.extend(Ext.BoxComponent, {
20006     /**
20007      * @cfg {Boolean} monitorResize
20008      * True to automatically monitor window resize events to handle anything that is sensitive to the current size
20009      * of the viewport.  This value is typically managed by the chosen <code>{@link #layout}</code> and should not need
20010      * to be set manually.
20011      */
20012     /**
20013      * @cfg {String/Object} layout
20014      * <p><b>*Important</b>: In order for child items to be correctly sized and
20015      * positioned, typically a layout manager <b>must</b> be specified through
20016      * the <code>layout</code> configuration option.</p>
20017      * <br><p>The sizing and positioning of child {@link items} is the responsibility of
20018      * the Container's layout manager which creates and manages the type of layout
20019      * you have in mind.  For example:</p><pre><code>
20020 new Ext.Window({
20021     width:300, height: 300,
20022     layout: 'fit', // explicitly set layout manager: override the default (layout:'auto')
20023     items: [{
20024         title: 'Panel inside a Window'
20025     }]
20026 }).show();
20027      * </code></pre>
20028      * <p>If the {@link #layout} configuration is not explicitly specified for
20029      * a general purpose container (e.g. Container or Panel) the
20030      * {@link Ext.layout.ContainerLayout default layout manager} will be used
20031      * which does nothing but render child components sequentially into the
20032      * Container (no sizing or positioning will be performed in this situation).
20033      * Some container classes implicitly specify a default layout
20034      * (e.g. FormPanel specifies <code>layout:'form'</code>). Other specific
20035      * purpose classes internally specify/manage their internal layout (e.g.
20036      * GridPanel, TabPanel, TreePanel, Toolbar, Menu, etc.).</p>
20037      * <br><p><b><code>layout</code></b> may be specified as either as an Object or
20038      * as a String:</p><div><ul class="mdetail-params">
20039      *
20040      * <li><u>Specify as an Object</u></li>
20041      * <div><ul class="mdetail-params">
20042      * <li>Example usage:</li>
20043 <pre><code>
20044 layout: {
20045     type: 'vbox',
20046     padding: '5',
20047     align: 'left'
20048 }
20049 </code></pre>
20050      *
20051      * <li><code><b>type</b></code></li>
20052      * <br/><p>The layout type to be used for this container.  If not specified,
20053      * a default {@link Ext.layout.ContainerLayout} will be created and used.</p>
20054      * <br/><p>Valid layout <code>type</code> values are:</p>
20055      * <div class="sub-desc"><ul class="mdetail-params">
20056      * <li><code><b>{@link Ext.layout.AbsoluteLayout absolute}</b></code></li>
20057      * <li><code><b>{@link Ext.layout.AccordionLayout accordion}</b></code></li>
20058      * <li><code><b>{@link Ext.layout.AnchorLayout anchor}</b></code></li>
20059      * <li><code><b>{@link Ext.layout.ContainerLayout auto}</b></code> &nbsp;&nbsp;&nbsp; <b>Default</b></li>
20060      * <li><code><b>{@link Ext.layout.BorderLayout border}</b></code></li>
20061      * <li><code><b>{@link Ext.layout.CardLayout card}</b></code></li>
20062      * <li><code><b>{@link Ext.layout.ColumnLayout column}</b></code></li>
20063      * <li><code><b>{@link Ext.layout.FitLayout fit}</b></code></li>
20064      * <li><code><b>{@link Ext.layout.FormLayout form}</b></code></li>
20065      * <li><code><b>{@link Ext.layout.HBoxLayout hbox}</b></code></li>
20066      * <li><code><b>{@link Ext.layout.MenuLayout menu}</b></code></li>
20067      * <li><code><b>{@link Ext.layout.TableLayout table}</b></code></li>
20068      * <li><code><b>{@link Ext.layout.ToolbarLayout toolbar}</b></code></li>
20069      * <li><code><b>{@link Ext.layout.VBoxLayout vbox}</b></code></li>
20070      * </ul></div>
20071      *
20072      * <li>Layout specific configuration properties</li>
20073      * <br/><p>Additional layout specific configuration properties may also be
20074      * specified. For complete details regarding the valid config options for
20075      * each layout type, see the layout class corresponding to the <code>type</code>
20076      * specified.</p>
20077      *
20078      * </ul></div>
20079      *
20080      * <li><u>Specify as a String</u></li>
20081      * <div><ul class="mdetail-params">
20082      * <li>Example usage:</li>
20083 <pre><code>
20084 layout: 'vbox',
20085 layoutConfig: {
20086     padding: '5',
20087     align: 'left'
20088 }
20089 </code></pre>
20090      * <li><code><b>layout</b></code></li>
20091      * <br/><p>The layout <code>type</code> to be used for this container (see list
20092      * of valid layout type values above).</p><br/>
20093      * <li><code><b>{@link #layoutConfig}</b></code></li>
20094      * <br/><p>Additional layout specific configuration properties. For complete
20095      * details regarding the valid config options for each layout type, see the
20096      * layout class corresponding to the <code>layout</code> specified.</p>
20097      * </ul></div></ul></div>
20098      */
20099     /**
20100      * @cfg {Object} layoutConfig
20101      * This is a config object containing properties specific to the chosen
20102      * <b><code>{@link #layout}</code></b> if <b><code>{@link #layout}</code></b>
20103      * has been specified as a <i>string</i>.</p>
20104      */
20105     /**
20106      * @cfg {Boolean/Number} bufferResize
20107      * When set to true (50 milliseconds) or a number of milliseconds, the layout assigned for this container will buffer
20108      * the frequency it calculates and does a re-layout of components. This is useful for heavy containers or containers
20109      * with a large quantity of sub-components for which frequent layout calls would be expensive. Defaults to <code>50</code>.
20110      */
20111     bufferResize: 50,
20112
20113     /**
20114      * @cfg {String/Number} activeItem
20115      * A string component id or the numeric index of the component that should be initially activated within the
20116      * container's layout on render.  For example, activeItem: 'item-1' or activeItem: 0 (index 0 = the first
20117      * item in the container's collection).  activeItem only applies to layout styles that can display
20118      * items one at a time (like {@link Ext.layout.AccordionLayout}, {@link Ext.layout.CardLayout} and
20119      * {@link Ext.layout.FitLayout}).  Related to {@link Ext.layout.ContainerLayout#activeItem}.
20120      */
20121     /**
20122      * @cfg {Object/Array} items
20123      * <pre><b>** IMPORTANT</b>: be sure to <b>{@link #layout specify a <code>layout</code>} if needed ! **</b></pre>
20124      * <p>A single item, or an array of child Components to be added to this container,
20125      * for example:</p>
20126      * <pre><code>
20127 // specifying a single item
20128 items: {...},
20129 layout: 'fit',    // specify a layout!
20130
20131 // specifying multiple items
20132 items: [{...}, {...}],
20133 layout: 'anchor', // specify a layout!
20134      * </code></pre>
20135      * <p>Each item may be:</p>
20136      * <div><ul class="mdetail-params">
20137      * <li>any type of object based on {@link Ext.Component}</li>
20138      * <li>a fully instanciated object or</li>
20139      * <li>an object literal that:</li>
20140      * <div><ul class="mdetail-params">
20141      * <li>has a specified <code>{@link Ext.Component#xtype xtype}</code></li>
20142      * <li>the {@link Ext.Component#xtype} specified is associated with the Component
20143      * desired and should be chosen from one of the available xtypes as listed
20144      * in {@link Ext.Component}.</li>
20145      * <li>If an <code>{@link Ext.Component#xtype xtype}</code> is not explicitly
20146      * specified, the {@link #defaultType} for that Container is used.</li>
20147      * <li>will be "lazily instanciated", avoiding the overhead of constructing a fully
20148      * instanciated Component object</li>
20149      * </ul></div></ul></div>
20150      * <p><b>Notes</b>:</p>
20151      * <div><ul class="mdetail-params">
20152      * <li>Ext uses lazy rendering. Child Components will only be rendered
20153      * should it become necessary. Items are automatically laid out when they are first
20154      * shown (no sizing is done while hidden), or in response to a {@link #doLayout} call.</li>
20155      * <li>Do not specify <code>{@link Ext.Panel#contentEl contentEl}</code>/
20156      * <code>{@link Ext.Panel#html html}</code> with <code>items</code>.</li>
20157      * </ul></div>
20158      */
20159     /**
20160      * @cfg {Object|Function} defaults
20161      * <p>This option is a means of applying default settings to all added items whether added through the {@link #items}
20162      * config or via the {@link #add} or {@link #insert} methods.</p>
20163      * <p>If an added item is a config object, and <b>not</b> an instantiated Component, then the default properties are
20164      * unconditionally applied. If the added item <b>is</b> an instantiated Component, then the default properties are
20165      * applied conditionally so as not to override existing properties in the item.</p>
20166      * <p>If the defaults option is specified as a function, then the function will be called using this Container as the
20167      * scope (<code>this</code> reference) and passing the added item as the first parameter. Any resulting object
20168      * from that call is then applied to the item as default properties.</p>
20169      * <p>For example, to automatically apply padding to the body of each of a set of
20170      * contained {@link Ext.Panel} items, you could pass: <code>defaults: {bodyStyle:'padding:15px'}</code>.</p>
20171      * <p>Usage:</p><pre><code>
20172 defaults: {               // defaults are applied to items, not the container
20173     autoScroll:true
20174 },
20175 items: [
20176     {
20177         xtype: 'panel',   // defaults <b>do not</b> have precedence over
20178         id: 'panel1',     // options in config objects, so the defaults
20179         autoScroll: false // will not be applied here, panel1 will be autoScroll:false
20180     },
20181     new Ext.Panel({       // defaults <b>do</b> have precedence over options
20182         id: 'panel2',     // options in components, so the defaults
20183         autoScroll: false // will be applied here, panel2 will be autoScroll:true.
20184     })
20185 ]
20186      * </code></pre>
20187      */
20188
20189
20190     /** @cfg {Boolean} autoDestroy
20191      * If true the container will automatically destroy any contained component that is removed from it, else
20192      * destruction must be handled manually (defaults to true).
20193      */
20194     autoDestroy : true,
20195
20196     /** @cfg {Boolean} forceLayout
20197      * If true the container will force a layout initially even if hidden or collapsed. This option
20198      * is useful for forcing forms to render in collapsed or hidden containers. (defaults to false).
20199      */
20200     forceLayout: false,
20201
20202     /** @cfg {Boolean} hideBorders
20203      * True to hide the borders of each contained component, false to defer to the component's existing
20204      * border settings (defaults to false).
20205      */
20206     /** @cfg {String} defaultType
20207      * <p>The default {@link Ext.Component xtype} of child Components to create in this Container when
20208      * a child item is specified as a raw configuration object, rather than as an instantiated Component.</p>
20209      * <p>Defaults to <code>'panel'</code>, except {@link Ext.menu.Menu} which defaults to <code>'menuitem'</code>,
20210      * and {@link Ext.Toolbar} and {@link Ext.ButtonGroup} which default to <code>'button'</code>.</p>
20211      */
20212     defaultType : 'panel',
20213
20214     /** @cfg {String} resizeEvent
20215      * The event to listen to for resizing in layouts. Defaults to <code>'resize'</code>.
20216      */
20217     resizeEvent: 'resize',
20218
20219     /**
20220      * @cfg {Array} bubbleEvents
20221      * <p>An array of events that, when fired, should be bubbled to any parent container.
20222      * See {@link Ext.util.Observable#enableBubble}.
20223      * Defaults to <code>['add', 'remove']</code>.
20224      */
20225     bubbleEvents: ['add', 'remove'],
20226
20227     // private
20228     initComponent : function(){
20229         Ext.Container.superclass.initComponent.call(this);
20230
20231         this.addEvents(
20232             /**
20233              * @event afterlayout
20234              * Fires when the components in this container are arranged by the associated layout manager.
20235              * @param {Ext.Container} this
20236              * @param {ContainerLayout} layout The ContainerLayout implementation for this container
20237              */
20238             'afterlayout',
20239             /**
20240              * @event beforeadd
20241              * Fires before any {@link Ext.Component} is added or inserted into the container.
20242              * A handler can return false to cancel the add.
20243              * @param {Ext.Container} this
20244              * @param {Ext.Component} component The component being added
20245              * @param {Number} index The index at which the component will be added to the container's items collection
20246              */
20247             'beforeadd',
20248             /**
20249              * @event beforeremove
20250              * Fires before any {@link Ext.Component} is removed from the container.  A handler can return
20251              * false to cancel the remove.
20252              * @param {Ext.Container} this
20253              * @param {Ext.Component} component The component being removed
20254              */
20255             'beforeremove',
20256             /**
20257              * @event add
20258              * @bubbles
20259              * Fires after any {@link Ext.Component} is added or inserted into the container.
20260              * @param {Ext.Container} this
20261              * @param {Ext.Component} component The component that was added
20262              * @param {Number} index The index at which the component was added to the container's items collection
20263              */
20264             'add',
20265             /**
20266              * @event remove
20267              * @bubbles
20268              * Fires after any {@link Ext.Component} is removed from the container.
20269              * @param {Ext.Container} this
20270              * @param {Ext.Component} component The component that was removed
20271              */
20272             'remove'
20273         );
20274
20275         /**
20276          * The collection of components in this container as a {@link Ext.util.MixedCollection}
20277          * @type MixedCollection
20278          * @property items
20279          */
20280         var items = this.items;
20281         if(items){
20282             delete this.items;
20283             this.add(items);
20284         }
20285     },
20286
20287     // private
20288     initItems : function(){
20289         if(!this.items){
20290             this.items = new Ext.util.MixedCollection(false, this.getComponentId);
20291             this.getLayout(); // initialize the layout
20292         }
20293     },
20294
20295     // private
20296     setLayout : function(layout){
20297         if(this.layout && this.layout != layout){
20298             this.layout.setContainer(null);
20299         }
20300         this.layout = layout;
20301         this.initItems();
20302         layout.setContainer(this);
20303     },
20304
20305     afterRender: function(){
20306         // Render this Container, this should be done before setLayout is called which
20307         // will hook onResize
20308         Ext.Container.superclass.afterRender.call(this);
20309         if(!this.layout){
20310             this.layout = 'auto';
20311         }
20312         if(Ext.isObject(this.layout) && !this.layout.layout){
20313             this.layoutConfig = this.layout;
20314             this.layout = this.layoutConfig.type;
20315         }
20316         if(Ext.isString(this.layout)){
20317             this.layout = new Ext.Container.LAYOUTS[this.layout.toLowerCase()](this.layoutConfig);
20318         }
20319         this.setLayout(this.layout);
20320
20321         // If a CardLayout, the active item set
20322         if(this.activeItem !== undefined && this.layout.setActiveItem){
20323             var item = this.activeItem;
20324             delete this.activeItem;
20325             this.layout.setActiveItem(item);
20326         }
20327
20328         // If we have no ownerCt, render and size all children
20329         if(!this.ownerCt){
20330             this.doLayout(false, true);
20331         }
20332
20333         // This is a manually configured flag set by users in conjunction with renderTo.
20334         // Not to be confused with the flag by the same name used in Layouts.
20335         if(this.monitorResize === true){
20336             Ext.EventManager.onWindowResize(this.doLayout, this, [false]);
20337         }
20338     },
20339
20340     /**
20341      * <p>Returns the Element to be used to contain the child Components of this Container.</p>
20342      * <p>An implementation is provided which returns the Container's {@link #getEl Element}, but
20343      * if there is a more complex structure to a Container, this may be overridden to return
20344      * the element into which the {@link #layout layout} renders child Components.</p>
20345      * @return {Ext.Element} The Element to render child Components into.
20346      */
20347     getLayoutTarget : function(){
20348         return this.el;
20349     },
20350
20351     // private - used as the key lookup function for the items collection
20352     getComponentId : function(comp){
20353         return comp.getItemId();
20354     },
20355
20356     /**
20357      * <p>Adds {@link Ext.Component Component}(s) to this Container.</p>
20358      * <br><p><b>Description</b></u> :
20359      * <div><ul class="mdetail-params">
20360      * <li>Fires the {@link #beforeadd} event before adding</li>
20361      * <li>The Container's {@link #defaults default config values} will be applied
20362      * accordingly (see <code>{@link #defaults}</code> for details).</li>
20363      * <li>Fires the {@link #add} event after the component has been added.</li>
20364      * </ul></div>
20365      * <br><p><b>Notes</b></u> :
20366      * <div><ul class="mdetail-params">
20367      * <li>If the Container is <i>already rendered</i> when <code>add</code>
20368      * is called, you may need to call {@link #doLayout} to refresh the view which causes
20369      * any unrendered child Components to be rendered. This is required so that you can
20370      * <code>add</code> multiple child components if needed while only refreshing the layout
20371      * once. For example:<pre><code>
20372 var tb = new {@link Ext.Toolbar}();
20373 tb.render(document.body);  // toolbar is rendered
20374 tb.add({text:'Button 1'}); // add multiple items ({@link #defaultType} for {@link Ext.Toolbar Toolbar} is 'button')
20375 tb.add({text:'Button 2'});
20376 tb.{@link #doLayout}();             // refresh the layout
20377      * </code></pre></li>
20378      * <li><i>Warning:</i> Containers directly managed by the BorderLayout layout manager
20379      * may not be removed or added.  See the Notes for {@link Ext.layout.BorderLayout BorderLayout}
20380      * for more details.</li>
20381      * </ul></div>
20382      * @param {...Object/Array} component
20383      * <p>Either one or more Components to add or an Array of Components to add.  See
20384      * <code>{@link #items}</code> for additional information.</p>
20385      * @return {Ext.Component/Array} The Components that were added.
20386      */
20387     add : function(comp){
20388         this.initItems();
20389         var args = arguments.length > 1;
20390         if(args || Ext.isArray(comp)){
20391             var result = [];
20392             Ext.each(args ? arguments : comp, function(c){
20393                 result.push(this.add(c));
20394             }, this);
20395             return result;
20396         }
20397         var c = this.lookupComponent(this.applyDefaults(comp));
20398         var index = this.items.length;
20399         if(this.fireEvent('beforeadd', this, c, index) !== false && this.onBeforeAdd(c) !== false){
20400             this.items.add(c);
20401             // *onAdded
20402             c.onAdded(this, index);
20403             this.onAdd(c);
20404             this.fireEvent('add', this, c, index);
20405         }
20406         return c;
20407     },
20408
20409     onAdd : function(c){
20410         // Empty template method
20411     },
20412
20413     // private
20414     onAdded : function(container, pos) {
20415         //overridden here so we can cascade down, not worth creating a template method.
20416         this.ownerCt = container;
20417         this.initRef();
20418         //initialize references for child items
20419         this.cascade(function(c){
20420             c.initRef();
20421         });
20422         this.fireEvent('added', this, container, pos);
20423     },
20424
20425     /**
20426      * Inserts a Component into this Container at a specified index. Fires the
20427      * {@link #beforeadd} event before inserting, then fires the {@link #add} event after the
20428      * Component has been inserted.
20429      * @param {Number} index The index at which the Component will be inserted
20430      * into the Container's items collection
20431      * @param {Ext.Component} component The child Component to insert.<br><br>
20432      * Ext uses lazy rendering, and will only render the inserted Component should
20433      * it become necessary.<br><br>
20434      * A Component config object may be passed in order to avoid the overhead of
20435      * constructing a real Component object if lazy rendering might mean that the
20436      * inserted Component will not be rendered immediately. To take advantage of
20437      * this 'lazy instantiation', set the {@link Ext.Component#xtype} config
20438      * property to the registered type of the Component wanted.<br><br>
20439      * For a list of all available xtypes, see {@link Ext.Component}.
20440      * @return {Ext.Component} component The Component (or config object) that was
20441      * inserted with the Container's default config values applied.
20442      */
20443     insert : function(index, comp) {
20444         var args   = arguments,
20445             length = args.length,
20446             result = [],
20447             i, c;
20448         
20449         this.initItems();
20450         
20451         if (length > 2) {
20452             for (i = length - 1; i >= 1; --i) {
20453                 result.push(this.insert(index, args[i]));
20454             }
20455             return result;
20456         }
20457         
20458         c = this.lookupComponent(this.applyDefaults(comp));
20459         index = Math.min(index, this.items.length);
20460         
20461         if (this.fireEvent('beforeadd', this, c, index) !== false && this.onBeforeAdd(c) !== false) {
20462             if (c.ownerCt == this) {
20463                 this.items.remove(c);
20464             }
20465             this.items.insert(index, c);
20466             c.onAdded(this, index);
20467             this.onAdd(c);
20468             this.fireEvent('add', this, c, index);
20469         }
20470         
20471         return c;
20472     },
20473
20474     // private
20475     applyDefaults : function(c){
20476         var d = this.defaults;
20477         if(d){
20478             if(Ext.isFunction(d)){
20479                 d = d.call(this, c);
20480             }
20481             if(Ext.isString(c)){
20482                 c = Ext.ComponentMgr.get(c);
20483                 Ext.apply(c, d);
20484             }else if(!c.events){
20485                 Ext.applyIf(c.isAction ? c.initialConfig : c, d);
20486             }else{
20487                 Ext.apply(c, d);
20488             }
20489         }
20490         return c;
20491     },
20492
20493     // private
20494     onBeforeAdd : function(item){
20495         if(item.ownerCt){
20496             item.ownerCt.remove(item, false);
20497         }
20498         if(this.hideBorders === true){
20499             item.border = (item.border === true);
20500         }
20501     },
20502
20503     /**
20504      * Removes a component from this container.  Fires the {@link #beforeremove} event before removing, then fires
20505      * the {@link #remove} event after the component has been removed.
20506      * @param {Component/String} component The component reference or id to remove.
20507      * @param {Boolean} autoDestroy (optional) True to automatically invoke the removed Component's {@link Ext.Component#destroy} function.
20508      * Defaults to the value of this Container's {@link #autoDestroy} config.
20509      * @return {Ext.Component} component The Component that was removed.
20510      */
20511     remove : function(comp, autoDestroy){
20512         this.initItems();
20513         var c = this.getComponent(comp);
20514         if(c && this.fireEvent('beforeremove', this, c) !== false){
20515             this.doRemove(c, autoDestroy);
20516             this.fireEvent('remove', this, c);
20517         }
20518         return c;
20519     },
20520
20521     onRemove: function(c){
20522         // Empty template method
20523     },
20524
20525     // private
20526     doRemove: function(c, autoDestroy){
20527         var l = this.layout,
20528             hasLayout = l && this.rendered;
20529
20530         if(hasLayout){
20531             l.onRemove(c);
20532         }
20533         this.items.remove(c);
20534         c.onRemoved();
20535         this.onRemove(c);
20536         if(autoDestroy === true || (autoDestroy !== false && this.autoDestroy)){
20537             c.destroy();
20538         }
20539         if(hasLayout){
20540             l.afterRemove(c);
20541         }
20542     },
20543
20544     /**
20545      * Removes all components from this container.
20546      * @param {Boolean} autoDestroy (optional) True to automatically invoke the removed Component's {@link Ext.Component#destroy} function.
20547      * Defaults to the value of this Container's {@link #autoDestroy} config.
20548      * @return {Array} Array of the destroyed components
20549      */
20550     removeAll: function(autoDestroy){
20551         this.initItems();
20552         var item, rem = [], items = [];
20553         this.items.each(function(i){
20554             rem.push(i);
20555         });
20556         for (var i = 0, len = rem.length; i < len; ++i){
20557             item = rem[i];
20558             this.remove(item, autoDestroy);
20559             if(item.ownerCt !== this){
20560                 items.push(item);
20561             }
20562         }
20563         return items;
20564     },
20565
20566     /**
20567      * Examines this container's <code>{@link #items}</code> <b>property</b>
20568      * and gets a direct child component of this container.
20569      * @param {String/Number} comp This parameter may be any of the following:
20570      * <div><ul class="mdetail-params">
20571      * <li>a <b><code>String</code></b> : representing the <code>{@link Ext.Component#itemId itemId}</code>
20572      * or <code>{@link Ext.Component#id id}</code> of the child component </li>
20573      * <li>a <b><code>Number</code></b> : representing the position of the child component
20574      * within the <code>{@link #items}</code> <b>property</b></li>
20575      * </ul></div>
20576      * <p>For additional information see {@link Ext.util.MixedCollection#get}.
20577      * @return Ext.Component The component (if found).
20578      */
20579     getComponent : function(comp){
20580         if(Ext.isObject(comp)){
20581             comp = comp.getItemId();
20582         }
20583         return this.items.get(comp);
20584     },
20585
20586     // private
20587     lookupComponent : function(comp){
20588         if(Ext.isString(comp)){
20589             return Ext.ComponentMgr.get(comp);
20590         }else if(!comp.events){
20591             return this.createComponent(comp);
20592         }
20593         return comp;
20594     },
20595
20596     // private
20597     createComponent : function(config, defaultType){
20598         if (config.render) {
20599             return config;
20600         }
20601         // add in ownerCt at creation time but then immediately
20602         // remove so that onBeforeAdd can handle it
20603         var c = Ext.create(Ext.apply({
20604             ownerCt: this
20605         }, config), defaultType || this.defaultType);
20606         delete c.initialConfig.ownerCt;
20607         delete c.ownerCt;
20608         return c;
20609     },
20610
20611     /**
20612      * @private
20613      * We can only lay out if there is a view area in which to layout.
20614      * display:none on the layout target, *or any of its parent elements* will mean it has no view area.
20615      */
20616     canLayout : function() {
20617         var el = this.getVisibilityEl();
20618         return el && el.dom && !el.isStyle("display", "none");
20619     },
20620
20621     /**
20622      * Force this container's layout to be recalculated. A call to this function is required after adding a new component
20623      * to an already rendered container, or possibly after changing sizing/position properties of child components.
20624      * @param {Boolean} shallow (optional) True to only calc the layout of this component, and let child components auto
20625      * calc layouts as required (defaults to false, which calls doLayout recursively for each subcontainer)
20626      * @param {Boolean} force (optional) True to force a layout to occur, even if the item is hidden.
20627      * @return {Ext.Container} this
20628      */
20629
20630     doLayout : function(shallow, force){
20631         var rendered = this.rendered,
20632             forceLayout = force || this.forceLayout;
20633
20634         if(this.collapsed || !this.canLayout()){
20635             this.deferLayout = this.deferLayout || !shallow;
20636             if(!forceLayout){
20637                 return;
20638             }
20639             shallow = shallow && !this.deferLayout;
20640         } else {
20641             delete this.deferLayout;
20642         }
20643         if(rendered && this.layout){
20644             this.layout.layout();
20645         }
20646         if(shallow !== true && this.items){
20647             var cs = this.items.items;
20648             for(var i = 0, len = cs.length; i < len; i++){
20649                 var c = cs[i];
20650                 if(c.doLayout){
20651                     c.doLayout(false, forceLayout);
20652                 }
20653             }
20654         }
20655         if(rendered){
20656             this.onLayout(shallow, forceLayout);
20657         }
20658         // Initial layout completed
20659         this.hasLayout = true;
20660         delete this.forceLayout;
20661     },
20662
20663     onLayout : Ext.emptyFn,
20664
20665     // private
20666     shouldBufferLayout: function(){
20667         /*
20668          * Returns true if the container should buffer a layout.
20669          * This is true only if the container has previously been laid out
20670          * and has a parent container that is pending a layout.
20671          */
20672         var hl = this.hasLayout;
20673         if(this.ownerCt){
20674             // Only ever buffer if we've laid out the first time and we have one pending.
20675             return hl ? !this.hasLayoutPending() : false;
20676         }
20677         // Never buffer initial layout
20678         return hl;
20679     },
20680
20681     // private
20682     hasLayoutPending: function(){
20683         // Traverse hierarchy to see if any parent container has a pending layout.
20684         var pending = false;
20685         this.ownerCt.bubble(function(c){
20686             if(c.layoutPending){
20687                 pending = true;
20688                 return false;
20689             }
20690         });
20691         return pending;
20692     },
20693
20694     onShow : function(){
20695         // removes css classes that were added to hide
20696         Ext.Container.superclass.onShow.call(this);
20697         // If we were sized during the time we were hidden, layout.
20698         if(Ext.isDefined(this.deferLayout)){
20699             delete this.deferLayout;
20700             this.doLayout(true);
20701         }
20702     },
20703
20704     /**
20705      * Returns the layout currently in use by the container.  If the container does not currently have a layout
20706      * set, a default {@link Ext.layout.ContainerLayout} will be created and set as the container's layout.
20707      * @return {ContainerLayout} layout The container's layout
20708      */
20709     getLayout : function(){
20710         if(!this.layout){
20711             var layout = new Ext.layout.AutoLayout(this.layoutConfig);
20712             this.setLayout(layout);
20713         }
20714         return this.layout;
20715     },
20716
20717     // private
20718     beforeDestroy : function(){
20719         var c;
20720         if(this.items){
20721             while(c = this.items.first()){
20722                 this.doRemove(c, true);
20723             }
20724         }
20725         if(this.monitorResize){
20726             Ext.EventManager.removeResizeListener(this.doLayout, this);
20727         }
20728         Ext.destroy(this.layout);
20729         Ext.Container.superclass.beforeDestroy.call(this);
20730     },
20731
20732     /**
20733      * Cascades down the component/container heirarchy from this component (called first), calling the specified function with
20734      * each component. The scope (<i>this</i>) of
20735      * function call will be the scope provided or the current component. The arguments to the function
20736      * will be the args provided or the current component. If the function returns false at any point,
20737      * the cascade is stopped on that branch.
20738      * @param {Function} fn The function to call
20739      * @param {Object} scope (optional) The scope of the function (defaults to current component)
20740      * @param {Array} args (optional) The args to call the function with (defaults to passing the current component)
20741      * @return {Ext.Container} this
20742      */
20743     cascade : function(fn, scope, args){
20744         if(fn.apply(scope || this, args || [this]) !== false){
20745             if(this.items){
20746                 var cs = this.items.items;
20747                 for(var i = 0, len = cs.length; i < len; i++){
20748                     if(cs[i].cascade){
20749                         cs[i].cascade(fn, scope, args);
20750                     }else{
20751                         fn.apply(scope || cs[i], args || [cs[i]]);
20752                     }
20753                 }
20754             }
20755         }
20756         return this;
20757     },
20758
20759     /**
20760      * Find a component under this container at any level by id
20761      * @param {String} id
20762      * @deprecated Fairly useless method, since you can just use Ext.getCmp. Should be removed for 4.0
20763      * If you need to test if an id belongs to a container, you can use getCmp and findParent*.
20764      * @return Ext.Component
20765      */
20766     findById : function(id){
20767         var m = null, 
20768             ct = this;
20769         this.cascade(function(c){
20770             if(ct != c && c.id === id){
20771                 m = c;
20772                 return false;
20773             }
20774         });
20775         return m;
20776     },
20777
20778     /**
20779      * Find a component under this container at any level by xtype or class
20780      * @param {String/Class} xtype The xtype string for a component, or the class of the component directly
20781      * @param {Boolean} shallow (optional) False to check whether this Component is descended from the xtype (this is
20782      * the default), or true to check whether this Component is directly of the specified xtype.
20783      * @return {Array} Array of Ext.Components
20784      */
20785     findByType : function(xtype, shallow){
20786         return this.findBy(function(c){
20787             return c.isXType(xtype, shallow);
20788         });
20789     },
20790
20791     /**
20792      * Find a component under this container at any level by property
20793      * @param {String} prop
20794      * @param {String} value
20795      * @return {Array} Array of Ext.Components
20796      */
20797     find : function(prop, value){
20798         return this.findBy(function(c){
20799             return c[prop] === value;
20800         });
20801     },
20802
20803     /**
20804      * Find a component under this container at any level by a custom function. If the passed function returns
20805      * true, the component will be included in the results. The passed function is called with the arguments (component, this container).
20806      * @param {Function} fn The function to call
20807      * @param {Object} scope (optional)
20808      * @return {Array} Array of Ext.Components
20809      */
20810     findBy : function(fn, scope){
20811         var m = [], ct = this;
20812         this.cascade(function(c){
20813             if(ct != c && fn.call(scope || c, c, ct) === true){
20814                 m.push(c);
20815             }
20816         });
20817         return m;
20818     },
20819
20820     /**
20821      * Get a component contained by this container (alias for items.get(key))
20822      * @param {String/Number} key The index or id of the component
20823      * @deprecated Should be removed in 4.0, since getComponent does the same thing.
20824      * @return {Ext.Component} Ext.Component
20825      */
20826     get : function(key){
20827         return this.getComponent(key);
20828     }
20829 });
20830
20831 Ext.Container.LAYOUTS = {};
20832 Ext.reg('container', Ext.Container);
20833 /**
20834  * @class Ext.layout.ContainerLayout
20835  * <p>This class is intended to be extended or created via the <tt><b>{@link Ext.Container#layout layout}</b></tt>
20836  * configuration property.  See <tt><b>{@link Ext.Container#layout}</b></tt> for additional details.</p>
20837  */
20838 Ext.layout.ContainerLayout = Ext.extend(Object, {
20839     /**
20840      * @cfg {String} extraCls
20841      * <p>An optional extra CSS class that will be added to the container. This can be useful for adding
20842      * customized styles to the container or any of its children using standard CSS rules. See
20843      * {@link Ext.Component}.{@link Ext.Component#ctCls ctCls} also.</p>
20844      * <p><b>Note</b>: <tt>extraCls</tt> defaults to <tt>''</tt> except for the following classes
20845      * which assign a value by default:
20846      * <div class="mdetail-params"><ul>
20847      * <li>{@link Ext.layout.AbsoluteLayout Absolute Layout} : <tt>'x-abs-layout-item'</tt></li>
20848      * <li>{@link Ext.layout.Box Box Layout} : <tt>'x-box-item'</tt></li>
20849      * <li>{@link Ext.layout.ColumnLayout Column Layout} : <tt>'x-column'</tt></li>
20850      * </ul></div>
20851      * To configure the above Classes with an extra CSS class append to the default.  For example,
20852      * for ColumnLayout:<pre><code>
20853      * extraCls: 'x-column custom-class'
20854      * </code></pre>
20855      * </p>
20856      */
20857     /**
20858      * @cfg {Boolean} renderHidden
20859      * True to hide each contained item on render (defaults to false).
20860      */
20861
20862     /**
20863      * A reference to the {@link Ext.Component} that is active.  For example, <pre><code>
20864      * if(myPanel.layout.activeItem.id == 'item-1') { ... }
20865      * </code></pre>
20866      * <tt>activeItem</tt> only applies to layout styles that can display items one at a time
20867      * (like {@link Ext.layout.AccordionLayout}, {@link Ext.layout.CardLayout}
20868      * and {@link Ext.layout.FitLayout}).  Read-only.  Related to {@link Ext.Container#activeItem}.
20869      * @type {Ext.Component}
20870      * @property activeItem
20871      */
20872
20873     // private
20874     monitorResize:false,
20875     // private
20876     activeItem : null,
20877
20878     constructor : function(config){
20879         this.id = Ext.id(null, 'ext-layout-');
20880         Ext.apply(this, config);
20881     },
20882
20883     type: 'container',
20884
20885     /* Workaround for how IE measures autoWidth elements.  It prefers bottom-up measurements
20886       whereas other browser prefer top-down.  We will hide all target child elements before we measure and
20887       put them back to get an accurate measurement.
20888     */
20889     IEMeasureHack : function(target, viewFlag) {
20890         var tChildren = target.dom.childNodes, tLen = tChildren.length, c, d = [], e, i, ret;
20891         for (i = 0 ; i < tLen ; i++) {
20892             c = tChildren[i];
20893             e = Ext.get(c);
20894             if (e) {
20895                 d[i] = e.getStyle('display');
20896                 e.setStyle({display: 'none'});
20897             }
20898         }
20899         ret = target ? target.getViewSize(viewFlag) : {};
20900         for (i = 0 ; i < tLen ; i++) {
20901             c = tChildren[i];
20902             e = Ext.get(c);
20903             if (e) {
20904                 e.setStyle({display: d[i]});
20905             }
20906         }
20907         return ret;
20908     },
20909
20910     // Placeholder for the derived layouts
20911     getLayoutTargetSize : Ext.EmptyFn,
20912
20913     // private
20914     layout : function(){
20915         var ct = this.container, target = ct.getLayoutTarget();
20916         if(!(this.hasLayout || Ext.isEmpty(this.targetCls))){
20917             target.addClass(this.targetCls);
20918         }
20919         this.onLayout(ct, target);
20920         ct.fireEvent('afterlayout', ct, this);
20921     },
20922
20923     // private
20924     onLayout : function(ct, target){
20925         this.renderAll(ct, target);
20926     },
20927
20928     // private
20929     isValidParent : function(c, target){
20930         return target && c.getPositionEl().dom.parentNode == (target.dom || target);
20931     },
20932
20933     // private
20934     renderAll : function(ct, target){
20935         var items = ct.items.items, i, c, len = items.length;
20936         for(i = 0; i < len; i++) {
20937             c = items[i];
20938             if(c && (!c.rendered || !this.isValidParent(c, target))){
20939                 this.renderItem(c, i, target);
20940             }
20941         }
20942     },
20943
20944     /**
20945      * @private
20946      * Renders the given Component into the target Element. If the Component is already rendered,
20947      * it is moved to the provided target instead.
20948      * @param {Ext.Component} c The Component to render
20949      * @param {Number} position The position within the target to render the item to
20950      * @param {Ext.Element} target The target Element
20951      */
20952     renderItem : function(c, position, target){
20953         if (c) {
20954             if (!c.rendered) {
20955                 c.render(target, position);
20956                 this.configureItem(c);
20957             } else if (!this.isValidParent(c, target)) {
20958                 if (Ext.isNumber(position)) {
20959                     position = target.dom.childNodes[position];
20960                 }
20961                 
20962                 target.dom.insertBefore(c.getPositionEl().dom, position || null);
20963                 c.container = target;
20964                 this.configureItem(c);
20965             }
20966         }
20967     },
20968
20969     // private.
20970     // Get all rendered items to lay out.
20971     getRenderedItems: function(ct){
20972         var t = ct.getLayoutTarget(), cti = ct.items.items, len = cti.length, i, c, items = [];
20973         for (i = 0; i < len; i++) {
20974             if((c = cti[i]).rendered && this.isValidParent(c, t) && c.shouldLayout !== false){
20975                 items.push(c);
20976             }
20977         };
20978         return items;
20979     },
20980
20981     /**
20982      * @private
20983      * Applies extraCls and hides the item if renderHidden is true
20984      */
20985     configureItem: function(c){
20986         if (this.extraCls) {
20987             var t = c.getPositionEl ? c.getPositionEl() : c;
20988             t.addClass(this.extraCls);
20989         }
20990         
20991         // If we are forcing a layout, do so *before* we hide so elements have height/width
20992         if (c.doLayout && this.forceLayout) {
20993             c.doLayout();
20994         }
20995         if (this.renderHidden && c != this.activeItem) {
20996             c.hide();
20997         }
20998     },
20999
21000     onRemove: function(c){
21001         if(this.activeItem == c){
21002             delete this.activeItem;
21003         }
21004         if(c.rendered && this.extraCls){
21005             var t = c.getPositionEl ? c.getPositionEl() : c;
21006             t.removeClass(this.extraCls);
21007         }
21008     },
21009
21010     afterRemove: function(c){
21011         if(c.removeRestore){
21012             c.removeMode = 'container';
21013             delete c.removeRestore;
21014         }
21015     },
21016
21017     // private
21018     onResize: function(){
21019         var ct = this.container,
21020             b;
21021         if(ct.collapsed){
21022             return;
21023         }
21024         if(b = ct.bufferResize && ct.shouldBufferLayout()){
21025             if(!this.resizeTask){
21026                 this.resizeTask = new Ext.util.DelayedTask(this.runLayout, this);
21027                 this.resizeBuffer = Ext.isNumber(b) ? b : 50;
21028             }
21029             ct.layoutPending = true;
21030             this.resizeTask.delay(this.resizeBuffer);
21031         }else{
21032             this.runLayout();
21033         }
21034     },
21035
21036     runLayout: function(){
21037         var ct = this.container;
21038         this.layout();
21039         ct.onLayout();
21040         delete ct.layoutPending;
21041     },
21042
21043     // private
21044     setContainer : function(ct){
21045         /**
21046          * This monitorResize flag will be renamed soon as to avoid confusion
21047          * with the Container version which hooks onWindowResize to doLayout
21048          *
21049          * monitorResize flag in this context attaches the resize event between
21050          * a container and it's layout
21051          */
21052         if(this.monitorResize && ct != this.container){
21053             var old = this.container;
21054             if(old){
21055                 old.un(old.resizeEvent, this.onResize, this);
21056             }
21057             if(ct){
21058                 ct.on(ct.resizeEvent, this.onResize, this);
21059             }
21060         }
21061         this.container = ct;
21062     },
21063
21064     /**
21065      * Parses a number or string representing margin sizes into an object. Supports CSS-style margin declarations
21066      * (e.g. 10, "10", "10 10", "10 10 10" and "10 10 10 10" are all valid options and would return the same result)
21067      * @param {Number|String} v The encoded margins
21068      * @return {Object} An object with margin sizes for top, right, bottom and left
21069      */
21070     parseMargins : function(v){
21071         if (Ext.isNumber(v)) {
21072             v = v.toString();
21073         }
21074         var ms  = v.split(' '),
21075             len = ms.length;
21076             
21077         if (len == 1) {
21078             ms[1] = ms[2] = ms[3] = ms[0];
21079         } else if(len == 2) {
21080             ms[2] = ms[0];
21081             ms[3] = ms[1];
21082         } else if(len == 3) {
21083             ms[3] = ms[1];
21084         }
21085         
21086         return {
21087             top   :parseInt(ms[0], 10) || 0,
21088             right :parseInt(ms[1], 10) || 0,
21089             bottom:parseInt(ms[2], 10) || 0,
21090             left  :parseInt(ms[3], 10) || 0
21091         };
21092     },
21093
21094     /**
21095      * The {@link Ext.Template Ext.Template} used by Field rendering layout classes (such as
21096      * {@link Ext.layout.FormLayout}) to create the DOM structure of a fully wrapped,
21097      * labeled and styled form Field. A default Template is supplied, but this may be
21098      * overriden to create custom field structures. The template processes values returned from
21099      * {@link Ext.layout.FormLayout#getTemplateArgs}.
21100      * @property fieldTpl
21101      * @type Ext.Template
21102      */
21103     fieldTpl: (function() {
21104         var t = new Ext.Template(
21105             '<div class="x-form-item {itemCls}" tabIndex="-1">',
21106                 '<label for="{id}" style="{labelStyle}" class="x-form-item-label">{label}{labelSeparator}</label>',
21107                 '<div class="x-form-element" id="x-form-el-{id}" style="{elementStyle}">',
21108                 '</div><div class="{clearCls}"></div>',
21109             '</div>'
21110         );
21111         t.disableFormats = true;
21112         return t.compile();
21113     })(),
21114
21115     /*
21116      * Destroys this layout. This is a template method that is empty by default, but should be implemented
21117      * by subclasses that require explicit destruction to purge event handlers or remove DOM nodes.
21118      * @protected
21119      */
21120     destroy : function(){
21121         // Stop any buffered layout tasks
21122         if(this.resizeTask && this.resizeTask.cancel){
21123             this.resizeTask.cancel();
21124         }
21125         if(this.container) {
21126             this.container.un(this.container.resizeEvent, this.onResize, this);
21127         }
21128         if(!Ext.isEmpty(this.targetCls)){
21129             var target = this.container.getLayoutTarget();
21130             if(target){
21131                 target.removeClass(this.targetCls);
21132             }
21133         }
21134     }
21135 });/**
21136  * @class Ext.layout.AutoLayout
21137  * <p>The AutoLayout is the default layout manager delegated by {@link Ext.Container} to
21138  * render any child Components when no <tt>{@link Ext.Container#layout layout}</tt> is configured into
21139  * a {@link Ext.Container Container}.</tt>.  AutoLayout provides only a passthrough of any layout calls
21140  * to any child containers.</p>
21141  */
21142 Ext.layout.AutoLayout = Ext.extend(Ext.layout.ContainerLayout, {
21143     type: 'auto',
21144
21145     monitorResize: true,
21146
21147     onLayout : function(ct, target){
21148         Ext.layout.AutoLayout.superclass.onLayout.call(this, ct, target);
21149         var cs = this.getRenderedItems(ct), len = cs.length, i, c;
21150         for(i = 0; i < len; i++){
21151             c = cs[i];
21152             if (c.doLayout){
21153                 // Shallow layout children
21154                 c.doLayout(true);
21155             }
21156         }
21157     }
21158 });
21159
21160 Ext.Container.LAYOUTS['auto'] = Ext.layout.AutoLayout;
21161 /**
21162  * @class Ext.layout.FitLayout
21163  * @extends Ext.layout.ContainerLayout
21164  * <p>This is a base class for layouts that contain <b>a single item</b> that automatically expands to fill the layout's
21165  * container.  This class is intended to be extended or created via the <tt>layout:'fit'</tt> {@link Ext.Container#layout}
21166  * config, and should generally not need to be created directly via the new keyword.</p>
21167  * <p>FitLayout does not have any direct config options (other than inherited ones).  To fit a panel to a container
21168  * using FitLayout, simply set layout:'fit' on the container and add a single panel to it.  If the container has
21169  * multiple panels, only the first one will be displayed.  Example usage:</p>
21170  * <pre><code>
21171 var p = new Ext.Panel({
21172     title: 'Fit Layout',
21173     layout:'fit',
21174     items: {
21175         title: 'Inner Panel',
21176         html: '&lt;p&gt;This is the inner panel content&lt;/p&gt;',
21177         border: false
21178     }
21179 });
21180 </code></pre>
21181  */
21182 Ext.layout.FitLayout = Ext.extend(Ext.layout.ContainerLayout, {
21183     // private
21184     monitorResize:true,
21185
21186     type: 'fit',
21187
21188     getLayoutTargetSize : function() {
21189         var target = this.container.getLayoutTarget();
21190         if (!target) {
21191             return {};
21192         }
21193         // Style Sized (scrollbars not included)
21194         return target.getStyleSize();
21195     },
21196
21197     // private
21198     onLayout : function(ct, target){
21199         Ext.layout.FitLayout.superclass.onLayout.call(this, ct, target);
21200         if(!ct.collapsed){
21201             this.setItemSize(this.activeItem || ct.items.itemAt(0), this.getLayoutTargetSize());
21202         }
21203     },
21204
21205     // private
21206     setItemSize : function(item, size){
21207         if(item && size.height > 0){ // display none?
21208             item.setSize(size);
21209         }
21210     }
21211 });
21212 Ext.Container.LAYOUTS['fit'] = Ext.layout.FitLayout;/**
21213  * @class Ext.layout.CardLayout
21214  * @extends Ext.layout.FitLayout
21215  * <p>This layout manages multiple child Components, each fitted to the Container, where only a single child Component can be
21216  * visible at any given time.  This layout style is most commonly used for wizards, tab implementations, etc.
21217  * This class is intended to be extended or created via the layout:'card' {@link Ext.Container#layout} config,
21218  * and should generally not need to be created directly via the new keyword.</p>
21219  * <p>The CardLayout's focal method is {@link #setActiveItem}.  Since only one panel is displayed at a time,
21220  * the only way to move from one Component to the next is by calling setActiveItem, passing the id or index of
21221  * the next panel to display.  The layout itself does not provide a user interface for handling this navigation,
21222  * so that functionality must be provided by the developer.</p>
21223  * <p>In the following example, a simplistic wizard setup is demonstrated.  A button bar is added
21224  * to the footer of the containing panel to provide navigation buttons.  The buttons will be handled by a
21225  * common navigation routine -- for this example, the implementation of that routine has been ommitted since
21226  * it can be any type of custom logic.  Note that other uses of a CardLayout (like a tab control) would require a
21227  * completely different implementation.  For serious implementations, a better approach would be to extend
21228  * CardLayout to provide the custom functionality needed.  Example usage:</p>
21229  * <pre><code>
21230 var navHandler = function(direction){
21231     // This routine could contain business logic required to manage the navigation steps.
21232     // It would call setActiveItem as needed, manage navigation button state, handle any
21233     // branching logic that might be required, handle alternate actions like cancellation
21234     // or finalization, etc.  A complete wizard implementation could get pretty
21235     // sophisticated depending on the complexity required, and should probably be
21236     // done as a subclass of CardLayout in a real-world implementation.
21237 };
21238
21239 var card = new Ext.Panel({
21240     title: 'Example Wizard',
21241     layout:'card',
21242     activeItem: 0, // make sure the active item is set on the container config!
21243     bodyStyle: 'padding:15px',
21244     defaults: {
21245         // applied to each contained panel
21246         border:false
21247     },
21248     // just an example of one possible navigation scheme, using buttons
21249     bbar: [
21250         {
21251             id: 'move-prev',
21252             text: 'Back',
21253             handler: navHandler.createDelegate(this, [-1]),
21254             disabled: true
21255         },
21256         '->', // greedy spacer so that the buttons are aligned to each side
21257         {
21258             id: 'move-next',
21259             text: 'Next',
21260             handler: navHandler.createDelegate(this, [1])
21261         }
21262     ],
21263     // the panels (or "cards") within the layout
21264     items: [{
21265         id: 'card-0',
21266         html: '&lt;h1&gt;Welcome to the Wizard!&lt;/h1&gt;&lt;p&gt;Step 1 of 3&lt;/p&gt;'
21267     },{
21268         id: 'card-1',
21269         html: '&lt;p&gt;Step 2 of 3&lt;/p&gt;'
21270     },{
21271         id: 'card-2',
21272         html: '&lt;h1&gt;Congratulations!&lt;/h1&gt;&lt;p&gt;Step 3 of 3 - Complete&lt;/p&gt;'
21273     }]
21274 });
21275 </code></pre>
21276  */
21277 Ext.layout.CardLayout = Ext.extend(Ext.layout.FitLayout, {
21278     /**
21279      * @cfg {Boolean} deferredRender
21280      * True to render each contained item at the time it becomes active, false to render all contained items
21281      * as soon as the layout is rendered (defaults to false).  If there is a significant amount of content or
21282      * a lot of heavy controls being rendered into panels that are not displayed by default, setting this to
21283      * true might improve performance.
21284      */
21285     deferredRender : false,
21286
21287     /**
21288      * @cfg {Boolean} layoutOnCardChange
21289      * True to force a layout of the active item when the active card is changed. Defaults to false.
21290      */
21291     layoutOnCardChange : false,
21292
21293     /**
21294      * @cfg {Boolean} renderHidden @hide
21295      */
21296     // private
21297     renderHidden : true,
21298
21299     type: 'card',
21300
21301     /**
21302      * Sets the active (visible) item in the layout.
21303      * @param {String/Number} item The string component id or numeric index of the item to activate
21304      */
21305     setActiveItem : function(item){
21306         var ai = this.activeItem,
21307             ct = this.container;
21308         item = ct.getComponent(item);
21309
21310         // Is this a valid, different card?
21311         if(item && ai != item){
21312
21313             // Changing cards, hide the current one
21314             if(ai){
21315                 ai.hide();
21316                 if (ai.hidden !== true) {
21317                     return false;
21318                 }
21319                 ai.fireEvent('deactivate', ai);
21320             }
21321
21322             var layout = item.doLayout && (this.layoutOnCardChange || !item.rendered);
21323
21324             // Change activeItem reference
21325             this.activeItem = item;
21326
21327             // The container is about to get a recursive layout, remove any deferLayout reference
21328             // because it will trigger a redundant layout.
21329             delete item.deferLayout;
21330
21331             // Show the new component
21332             item.show();
21333
21334             this.layout();
21335
21336             if(layout){
21337                 item.doLayout();
21338             }
21339             item.fireEvent('activate', item);
21340         }
21341     },
21342
21343     // private
21344     renderAll : function(ct, target){
21345         if(this.deferredRender){
21346             this.renderItem(this.activeItem, undefined, target);
21347         }else{
21348             Ext.layout.CardLayout.superclass.renderAll.call(this, ct, target);
21349         }
21350     }
21351 });
21352 Ext.Container.LAYOUTS['card'] = Ext.layout.CardLayout;
21353 /**
21354  * @class Ext.layout.AnchorLayout
21355  * @extends Ext.layout.ContainerLayout
21356  * <p>This is a layout that enables anchoring of contained elements relative to the container's dimensions.
21357  * If the container is resized, all anchored items are automatically rerendered according to their
21358  * <b><tt>{@link #anchor}</tt></b> rules.</p>
21359  * <p>This class is intended to be extended or created via the layout:'anchor' {@link Ext.Container#layout}
21360  * config, and should generally not need to be created directly via the new keyword.</p>
21361  * <p>AnchorLayout does not have any direct config options (other than inherited ones). By default,
21362  * AnchorLayout will calculate anchor measurements based on the size of the container itself. However, the
21363  * container using the AnchorLayout can supply an anchoring-specific config property of <b>anchorSize</b>.
21364  * If anchorSize is specifed, the layout will use it as a virtual container for the purposes of calculating
21365  * anchor measurements based on it instead, allowing the container to be sized independently of the anchoring
21366  * logic if necessary.  For example:</p>
21367  * <pre><code>
21368 var viewport = new Ext.Viewport({
21369     layout:'anchor',
21370     anchorSize: {width:800, height:600},
21371     items:[{
21372         title:'Item 1',
21373         html:'Content 1',
21374         width:800,
21375         anchor:'right 20%'
21376     },{
21377         title:'Item 2',
21378         html:'Content 2',
21379         width:300,
21380         anchor:'50% 30%'
21381     },{
21382         title:'Item 3',
21383         html:'Content 3',
21384         width:600,
21385         anchor:'-100 50%'
21386     }]
21387 });
21388  * </code></pre>
21389  */
21390 Ext.layout.AnchorLayout = Ext.extend(Ext.layout.ContainerLayout, {
21391     /**
21392      * @cfg {String} anchor
21393      * <p>This configuation option is to be applied to <b>child <tt>items</tt></b> of a container managed by
21394      * this layout (ie. configured with <tt>layout:'anchor'</tt>).</p><br/>
21395      *
21396      * <p>This value is what tells the layout how an item should be anchored to the container. <tt>items</tt>
21397      * added to an AnchorLayout accept an anchoring-specific config property of <b>anchor</b> which is a string
21398      * containing two values: the horizontal anchor value and the vertical anchor value (for example, '100% 50%').
21399      * The following types of anchor values are supported:<div class="mdetail-params"><ul>
21400      *
21401      * <li><b>Percentage</b> : Any value between 1 and 100, expressed as a percentage.<div class="sub-desc">
21402      * The first anchor is the percentage width that the item should take up within the container, and the
21403      * second is the percentage height.  For example:<pre><code>
21404 // two values specified
21405 anchor: '100% 50%' // render item complete width of the container and
21406                    // 1/2 height of the container
21407 // one value specified
21408 anchor: '100%'     // the width value; the height will default to auto
21409      * </code></pre></div></li>
21410      *
21411      * <li><b>Offsets</b> : Any positive or negative integer value.<div class="sub-desc">
21412      * This is a raw adjustment where the first anchor is the offset from the right edge of the container,
21413      * and the second is the offset from the bottom edge. For example:<pre><code>
21414 // two values specified
21415 anchor: '-50 -100' // render item the complete width of the container
21416                    // minus 50 pixels and
21417                    // the complete height minus 100 pixels.
21418 // one value specified
21419 anchor: '-50'      // anchor value is assumed to be the right offset value
21420                    // bottom offset will default to 0
21421      * </code></pre></div></li>
21422      *
21423      * <li><b>Sides</b> : Valid values are <tt>'right'</tt> (or <tt>'r'</tt>) and <tt>'bottom'</tt>
21424      * (or <tt>'b'</tt>).<div class="sub-desc">
21425      * Either the container must have a fixed size or an anchorSize config value defined at render time in
21426      * order for these to have any effect.</div></li>
21427      *
21428      * <li><b>Mixed</b> : <div class="sub-desc">
21429      * Anchor values can also be mixed as needed.  For example, to render the width offset from the container
21430      * right edge by 50 pixels and 75% of the container's height use:
21431      * <pre><code>
21432 anchor: '-50 75%'
21433      * </code></pre></div></li>
21434      *
21435      *
21436      * </ul></div>
21437      */
21438
21439     // private
21440     monitorResize : true,
21441
21442     type : 'anchor',
21443
21444     /**
21445      * @cfg {String} defaultAnchor
21446      *
21447      * default anchor for all child container items applied if no anchor or specific width is set on the child item.  Defaults to '100%'.
21448      *
21449      */
21450     defaultAnchor : '100%',
21451
21452     parseAnchorRE : /^(r|right|b|bottom)$/i,
21453
21454
21455     getLayoutTargetSize : function() {
21456         var target = this.container.getLayoutTarget(), ret = {};
21457         if (target) {
21458             ret = target.getViewSize();
21459
21460             // IE in strict mode will return a width of 0 on the 1st pass of getViewSize.
21461             // Use getStyleSize to verify the 0 width, the adjustment pass will then work properly
21462             // with getViewSize
21463             if (Ext.isIE && Ext.isStrict && ret.width == 0){
21464                 ret =  target.getStyleSize();
21465             }
21466             ret.width -= target.getPadding('lr');
21467             ret.height -= target.getPadding('tb');
21468         }
21469         return ret;
21470     },
21471
21472     // private
21473     onLayout : function(container, target) {
21474         Ext.layout.AnchorLayout.superclass.onLayout.call(this, container, target);
21475
21476         var size = this.getLayoutTargetSize(),
21477             containerWidth = size.width,
21478             containerHeight = size.height,
21479             overflow = target.getStyle('overflow'),
21480             components = this.getRenderedItems(container),
21481             len = components.length,
21482             boxes = [],
21483             box,
21484             anchorWidth,
21485             anchorHeight,
21486             component,
21487             anchorSpec,
21488             calcWidth,
21489             calcHeight,
21490             anchorsArray,
21491             totalHeight = 0,
21492             i,
21493             el;
21494
21495         if(containerWidth < 20 && containerHeight < 20){
21496             return;
21497         }
21498
21499         // find the container anchoring size
21500         if(container.anchorSize) {
21501             if(typeof container.anchorSize == 'number') {
21502                 anchorWidth = container.anchorSize;
21503             } else {
21504                 anchorWidth = container.anchorSize.width;
21505                 anchorHeight = container.anchorSize.height;
21506             }
21507         } else {
21508             anchorWidth = container.initialConfig.width;
21509             anchorHeight = container.initialConfig.height;
21510         }
21511
21512         for(i = 0; i < len; i++) {
21513             component = components[i];
21514             el = component.getPositionEl();
21515
21516             // If a child container item has no anchor and no specific width, set the child to the default anchor size
21517             if (!component.anchor && component.items && !Ext.isNumber(component.width) && !(Ext.isIE6 && Ext.isStrict)){
21518                 component.anchor = this.defaultAnchor;
21519             }
21520
21521             if(component.anchor) {
21522                 anchorSpec = component.anchorSpec;
21523                 // cache all anchor values
21524                 if(!anchorSpec){
21525                     anchorsArray = component.anchor.split(' ');
21526                     component.anchorSpec = anchorSpec = {
21527                         right: this.parseAnchor(anchorsArray[0], component.initialConfig.width, anchorWidth),
21528                         bottom: this.parseAnchor(anchorsArray[1], component.initialConfig.height, anchorHeight)
21529                     };
21530                 }
21531                 calcWidth = anchorSpec.right ? this.adjustWidthAnchor(anchorSpec.right(containerWidth) - el.getMargins('lr'), component) : undefined;
21532                 calcHeight = anchorSpec.bottom ? this.adjustHeightAnchor(anchorSpec.bottom(containerHeight) - el.getMargins('tb'), component) : undefined;
21533
21534                 if(calcWidth || calcHeight) {
21535                     boxes.push({
21536                         component: component,
21537                         width: calcWidth || undefined,
21538                         height: calcHeight || undefined
21539                     });
21540                 }
21541             }
21542         }
21543         for (i = 0, len = boxes.length; i < len; i++) {
21544             box = boxes[i];
21545             box.component.setSize(box.width, box.height);
21546         }
21547
21548         if (overflow && overflow != 'hidden' && !this.adjustmentPass) {
21549             var newTargetSize = this.getLayoutTargetSize();
21550             if (newTargetSize.width != size.width || newTargetSize.height != size.height){
21551                 this.adjustmentPass = true;
21552                 this.onLayout(container, target);
21553             }
21554         }
21555
21556         delete this.adjustmentPass;
21557     },
21558
21559     // private
21560     parseAnchor : function(a, start, cstart) {
21561         if (a && a != 'none') {
21562             var last;
21563             // standard anchor
21564             if (this.parseAnchorRE.test(a)) {
21565                 var diff = cstart - start;
21566                 return function(v){
21567                     if(v !== last){
21568                         last = v;
21569                         return v - diff;
21570                     }
21571                 };
21572             // percentage
21573             } else if(a.indexOf('%') != -1) {
21574                 var ratio = parseFloat(a.replace('%', ''))*.01;
21575                 return function(v){
21576                     if(v !== last){
21577                         last = v;
21578                         return Math.floor(v*ratio);
21579                     }
21580                 };
21581             // simple offset adjustment
21582             } else {
21583                 a = parseInt(a, 10);
21584                 if (!isNaN(a)) {
21585                     return function(v) {
21586                         if (v !== last) {
21587                             last = v;
21588                             return v + a;
21589                         }
21590                     };
21591                 }
21592             }
21593         }
21594         return false;
21595     },
21596
21597     // private
21598     adjustWidthAnchor : function(value, comp){
21599         return value;
21600     },
21601
21602     // private
21603     adjustHeightAnchor : function(value, comp){
21604         return value;
21605     }
21606
21607     /**
21608      * @property activeItem
21609      * @hide
21610      */
21611 });
21612 Ext.Container.LAYOUTS['anchor'] = Ext.layout.AnchorLayout;
21613 /**
21614  * @class Ext.layout.ColumnLayout
21615  * @extends Ext.layout.ContainerLayout
21616  * <p>This is the layout style of choice for creating structural layouts in a multi-column format where the width of
21617  * each column can be specified as a percentage or fixed width, but the height is allowed to vary based on the content.
21618  * This class is intended to be extended or created via the layout:'column' {@link Ext.Container#layout} config,
21619  * and should generally not need to be created directly via the new keyword.</p>
21620  * <p>ColumnLayout does not have any direct config options (other than inherited ones), but it does support a
21621  * specific config property of <b><tt>columnWidth</tt></b> that can be included in the config of any panel added to it.  The
21622  * layout will use the columnWidth (if present) or width of each panel during layout to determine how to size each panel.
21623  * If width or columnWidth is not specified for a given panel, its width will default to the panel's width (or auto).</p>
21624  * <p>The width property is always evaluated as pixels, and must be a number greater than or equal to 1.
21625  * The columnWidth property is always evaluated as a percentage, and must be a decimal value greater than 0 and
21626  * less than 1 (e.g., .25).</p>
21627  * <p>The basic rules for specifying column widths are pretty simple.  The logic makes two passes through the
21628  * set of contained panels.  During the first layout pass, all panels that either have a fixed width or none
21629  * specified (auto) are skipped, but their widths are subtracted from the overall container width.  During the second
21630  * pass, all panels with columnWidths are assigned pixel widths in proportion to their percentages based on
21631  * the total <b>remaining</b> container width.  In other words, percentage width panels are designed to fill the space
21632  * left over by all the fixed-width and/or auto-width panels.  Because of this, while you can specify any number of columns
21633  * with different percentages, the columnWidths must always add up to 1 (or 100%) when added together, otherwise your
21634  * layout may not render as expected.  Example usage:</p>
21635  * <pre><code>
21636 // All columns are percentages -- they must add up to 1
21637 var p = new Ext.Panel({
21638     title: 'Column Layout - Percentage Only',
21639     layout:'column',
21640     items: [{
21641         title: 'Column 1',
21642         columnWidth: .25
21643     },{
21644         title: 'Column 2',
21645         columnWidth: .6
21646     },{
21647         title: 'Column 3',
21648         columnWidth: .15
21649     }]
21650 });
21651
21652 // Mix of width and columnWidth -- all columnWidth values must add up
21653 // to 1. The first column will take up exactly 120px, and the last two
21654 // columns will fill the remaining container width.
21655 var p = new Ext.Panel({
21656     title: 'Column Layout - Mixed',
21657     layout:'column',
21658     items: [{
21659         title: 'Column 1',
21660         width: 120
21661     },{
21662         title: 'Column 2',
21663         columnWidth: .8
21664     },{
21665         title: 'Column 3',
21666         columnWidth: .2
21667     }]
21668 });
21669 </code></pre>
21670  */
21671 Ext.layout.ColumnLayout = Ext.extend(Ext.layout.ContainerLayout, {
21672     // private
21673     monitorResize:true,
21674
21675     type: 'column',
21676
21677     extraCls: 'x-column',
21678
21679     scrollOffset : 0,
21680
21681     // private
21682
21683     targetCls: 'x-column-layout-ct',
21684
21685     isValidParent : function(c, target){
21686         return this.innerCt && c.getPositionEl().dom.parentNode == this.innerCt.dom;
21687     },
21688
21689     getLayoutTargetSize : function() {
21690         var target = this.container.getLayoutTarget(), ret;
21691         if (target) {
21692             ret = target.getViewSize();
21693
21694             // IE in strict mode will return a width of 0 on the 1st pass of getViewSize.
21695             // Use getStyleSize to verify the 0 width, the adjustment pass will then work properly
21696             // with getViewSize
21697             if (Ext.isIE && Ext.isStrict && ret.width == 0){
21698                 ret =  target.getStyleSize();
21699             }
21700
21701             ret.width -= target.getPadding('lr');
21702             ret.height -= target.getPadding('tb');
21703         }
21704         return ret;
21705     },
21706
21707     renderAll : function(ct, target) {
21708         if(!this.innerCt){
21709             // the innerCt prevents wrapping and shuffling while
21710             // the container is resizing
21711             this.innerCt = target.createChild({cls:'x-column-inner'});
21712             this.innerCt.createChild({cls:'x-clear'});
21713         }
21714         Ext.layout.ColumnLayout.superclass.renderAll.call(this, ct, this.innerCt);
21715     },
21716
21717     // private
21718     onLayout : function(ct, target){
21719         var cs = ct.items.items,
21720             len = cs.length,
21721             c,
21722             i,
21723             m,
21724             margins = [];
21725
21726         this.renderAll(ct, target);
21727
21728         var size = this.getLayoutTargetSize();
21729
21730         if(size.width < 1 && size.height < 1){ // display none?
21731             return;
21732         }
21733
21734         var w = size.width - this.scrollOffset,
21735             h = size.height,
21736             pw = w;
21737
21738         this.innerCt.setWidth(w);
21739
21740         // some columns can be percentages while others are fixed
21741         // so we need to make 2 passes
21742
21743         for(i = 0; i < len; i++){
21744             c = cs[i];
21745             m = c.getPositionEl().getMargins('lr');
21746             margins[i] = m;
21747             if(!c.columnWidth){
21748                 pw -= (c.getWidth() + m);
21749             }
21750         }
21751
21752         pw = pw < 0 ? 0 : pw;
21753
21754         for(i = 0; i < len; i++){
21755             c = cs[i];
21756             m = margins[i];
21757             if(c.columnWidth){
21758                 c.setSize(Math.floor(c.columnWidth * pw) - m);
21759             }
21760         }
21761
21762         // Browsers differ as to when they account for scrollbars.  We need to re-measure to see if the scrollbar
21763         // spaces were accounted for properly.  If not, re-layout.
21764         if (Ext.isIE) {
21765             if (i = target.getStyle('overflow') && i != 'hidden' && !this.adjustmentPass) {
21766                 var ts = this.getLayoutTargetSize();
21767                 if (ts.width != size.width){
21768                     this.adjustmentPass = true;
21769                     this.onLayout(ct, target);
21770                 }
21771             }
21772         }
21773         delete this.adjustmentPass;
21774     }
21775
21776     /**
21777      * @property activeItem
21778      * @hide
21779      */
21780 });
21781
21782 Ext.Container.LAYOUTS['column'] = Ext.layout.ColumnLayout;
21783 /**
21784  * @class Ext.layout.BorderLayout
21785  * @extends Ext.layout.ContainerLayout
21786  * <p>This is a multi-pane, application-oriented UI layout style that supports multiple
21787  * nested panels, automatic {@link Ext.layout.BorderLayout.Region#split split} bars between
21788  * {@link Ext.layout.BorderLayout.Region#BorderLayout.Region regions} and built-in
21789  * {@link Ext.layout.BorderLayout.Region#collapsible expanding and collapsing} of regions.</p>
21790  * <p>This class is intended to be extended or created via the <tt>layout:'border'</tt>
21791  * {@link Ext.Container#layout} config, and should generally not need to be created directly
21792  * via the new keyword.</p>
21793  * <p>BorderLayout does not have any direct config options (other than inherited ones).
21794  * All configuration options available for customizing the BorderLayout are at the
21795  * {@link Ext.layout.BorderLayout.Region} and {@link Ext.layout.BorderLayout.SplitRegion}
21796  * levels.</p>
21797  * <p>Example usage:</p>
21798  * <pre><code>
21799 var myBorderPanel = new Ext.Panel({
21800     {@link Ext.Component#renderTo renderTo}: document.body,
21801     {@link Ext.BoxComponent#width width}: 700,
21802     {@link Ext.BoxComponent#height height}: 500,
21803     {@link Ext.Panel#title title}: 'Border Layout',
21804     {@link Ext.Container#layout layout}: 'border',
21805     {@link Ext.Container#items items}: [{
21806         {@link Ext.Panel#title title}: 'South Region is resizable',
21807         {@link Ext.layout.BorderLayout.Region#BorderLayout.Region region}: 'south',     // position for region
21808         {@link Ext.BoxComponent#height height}: 100,
21809         {@link Ext.layout.BorderLayout.Region#split split}: true,         // enable resizing
21810         {@link Ext.SplitBar#minSize minSize}: 75,         // defaults to {@link Ext.layout.BorderLayout.Region#minHeight 50}
21811         {@link Ext.SplitBar#maxSize maxSize}: 150,
21812         {@link Ext.layout.BorderLayout.Region#margins margins}: '0 5 5 5'
21813     },{
21814         // xtype: 'panel' implied by default
21815         {@link Ext.Panel#title title}: 'West Region is collapsible',
21816         {@link Ext.layout.BorderLayout.Region#BorderLayout.Region region}:'west',
21817         {@link Ext.layout.BorderLayout.Region#margins margins}: '5 0 0 5',
21818         {@link Ext.BoxComponent#width width}: 200,
21819         {@link Ext.layout.BorderLayout.Region#collapsible collapsible}: true,   // make collapsible
21820         {@link Ext.layout.BorderLayout.Region#cmargins cmargins}: '5 5 0 5', // adjust top margin when collapsed
21821         {@link Ext.Component#id id}: 'west-region-container',
21822         {@link Ext.Container#layout layout}: 'fit',
21823         {@link Ext.Panel#unstyled unstyled}: true
21824     },{
21825         {@link Ext.Panel#title title}: 'Center Region',
21826         {@link Ext.layout.BorderLayout.Region#BorderLayout.Region region}: 'center',     // center region is required, no width/height specified
21827         {@link Ext.Component#xtype xtype}: 'container',
21828         {@link Ext.Container#layout layout}: 'fit',
21829         {@link Ext.layout.BorderLayout.Region#margins margins}: '5 5 0 0'
21830     }]
21831 });
21832 </code></pre>
21833  * <p><b><u>Notes</u></b>:</p><div class="mdetail-params"><ul>
21834  * <li>Any container using the BorderLayout <b>must</b> have a child item with <tt>region:'center'</tt>.
21835  * The child item in the center region will always be resized to fill the remaining space not used by
21836  * the other regions in the layout.</li>
21837  * <li>Any child items with a region of <tt>west</tt> or <tt>east</tt> must have <tt>width</tt> defined
21838  * (an integer representing the number of pixels that the region should take up).</li>
21839  * <li>Any child items with a region of <tt>north</tt> or <tt>south</tt> must have <tt>height</tt> defined.</li>
21840  * <li>The regions of a BorderLayout are <b>fixed at render time</b> and thereafter, its child Components may not be removed or added</b>.  To add/remove
21841  * Components within a BorderLayout, have them wrapped by an additional Container which is directly
21842  * managed by the BorderLayout.  If the region is to be collapsible, the Container used directly
21843  * by the BorderLayout manager should be a Panel.  In the following example a Container (an Ext.Panel)
21844  * is added to the west region:
21845  * <div style="margin-left:16px"><pre><code>
21846 wrc = {@link Ext#getCmp Ext.getCmp}('west-region-container');
21847 wrc.{@link Ext.Panel#removeAll removeAll}();
21848 wrc.{@link Ext.Container#add add}({
21849     title: 'Added Panel',
21850     html: 'Some content'
21851 });
21852 wrc.{@link Ext.Container#doLayout doLayout}();
21853  * </code></pre></div>
21854  * </li>
21855  * <li> To reference a {@link Ext.layout.BorderLayout.Region Region}:
21856  * <div style="margin-left:16px"><pre><code>
21857 wr = myBorderPanel.layout.west;
21858  * </code></pre></div>
21859  * </li>
21860  * </ul></div>
21861  */
21862 Ext.layout.BorderLayout = Ext.extend(Ext.layout.ContainerLayout, {
21863     // private
21864     monitorResize:true,
21865     // private
21866     rendered : false,
21867
21868     type: 'border',
21869
21870     targetCls: 'x-border-layout-ct',
21871
21872     getLayoutTargetSize : function() {
21873         var target = this.container.getLayoutTarget();
21874         return target ? target.getViewSize() : {};
21875     },
21876
21877     // private
21878     onLayout : function(ct, target){
21879         var collapsed, i, c, pos, items = ct.items.items, len = items.length;
21880         if(!this.rendered){
21881             collapsed = [];
21882             for(i = 0; i < len; i++) {
21883                 c = items[i];
21884                 pos = c.region;
21885                 if(c.collapsed){
21886                     collapsed.push(c);
21887                 }
21888                 c.collapsed = false;
21889                 if(!c.rendered){
21890                     c.render(target, i);
21891                     c.getPositionEl().addClass('x-border-panel');
21892                 }
21893                 this[pos] = pos != 'center' && c.split ?
21894                     new Ext.layout.BorderLayout.SplitRegion(this, c.initialConfig, pos) :
21895                     new Ext.layout.BorderLayout.Region(this, c.initialConfig, pos);
21896                 this[pos].render(target, c);
21897             }
21898             this.rendered = true;
21899         }
21900
21901         var size = this.getLayoutTargetSize();
21902         if(size.width < 20 || size.height < 20){ // display none?
21903             if(collapsed){
21904                 this.restoreCollapsed = collapsed;
21905             }
21906             return;
21907         }else if(this.restoreCollapsed){
21908             collapsed = this.restoreCollapsed;
21909             delete this.restoreCollapsed;
21910         }
21911
21912         var w = size.width, h = size.height,
21913             centerW = w, centerH = h, centerY = 0, centerX = 0,
21914             n = this.north, s = this.south, west = this.west, e = this.east, c = this.center,
21915             b, m, totalWidth, totalHeight;
21916         if(!c && Ext.layout.BorderLayout.WARN !== false){
21917             throw 'No center region defined in BorderLayout ' + ct.id;
21918         }
21919
21920         if(n && n.isVisible()){
21921             b = n.getSize();
21922             m = n.getMargins();
21923             b.width = w - (m.left+m.right);
21924             b.x = m.left;
21925             b.y = m.top;
21926             centerY = b.height + b.y + m.bottom;
21927             centerH -= centerY;
21928             n.applyLayout(b);
21929         }
21930         if(s && s.isVisible()){
21931             b = s.getSize();
21932             m = s.getMargins();
21933             b.width = w - (m.left+m.right);
21934             b.x = m.left;
21935             totalHeight = (b.height + m.top + m.bottom);
21936             b.y = h - totalHeight + m.top;
21937             centerH -= totalHeight;
21938             s.applyLayout(b);
21939         }
21940         if(west && west.isVisible()){
21941             b = west.getSize();
21942             m = west.getMargins();
21943             b.height = centerH - (m.top+m.bottom);
21944             b.x = m.left;
21945             b.y = centerY + m.top;
21946             totalWidth = (b.width + m.left + m.right);
21947             centerX += totalWidth;
21948             centerW -= totalWidth;
21949             west.applyLayout(b);
21950         }
21951         if(e && e.isVisible()){
21952             b = e.getSize();
21953             m = e.getMargins();
21954             b.height = centerH - (m.top+m.bottom);
21955             totalWidth = (b.width + m.left + m.right);
21956             b.x = w - totalWidth + m.left;
21957             b.y = centerY + m.top;
21958             centerW -= totalWidth;
21959             e.applyLayout(b);
21960         }
21961         if(c){
21962             m = c.getMargins();
21963             var centerBox = {
21964                 x: centerX + m.left,
21965                 y: centerY + m.top,
21966                 width: centerW - (m.left+m.right),
21967                 height: centerH - (m.top+m.bottom)
21968             };
21969             c.applyLayout(centerBox);
21970         }
21971         if(collapsed){
21972             for(i = 0, len = collapsed.length; i < len; i++){
21973                 collapsed[i].collapse(false);
21974             }
21975         }
21976         if(Ext.isIE && Ext.isStrict){ // workaround IE strict repainting issue
21977             target.repaint();
21978         }
21979         // Putting a border layout into an overflowed container is NOT correct and will make a second layout pass necessary.
21980         if (i = target.getStyle('overflow') && i != 'hidden' && !this.adjustmentPass) {
21981             var ts = this.getLayoutTargetSize();
21982             if (ts.width != size.width || ts.height != size.height){
21983                 this.adjustmentPass = true;
21984                 this.onLayout(ct, target);
21985             }
21986         }
21987         delete this.adjustmentPass;
21988     },
21989
21990     destroy: function() {
21991         var r = ['north', 'south', 'east', 'west'], i, region;
21992         for (i = 0; i < r.length; i++) {
21993             region = this[r[i]];
21994             if(region){
21995                 if(region.destroy){
21996                     region.destroy();
21997                 }else if (region.split){
21998                     region.split.destroy(true);
21999                 }
22000             }
22001         }
22002         Ext.layout.BorderLayout.superclass.destroy.call(this);
22003     }
22004
22005     /**
22006      * @property activeItem
22007      * @hide
22008      */
22009 });
22010
22011 /**
22012  * @class Ext.layout.BorderLayout.Region
22013  * <p>This is a region of a {@link Ext.layout.BorderLayout BorderLayout} that acts as a subcontainer
22014  * within the layout.  Each region has its own {@link Ext.layout.ContainerLayout layout} that is
22015  * independent of other regions and the containing BorderLayout, and can be any of the
22016  * {@link Ext.layout.ContainerLayout valid Ext layout types}.</p>
22017  * <p>Region size is managed automatically and cannot be changed by the user -- for
22018  * {@link #split resizable regions}, see {@link Ext.layout.BorderLayout.SplitRegion}.</p>
22019  * @constructor
22020  * Create a new Region.
22021  * @param {Layout} layout The {@link Ext.layout.BorderLayout BorderLayout} instance that is managing this Region.
22022  * @param {Object} config The configuration options
22023  * @param {String} position The region position.  Valid values are: <tt>north</tt>, <tt>south</tt>,
22024  * <tt>east</tt>, <tt>west</tt> and <tt>center</tt>.  Every {@link Ext.layout.BorderLayout BorderLayout}
22025  * <b>must have a center region</b> for the primary content -- all other regions are optional.
22026  */
22027 Ext.layout.BorderLayout.Region = function(layout, config, pos){
22028     Ext.apply(this, config);
22029     this.layout = layout;
22030     this.position = pos;
22031     this.state = {};
22032     if(typeof this.margins == 'string'){
22033         this.margins = this.layout.parseMargins(this.margins);
22034     }
22035     this.margins = Ext.applyIf(this.margins || {}, this.defaultMargins);
22036     if(this.collapsible){
22037         if(typeof this.cmargins == 'string'){
22038             this.cmargins = this.layout.parseMargins(this.cmargins);
22039         }
22040         if(this.collapseMode == 'mini' && !this.cmargins){
22041             this.cmargins = {left:0,top:0,right:0,bottom:0};
22042         }else{
22043             this.cmargins = Ext.applyIf(this.cmargins || {},
22044                 pos == 'north' || pos == 'south' ? this.defaultNSCMargins : this.defaultEWCMargins);
22045         }
22046     }
22047 };
22048
22049 Ext.layout.BorderLayout.Region.prototype = {
22050     /**
22051      * @cfg {Boolean} animFloat
22052      * When a collapsed region's bar is clicked, the region's panel will be displayed as a floated
22053      * panel that will close again once the user mouses out of that panel (or clicks out if
22054      * <tt>{@link #autoHide} = false</tt>).  Setting <tt>{@link #animFloat} = false</tt> will
22055      * prevent the open and close of these floated panels from being animated (defaults to <tt>true</tt>).
22056      */
22057     /**
22058      * @cfg {Boolean} autoHide
22059      * When a collapsed region's bar is clicked, the region's panel will be displayed as a floated
22060      * panel.  If <tt>autoHide = true</tt>, the panel will automatically hide after the user mouses
22061      * out of the panel.  If <tt>autoHide = false</tt>, the panel will continue to display until the
22062      * user clicks outside of the panel (defaults to <tt>true</tt>).
22063      */
22064     /**
22065      * @cfg {String} collapseMode
22066      * <tt>collapseMode</tt> supports two configuration values:<div class="mdetail-params"><ul>
22067      * <li><b><tt>undefined</tt></b> (default)<div class="sub-desc">By default, {@link #collapsible}
22068      * regions are collapsed by clicking the expand/collapse tool button that renders into the region's
22069      * title bar.</div></li>
22070      * <li><b><tt>'mini'</tt></b><div class="sub-desc">Optionally, when <tt>collapseMode</tt> is set to
22071      * <tt>'mini'</tt> the region's split bar will also display a small collapse button in the center of
22072      * the bar. In <tt>'mini'</tt> mode the region will collapse to a thinner bar than in normal mode.
22073      * </div></li>
22074      * </ul></div></p>
22075      * <p><b>Note</b>: if a collapsible region does not have a title bar, then set <tt>collapseMode =
22076      * 'mini'</tt> and <tt>{@link #split} = true</tt> in order for the region to be {@link #collapsible}
22077      * by the user as the expand/collapse tool button (that would go in the title bar) will not be rendered.</p>
22078      * <p>See also <tt>{@link #cmargins}</tt>.</p>
22079      */
22080     /**
22081      * @cfg {Object} margins
22082      * An object containing margins to apply to the region when in the expanded state in the
22083      * format:<pre><code>
22084 {
22085     top: (top margin),
22086     right: (right margin),
22087     bottom: (bottom margin),
22088     left: (left margin)
22089 }</code></pre>
22090      * <p>May also be a string containing space-separated, numeric margin values. The order of the
22091      * sides associated with each value matches the way CSS processes margin values:</p>
22092      * <p><div class="mdetail-params"><ul>
22093      * <li>If there is only one value, it applies to all sides.</li>
22094      * <li>If there are two values, the top and bottom borders are set to the first value and the
22095      * right and left are set to the second.</li>
22096      * <li>If there are three values, the top is set to the first value, the left and right are set
22097      * to the second, and the bottom is set to the third.</li>
22098      * <li>If there are four values, they apply to the top, right, bottom, and left, respectively.</li>
22099      * </ul></div></p>
22100      * <p>Defaults to:</p><pre><code>
22101      * {top:0, right:0, bottom:0, left:0}
22102      * </code></pre>
22103      */
22104     /**
22105      * @cfg {Object} cmargins
22106      * An object containing margins to apply to the region when in the collapsed state in the
22107      * format:<pre><code>
22108 {
22109     top: (top margin),
22110     right: (right margin),
22111     bottom: (bottom margin),
22112     left: (left margin)
22113 }</code></pre>
22114      * <p>May also be a string containing space-separated, numeric margin values. The order of the
22115      * sides associated with each value matches the way CSS processes margin values.</p>
22116      * <p><ul>
22117      * <li>If there is only one value, it applies to all sides.</li>
22118      * <li>If there are two values, the top and bottom borders are set to the first value and the
22119      * right and left are set to the second.</li>
22120      * <li>If there are three values, the top is set to the first value, the left and right are set
22121      * to the second, and the bottom is set to the third.</li>
22122      * <li>If there are four values, they apply to the top, right, bottom, and left, respectively.</li>
22123      * </ul></p>
22124      */
22125     /**
22126      * @cfg {Boolean} collapsible
22127      * <p><tt>true</tt> to allow the user to collapse this region (defaults to <tt>false</tt>).  If
22128      * <tt>true</tt>, an expand/collapse tool button will automatically be rendered into the title
22129      * bar of the region, otherwise the button will not be shown.</p>
22130      * <p><b>Note</b>: that a title bar is required to display the collapse/expand toggle button -- if
22131      * no <tt>title</tt> is specified for the region's panel, the region will only be collapsible if
22132      * <tt>{@link #collapseMode} = 'mini'</tt> and <tt>{@link #split} = true</tt>.
22133      */
22134     collapsible : false,
22135     /**
22136      * @cfg {Boolean} split
22137      * <p><tt>true</tt> to create a {@link Ext.layout.BorderLayout.SplitRegion SplitRegion} and
22138      * display a 5px wide {@link Ext.SplitBar} between this region and its neighbor, allowing the user to
22139      * resize the regions dynamically.  Defaults to <tt>false</tt> creating a
22140      * {@link Ext.layout.BorderLayout.Region Region}.</p><br>
22141      * <p><b>Notes</b>:</p><div class="mdetail-params"><ul>
22142      * <li>this configuration option is ignored if <tt>region='center'</tt></li>
22143      * <li>when <tt>split == true</tt>, it is common to specify a
22144      * <tt>{@link Ext.SplitBar#minSize minSize}</tt> and <tt>{@link Ext.SplitBar#maxSize maxSize}</tt>
22145      * for the {@link Ext.BoxComponent BoxComponent} representing the region. These are not native
22146      * configs of {@link Ext.BoxComponent BoxComponent}, and are used only by this class.</li>
22147      * <li>if <tt>{@link #collapseMode} = 'mini'</tt> requires <tt>split = true</tt> to reserve space
22148      * for the collapse tool</tt></li>
22149      * </ul></div>
22150      */
22151     split:false,
22152     /**
22153      * @cfg {Boolean} floatable
22154      * <tt>true</tt> to allow clicking a collapsed region's bar to display the region's panel floated
22155      * above the layout, <tt>false</tt> to force the user to fully expand a collapsed region by
22156      * clicking the expand button to see it again (defaults to <tt>true</tt>).
22157      */
22158     floatable: true,
22159     /**
22160      * @cfg {Number} minWidth
22161      * <p>The minimum allowable width in pixels for this region (defaults to <tt>50</tt>).
22162      * <tt>maxWidth</tt> may also be specified.</p><br>
22163      * <p><b>Note</b>: setting the <tt>{@link Ext.SplitBar#minSize minSize}</tt> /
22164      * <tt>{@link Ext.SplitBar#maxSize maxSize}</tt> supersedes any specified
22165      * <tt>minWidth</tt> / <tt>maxWidth</tt>.</p>
22166      */
22167     minWidth:50,
22168     /**
22169      * @cfg {Number} minHeight
22170      * The minimum allowable height in pixels for this region (defaults to <tt>50</tt>)
22171      * <tt>maxHeight</tt> may also be specified.</p><br>
22172      * <p><b>Note</b>: setting the <tt>{@link Ext.SplitBar#minSize minSize}</tt> /
22173      * <tt>{@link Ext.SplitBar#maxSize maxSize}</tt> supersedes any specified
22174      * <tt>minHeight</tt> / <tt>maxHeight</tt>.</p>
22175      */
22176     minHeight:50,
22177
22178     // private
22179     defaultMargins : {left:0,top:0,right:0,bottom:0},
22180     // private
22181     defaultNSCMargins : {left:5,top:5,right:5,bottom:5},
22182     // private
22183     defaultEWCMargins : {left:5,top:0,right:5,bottom:0},
22184     floatingZIndex: 100,
22185
22186     /**
22187      * True if this region is collapsed. Read-only.
22188      * @type Boolean
22189      * @property
22190      */
22191     isCollapsed : false,
22192
22193     /**
22194      * This region's panel.  Read-only.
22195      * @type Ext.Panel
22196      * @property panel
22197      */
22198     /**
22199      * This region's layout.  Read-only.
22200      * @type Layout
22201      * @property layout
22202      */
22203     /**
22204      * This region's layout position (north, south, east, west or center).  Read-only.
22205      * @type String
22206      * @property position
22207      */
22208
22209     // private
22210     render : function(ct, p){
22211         this.panel = p;
22212         p.el.enableDisplayMode();
22213         this.targetEl = ct;
22214         this.el = p.el;
22215
22216         var gs = p.getState, ps = this.position;
22217         p.getState = function(){
22218             return Ext.apply(gs.call(p) || {}, this.state);
22219         }.createDelegate(this);
22220
22221         if(ps != 'center'){
22222             p.allowQueuedExpand = false;
22223             p.on({
22224                 beforecollapse: this.beforeCollapse,
22225                 collapse: this.onCollapse,
22226                 beforeexpand: this.beforeExpand,
22227                 expand: this.onExpand,
22228                 hide: this.onHide,
22229                 show: this.onShow,
22230                 scope: this
22231             });
22232             if(this.collapsible || this.floatable){
22233                 p.collapseEl = 'el';
22234                 p.slideAnchor = this.getSlideAnchor();
22235             }
22236             if(p.tools && p.tools.toggle){
22237                 p.tools.toggle.addClass('x-tool-collapse-'+ps);
22238                 p.tools.toggle.addClassOnOver('x-tool-collapse-'+ps+'-over');
22239             }
22240         }
22241     },
22242
22243     // private
22244     getCollapsedEl : function(){
22245         if(!this.collapsedEl){
22246             if(!this.toolTemplate){
22247                 var tt = new Ext.Template(
22248                      '<div class="x-tool x-tool-{id}">&#160;</div>'
22249                 );
22250                 tt.disableFormats = true;
22251                 tt.compile();
22252                 Ext.layout.BorderLayout.Region.prototype.toolTemplate = tt;
22253             }
22254             this.collapsedEl = this.targetEl.createChild({
22255                 cls: "x-layout-collapsed x-layout-collapsed-"+this.position,
22256                 id: this.panel.id + '-xcollapsed'
22257             });
22258             this.collapsedEl.enableDisplayMode('block');
22259
22260             if(this.collapseMode == 'mini'){
22261                 this.collapsedEl.addClass('x-layout-cmini-'+this.position);
22262                 this.miniCollapsedEl = this.collapsedEl.createChild({
22263                     cls: "x-layout-mini x-layout-mini-"+this.position, html: "&#160;"
22264                 });
22265                 this.miniCollapsedEl.addClassOnOver('x-layout-mini-over');
22266                 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
22267                 this.collapsedEl.on('click', this.onExpandClick, this, {stopEvent:true});
22268             }else {
22269                 if(this.collapsible !== false && !this.hideCollapseTool) {
22270                     var t = this.expandToolEl = this.toolTemplate.append(
22271                             this.collapsedEl.dom,
22272                             {id:'expand-'+this.position}, true);
22273                     t.addClassOnOver('x-tool-expand-'+this.position+'-over');
22274                     t.on('click', this.onExpandClick, this, {stopEvent:true});
22275                 }
22276                 if(this.floatable !== false || this.titleCollapse){
22277                    this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
22278                    this.collapsedEl.on("click", this[this.floatable ? 'collapseClick' : 'onExpandClick'], this);
22279                 }
22280             }
22281         }
22282         return this.collapsedEl;
22283     },
22284
22285     // private
22286     onExpandClick : function(e){
22287         if(this.isSlid){
22288             this.panel.expand(false);
22289         }else{
22290             this.panel.expand();
22291         }
22292     },
22293
22294     // private
22295     onCollapseClick : function(e){
22296         this.panel.collapse();
22297     },
22298
22299     // private
22300     beforeCollapse : function(p, animate){
22301         this.lastAnim = animate;
22302         if(this.splitEl){
22303             this.splitEl.hide();
22304         }
22305         this.getCollapsedEl().show();
22306         var el = this.panel.getEl();
22307         this.originalZIndex = el.getStyle('z-index');
22308         el.setStyle('z-index', 100);
22309         this.isCollapsed = true;
22310         this.layout.layout();
22311     },
22312
22313     // private
22314     onCollapse : function(animate){
22315         this.panel.el.setStyle('z-index', 1);
22316         if(this.lastAnim === false || this.panel.animCollapse === false){
22317             this.getCollapsedEl().dom.style.visibility = 'visible';
22318         }else{
22319             this.getCollapsedEl().slideIn(this.panel.slideAnchor, {duration:.2});
22320         }
22321         this.state.collapsed = true;
22322         this.panel.saveState();
22323     },
22324
22325     // private
22326     beforeExpand : function(animate){
22327         if(this.isSlid){
22328             this.afterSlideIn();
22329         }
22330         var c = this.getCollapsedEl();
22331         this.el.show();
22332         if(this.position == 'east' || this.position == 'west'){
22333             this.panel.setSize(undefined, c.getHeight());
22334         }else{
22335             this.panel.setSize(c.getWidth(), undefined);
22336         }
22337         c.hide();
22338         c.dom.style.visibility = 'hidden';
22339         this.panel.el.setStyle('z-index', this.floatingZIndex);
22340     },
22341
22342     // private
22343     onExpand : function(){
22344         this.isCollapsed = false;
22345         if(this.splitEl){
22346             this.splitEl.show();
22347         }
22348         this.layout.layout();
22349         this.panel.el.setStyle('z-index', this.originalZIndex);
22350         this.state.collapsed = false;
22351         this.panel.saveState();
22352     },
22353
22354     // private
22355     collapseClick : function(e){
22356         if(this.isSlid){
22357            e.stopPropagation();
22358            this.slideIn();
22359         }else{
22360            e.stopPropagation();
22361            this.slideOut();
22362         }
22363     },
22364
22365     // private
22366     onHide : function(){
22367         if(this.isCollapsed){
22368             this.getCollapsedEl().hide();
22369         }else if(this.splitEl){
22370             this.splitEl.hide();
22371         }
22372     },
22373
22374     // private
22375     onShow : function(){
22376         if(this.isCollapsed){
22377             this.getCollapsedEl().show();
22378         }else if(this.splitEl){
22379             this.splitEl.show();
22380         }
22381     },
22382
22383     /**
22384      * True if this region is currently visible, else false.
22385      * @return {Boolean}
22386      */
22387     isVisible : function(){
22388         return !this.panel.hidden;
22389     },
22390
22391     /**
22392      * Returns the current margins for this region.  If the region is collapsed, the
22393      * {@link #cmargins} (collapsed margins) value will be returned, otherwise the
22394      * {@link #margins} value will be returned.
22395      * @return {Object} An object containing the element's margins: <tt>{left: (left
22396      * margin), top: (top margin), right: (right margin), bottom: (bottom margin)}</tt>
22397      */
22398     getMargins : function(){
22399         return this.isCollapsed && this.cmargins ? this.cmargins : this.margins;
22400     },
22401
22402     /**
22403      * Returns the current size of this region.  If the region is collapsed, the size of the
22404      * collapsedEl will be returned, otherwise the size of the region's panel will be returned.
22405      * @return {Object} An object containing the element's size: <tt>{width: (element width),
22406      * height: (element height)}</tt>
22407      */
22408     getSize : function(){
22409         return this.isCollapsed ? this.getCollapsedEl().getSize() : this.panel.getSize();
22410     },
22411
22412     /**
22413      * Sets the specified panel as the container element for this region.
22414      * @param {Ext.Panel} panel The new panel
22415      */
22416     setPanel : function(panel){
22417         this.panel = panel;
22418     },
22419
22420     /**
22421      * Returns the minimum allowable width for this region.
22422      * @return {Number} The minimum width
22423      */
22424     getMinWidth: function(){
22425         return this.minWidth;
22426     },
22427
22428     /**
22429      * Returns the minimum allowable height for this region.
22430      * @return {Number} The minimum height
22431      */
22432     getMinHeight: function(){
22433         return this.minHeight;
22434     },
22435
22436     // private
22437     applyLayoutCollapsed : function(box){
22438         var ce = this.getCollapsedEl();
22439         ce.setLeftTop(box.x, box.y);
22440         ce.setSize(box.width, box.height);
22441     },
22442
22443     // private
22444     applyLayout : function(box){
22445         if(this.isCollapsed){
22446             this.applyLayoutCollapsed(box);
22447         }else{
22448             this.panel.setPosition(box.x, box.y);
22449             this.panel.setSize(box.width, box.height);
22450         }
22451     },
22452
22453     // private
22454     beforeSlide: function(){
22455         this.panel.beforeEffect();
22456     },
22457
22458     // private
22459     afterSlide : function(){
22460         this.panel.afterEffect();
22461     },
22462
22463     // private
22464     initAutoHide : function(){
22465         if(this.autoHide !== false){
22466             if(!this.autoHideHd){
22467                 this.autoHideSlideTask = new Ext.util.DelayedTask(this.slideIn, this);
22468                 this.autoHideHd = {
22469                     "mouseout": function(e){
22470                         if(!e.within(this.el, true)){
22471                             this.autoHideSlideTask.delay(500);
22472                         }
22473                     },
22474                     "mouseover" : function(e){
22475                         this.autoHideSlideTask.cancel();
22476                     },
22477                     scope : this
22478                 };
22479             }
22480             this.el.on(this.autoHideHd);
22481             this.collapsedEl.on(this.autoHideHd);
22482         }
22483     },
22484
22485     // private
22486     clearAutoHide : function(){
22487         if(this.autoHide !== false){
22488             this.el.un("mouseout", this.autoHideHd.mouseout);
22489             this.el.un("mouseover", this.autoHideHd.mouseover);
22490             this.collapsedEl.un("mouseout", this.autoHideHd.mouseout);
22491             this.collapsedEl.un("mouseover", this.autoHideHd.mouseover);
22492         }
22493     },
22494
22495     // private
22496     clearMonitor : function(){
22497         Ext.getDoc().un("click", this.slideInIf, this);
22498     },
22499
22500     /**
22501      * If this Region is {@link #floatable}, this method slides this Region into full visibility <i>over the top
22502      * of the center Region</i> where it floats until either {@link #slideIn} is called, or other regions of the layout
22503      * are clicked, or the mouse exits the Region.
22504      */
22505     slideOut : function(){
22506         if(this.isSlid || this.el.hasActiveFx()){
22507             return;
22508         }
22509         this.isSlid = true;
22510         var ts = this.panel.tools, dh, pc;
22511         if(ts && ts.toggle){
22512             ts.toggle.hide();
22513         }
22514         this.el.show();
22515
22516         // Temporarily clear the collapsed flag so we can onResize the panel on the slide
22517         pc = this.panel.collapsed;
22518         this.panel.collapsed = false;
22519
22520         if(this.position == 'east' || this.position == 'west'){
22521             // Temporarily clear the deferHeight flag so we can size the height on the slide
22522             dh = this.panel.deferHeight;
22523             this.panel.deferHeight = false;
22524
22525             this.panel.setSize(undefined, this.collapsedEl.getHeight());
22526
22527             // Put the deferHeight flag back after setSize
22528             this.panel.deferHeight = dh;
22529         }else{
22530             this.panel.setSize(this.collapsedEl.getWidth(), undefined);
22531         }
22532
22533         // Put the collapsed flag back after onResize
22534         this.panel.collapsed = pc;
22535
22536         this.restoreLT = [this.el.dom.style.left, this.el.dom.style.top];
22537         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
22538         this.el.setStyle("z-index", this.floatingZIndex+2);
22539         this.panel.el.replaceClass('x-panel-collapsed', 'x-panel-floating');
22540         if(this.animFloat !== false){
22541             this.beforeSlide();
22542             this.el.slideIn(this.getSlideAnchor(), {
22543                 callback: function(){
22544                     this.afterSlide();
22545                     this.initAutoHide();
22546                     Ext.getDoc().on("click", this.slideInIf, this);
22547                 },
22548                 scope: this,
22549                 block: true
22550             });
22551         }else{
22552             this.initAutoHide();
22553              Ext.getDoc().on("click", this.slideInIf, this);
22554         }
22555     },
22556
22557     // private
22558     afterSlideIn : function(){
22559         this.clearAutoHide();
22560         this.isSlid = false;
22561         this.clearMonitor();
22562         this.el.setStyle("z-index", "");
22563         this.panel.el.replaceClass('x-panel-floating', 'x-panel-collapsed');
22564         this.el.dom.style.left = this.restoreLT[0];
22565         this.el.dom.style.top = this.restoreLT[1];
22566
22567         var ts = this.panel.tools;
22568         if(ts && ts.toggle){
22569             ts.toggle.show();
22570         }
22571     },
22572
22573     /**
22574      * If this Region is {@link #floatable}, and this Region has been slid into floating visibility, then this method slides
22575      * this region back into its collapsed state.
22576      */
22577     slideIn : function(cb){
22578         if(!this.isSlid || this.el.hasActiveFx()){
22579             Ext.callback(cb);
22580             return;
22581         }
22582         this.isSlid = false;
22583         if(this.animFloat !== false){
22584             this.beforeSlide();
22585             this.el.slideOut(this.getSlideAnchor(), {
22586                 callback: function(){
22587                     this.el.hide();
22588                     this.afterSlide();
22589                     this.afterSlideIn();
22590                     Ext.callback(cb);
22591                 },
22592                 scope: this,
22593                 block: true
22594             });
22595         }else{
22596             this.el.hide();
22597             this.afterSlideIn();
22598         }
22599     },
22600
22601     // private
22602     slideInIf : function(e){
22603         if(!e.within(this.el)){
22604             this.slideIn();
22605         }
22606     },
22607
22608     // private
22609     anchors : {
22610         "west" : "left",
22611         "east" : "right",
22612         "north" : "top",
22613         "south" : "bottom"
22614     },
22615
22616     // private
22617     sanchors : {
22618         "west" : "l",
22619         "east" : "r",
22620         "north" : "t",
22621         "south" : "b"
22622     },
22623
22624     // private
22625     canchors : {
22626         "west" : "tl-tr",
22627         "east" : "tr-tl",
22628         "north" : "tl-bl",
22629         "south" : "bl-tl"
22630     },
22631
22632     // private
22633     getAnchor : function(){
22634         return this.anchors[this.position];
22635     },
22636
22637     // private
22638     getCollapseAnchor : function(){
22639         return this.canchors[this.position];
22640     },
22641
22642     // private
22643     getSlideAnchor : function(){
22644         return this.sanchors[this.position];
22645     },
22646
22647     // private
22648     getAlignAdj : function(){
22649         var cm = this.cmargins;
22650         switch(this.position){
22651             case "west":
22652                 return [0, 0];
22653             break;
22654             case "east":
22655                 return [0, 0];
22656             break;
22657             case "north":
22658                 return [0, 0];
22659             break;
22660             case "south":
22661                 return [0, 0];
22662             break;
22663         }
22664     },
22665
22666     // private
22667     getExpandAdj : function(){
22668         var c = this.collapsedEl, cm = this.cmargins;
22669         switch(this.position){
22670             case "west":
22671                 return [-(cm.right+c.getWidth()+cm.left), 0];
22672             break;
22673             case "east":
22674                 return [cm.right+c.getWidth()+cm.left, 0];
22675             break;
22676             case "north":
22677                 return [0, -(cm.top+cm.bottom+c.getHeight())];
22678             break;
22679             case "south":
22680                 return [0, cm.top+cm.bottom+c.getHeight()];
22681             break;
22682         }
22683     },
22684
22685     destroy : function(){
22686         if (this.autoHideSlideTask && this.autoHideSlideTask.cancel){
22687             this.autoHideSlideTask.cancel();
22688         }
22689         Ext.destroyMembers(this, 'miniCollapsedEl', 'collapsedEl', 'expandToolEl');
22690     }
22691 };
22692
22693 /**
22694  * @class Ext.layout.BorderLayout.SplitRegion
22695  * @extends Ext.layout.BorderLayout.Region
22696  * <p>This is a specialized type of {@link Ext.layout.BorderLayout.Region BorderLayout region} that
22697  * has a built-in {@link Ext.SplitBar} for user resizing of regions.  The movement of the split bar
22698  * is configurable to move either {@link #tickSize smooth or incrementally}.</p>
22699  * @constructor
22700  * Create a new SplitRegion.
22701  * @param {Layout} layout The {@link Ext.layout.BorderLayout BorderLayout} instance that is managing this Region.
22702  * @param {Object} config The configuration options
22703  * @param {String} position The region position.  Valid values are: north, south, east, west and center.  Every
22704  * BorderLayout must have a center region for the primary content -- all other regions are optional.
22705  */
22706 Ext.layout.BorderLayout.SplitRegion = function(layout, config, pos){
22707     Ext.layout.BorderLayout.SplitRegion.superclass.constructor.call(this, layout, config, pos);
22708     // prevent switch
22709     this.applyLayout = this.applyFns[pos];
22710 };
22711
22712 Ext.extend(Ext.layout.BorderLayout.SplitRegion, Ext.layout.BorderLayout.Region, {
22713     /**
22714      * @cfg {Number} tickSize
22715      * The increment, in pixels by which to move this Region's {@link Ext.SplitBar SplitBar}.
22716      * By default, the {@link Ext.SplitBar SplitBar} moves smoothly.
22717      */
22718     /**
22719      * @cfg {String} splitTip
22720      * The tooltip to display when the user hovers over a
22721      * {@link Ext.layout.BorderLayout.Region#collapsible non-collapsible} region's split bar
22722      * (defaults to <tt>"Drag to resize."</tt>).  Only applies if
22723      * <tt>{@link #useSplitTips} = true</tt>.
22724      */
22725     splitTip : "Drag to resize.",
22726     /**
22727      * @cfg {String} collapsibleSplitTip
22728      * The tooltip to display when the user hovers over a
22729      * {@link Ext.layout.BorderLayout.Region#collapsible collapsible} region's split bar
22730      * (defaults to "Drag to resize. Double click to hide."). Only applies if
22731      * <tt>{@link #useSplitTips} = true</tt>.
22732      */
22733     collapsibleSplitTip : "Drag to resize. Double click to hide.",
22734     /**
22735      * @cfg {Boolean} useSplitTips
22736      * <tt>true</tt> to display a tooltip when the user hovers over a region's split bar
22737      * (defaults to <tt>false</tt>).  The tooltip text will be the value of either
22738      * <tt>{@link #splitTip}</tt> or <tt>{@link #collapsibleSplitTip}</tt> as appropriate.
22739      */
22740     useSplitTips : false,
22741
22742     // private
22743     splitSettings : {
22744         north : {
22745             orientation: Ext.SplitBar.VERTICAL,
22746             placement: Ext.SplitBar.TOP,
22747             maxFn : 'getVMaxSize',
22748             minProp: 'minHeight',
22749             maxProp: 'maxHeight'
22750         },
22751         south : {
22752             orientation: Ext.SplitBar.VERTICAL,
22753             placement: Ext.SplitBar.BOTTOM,
22754             maxFn : 'getVMaxSize',
22755             minProp: 'minHeight',
22756             maxProp: 'maxHeight'
22757         },
22758         east : {
22759             orientation: Ext.SplitBar.HORIZONTAL,
22760             placement: Ext.SplitBar.RIGHT,
22761             maxFn : 'getHMaxSize',
22762             minProp: 'minWidth',
22763             maxProp: 'maxWidth'
22764         },
22765         west : {
22766             orientation: Ext.SplitBar.HORIZONTAL,
22767             placement: Ext.SplitBar.LEFT,
22768             maxFn : 'getHMaxSize',
22769             minProp: 'minWidth',
22770             maxProp: 'maxWidth'
22771         }
22772     },
22773
22774     // private
22775     applyFns : {
22776         west : function(box){
22777             if(this.isCollapsed){
22778                 return this.applyLayoutCollapsed(box);
22779             }
22780             var sd = this.splitEl.dom, s = sd.style;
22781             this.panel.setPosition(box.x, box.y);
22782             var sw = sd.offsetWidth;
22783             s.left = (box.x+box.width-sw)+'px';
22784             s.top = (box.y)+'px';
22785             s.height = Math.max(0, box.height)+'px';
22786             this.panel.setSize(box.width-sw, box.height);
22787         },
22788         east : function(box){
22789             if(this.isCollapsed){
22790                 return this.applyLayoutCollapsed(box);
22791             }
22792             var sd = this.splitEl.dom, s = sd.style;
22793             var sw = sd.offsetWidth;
22794             this.panel.setPosition(box.x+sw, box.y);
22795             s.left = (box.x)+'px';
22796             s.top = (box.y)+'px';
22797             s.height = Math.max(0, box.height)+'px';
22798             this.panel.setSize(box.width-sw, box.height);
22799         },
22800         north : function(box){
22801             if(this.isCollapsed){
22802                 return this.applyLayoutCollapsed(box);
22803             }
22804             var sd = this.splitEl.dom, s = sd.style;
22805             var sh = sd.offsetHeight;
22806             this.panel.setPosition(box.x, box.y);
22807             s.left = (box.x)+'px';
22808             s.top = (box.y+box.height-sh)+'px';
22809             s.width = Math.max(0, box.width)+'px';
22810             this.panel.setSize(box.width, box.height-sh);
22811         },
22812         south : function(box){
22813             if(this.isCollapsed){
22814                 return this.applyLayoutCollapsed(box);
22815             }
22816             var sd = this.splitEl.dom, s = sd.style;
22817             var sh = sd.offsetHeight;
22818             this.panel.setPosition(box.x, box.y+sh);
22819             s.left = (box.x)+'px';
22820             s.top = (box.y)+'px';
22821             s.width = Math.max(0, box.width)+'px';
22822             this.panel.setSize(box.width, box.height-sh);
22823         }
22824     },
22825
22826     // private
22827     render : function(ct, p){
22828         Ext.layout.BorderLayout.SplitRegion.superclass.render.call(this, ct, p);
22829
22830         var ps = this.position;
22831
22832         this.splitEl = ct.createChild({
22833             cls: "x-layout-split x-layout-split-"+ps, html: "&#160;",
22834             id: this.panel.id + '-xsplit'
22835         });
22836
22837         if(this.collapseMode == 'mini'){
22838             this.miniSplitEl = this.splitEl.createChild({
22839                 cls: "x-layout-mini x-layout-mini-"+ps, html: "&#160;"
22840             });
22841             this.miniSplitEl.addClassOnOver('x-layout-mini-over');
22842             this.miniSplitEl.on('click', this.onCollapseClick, this, {stopEvent:true});
22843         }
22844
22845         var s = this.splitSettings[ps];
22846
22847         this.split = new Ext.SplitBar(this.splitEl.dom, p.el, s.orientation);
22848         this.split.tickSize = this.tickSize;
22849         this.split.placement = s.placement;
22850         this.split.getMaximumSize = this[s.maxFn].createDelegate(this);
22851         this.split.minSize = this.minSize || this[s.minProp];
22852         this.split.on("beforeapply", this.onSplitMove, this);
22853         this.split.useShim = this.useShim === true;
22854         this.maxSize = this.maxSize || this[s.maxProp];
22855
22856         if(p.hidden){
22857             this.splitEl.hide();
22858         }
22859
22860         if(this.useSplitTips){
22861             this.splitEl.dom.title = this.collapsible ? this.collapsibleSplitTip : this.splitTip;
22862         }
22863         if(this.collapsible){
22864             this.splitEl.on("dblclick", this.onCollapseClick,  this);
22865         }
22866     },
22867
22868     //docs inherit from superclass
22869     getSize : function(){
22870         if(this.isCollapsed){
22871             return this.collapsedEl.getSize();
22872         }
22873         var s = this.panel.getSize();
22874         if(this.position == 'north' || this.position == 'south'){
22875             s.height += this.splitEl.dom.offsetHeight;
22876         }else{
22877             s.width += this.splitEl.dom.offsetWidth;
22878         }
22879         return s;
22880     },
22881
22882     // private
22883     getHMaxSize : function(){
22884          var cmax = this.maxSize || 10000;
22885          var center = this.layout.center;
22886          return Math.min(cmax, (this.el.getWidth()+center.el.getWidth())-center.getMinWidth());
22887     },
22888
22889     // private
22890     getVMaxSize : function(){
22891         var cmax = this.maxSize || 10000;
22892         var center = this.layout.center;
22893         return Math.min(cmax, (this.el.getHeight()+center.el.getHeight())-center.getMinHeight());
22894     },
22895
22896     // private
22897     onSplitMove : function(split, newSize){
22898         var s = this.panel.getSize();
22899         this.lastSplitSize = newSize;
22900         if(this.position == 'north' || this.position == 'south'){
22901             this.panel.setSize(s.width, newSize);
22902             this.state.height = newSize;
22903         }else{
22904             this.panel.setSize(newSize, s.height);
22905             this.state.width = newSize;
22906         }
22907         this.layout.layout();
22908         this.panel.saveState();
22909         return false;
22910     },
22911
22912     /**
22913      * Returns a reference to the split bar in use by this region.
22914      * @return {Ext.SplitBar} The split bar
22915      */
22916     getSplitBar : function(){
22917         return this.split;
22918     },
22919
22920     // inherit docs
22921     destroy : function() {
22922         Ext.destroy(this.miniSplitEl, this.split, this.splitEl);
22923         Ext.layout.BorderLayout.SplitRegion.superclass.destroy.call(this);
22924     }
22925 });
22926
22927 Ext.Container.LAYOUTS['border'] = Ext.layout.BorderLayout;
22928 /**
22929  * @class Ext.layout.FormLayout
22930  * @extends Ext.layout.AnchorLayout
22931  * <p>This layout manager is specifically designed for rendering and managing child Components of
22932  * {@link Ext.form.FormPanel forms}. It is responsible for rendering the labels of
22933  * {@link Ext.form.Field Field}s.</p>
22934  *
22935  * <p>This layout manager is used when a Container is configured with the <tt>layout:'form'</tt>
22936  * {@link Ext.Container#layout layout} config option, and should generally not need to be created directly
22937  * via the new keyword. See <tt><b>{@link Ext.Container#layout}</b></tt> for additional details.</p>
22938  *
22939  * <p>In an application, it will usually be preferrable to use a {@link Ext.form.FormPanel FormPanel}
22940  * (which is configured with FormLayout as its layout class by default) since it also provides built-in
22941  * functionality for {@link Ext.form.BasicForm#doAction loading, validating and submitting} the form.</p>
22942  *
22943  * <p>A {@link Ext.Container Container} <i>using</i> the FormLayout layout manager (e.g.
22944  * {@link Ext.form.FormPanel} or specifying <tt>layout:'form'</tt>) can also accept the following
22945  * layout-specific config properties:<div class="mdetail-params"><ul>
22946  * <li><b><tt>{@link Ext.form.FormPanel#hideLabels hideLabels}</tt></b></li>
22947  * <li><b><tt>{@link Ext.form.FormPanel#labelAlign labelAlign}</tt></b></li>
22948  * <li><b><tt>{@link Ext.form.FormPanel#labelPad labelPad}</tt></b></li>
22949  * <li><b><tt>{@link Ext.form.FormPanel#labelSeparator labelSeparator}</tt></b></li>
22950  * <li><b><tt>{@link Ext.form.FormPanel#labelWidth labelWidth}</tt></b></li>
22951  * </ul></div></p>
22952  *
22953  * <p>Any Component (including Fields) managed by FormLayout accepts the following as a config option:
22954  * <div class="mdetail-params"><ul>
22955  * <li><b><tt>{@link Ext.Component#anchor anchor}</tt></b></li>
22956  * </ul></div></p>
22957  *
22958  * <p>Any Component managed by FormLayout may be rendered as a form field (with an associated label) by
22959  * configuring it with a non-null <b><tt>{@link Ext.Component#fieldLabel fieldLabel}</tt></b>. Components configured
22960  * in this way may be configured with the following options which affect the way the FormLayout renders them:
22961  * <div class="mdetail-params"><ul>
22962  * <li><b><tt>{@link Ext.Component#clearCls clearCls}</tt></b></li>
22963  * <li><b><tt>{@link Ext.Component#fieldLabel fieldLabel}</tt></b></li>
22964  * <li><b><tt>{@link Ext.Component#hideLabel hideLabel}</tt></b></li>
22965  * <li><b><tt>{@link Ext.Component#itemCls itemCls}</tt></b></li>
22966  * <li><b><tt>{@link Ext.Component#labelSeparator labelSeparator}</tt></b></li>
22967  * <li><b><tt>{@link Ext.Component#labelStyle labelStyle}</tt></b></li>
22968  * </ul></div></p>
22969  *
22970  * <p>Example usage:</p>
22971  * <pre><code>
22972 // Required if showing validation messages
22973 Ext.QuickTips.init();
22974
22975 // While you can create a basic Panel with layout:'form', practically
22976 // you should usually use a FormPanel to also get its form functionality
22977 // since it already creates a FormLayout internally.
22978 var form = new Ext.form.FormPanel({
22979     title: 'Form Layout',
22980     bodyStyle: 'padding:15px',
22981     width: 350,
22982     defaultType: 'textfield',
22983     defaults: {
22984         // applied to each contained item
22985         width: 230,
22986         msgTarget: 'side'
22987     },
22988     items: [{
22989             fieldLabel: 'First Name',
22990             name: 'first',
22991             allowBlank: false,
22992             {@link Ext.Component#labelSeparator labelSeparator}: ':' // override labelSeparator layout config
22993         },{
22994             fieldLabel: 'Last Name',
22995             name: 'last'
22996         },{
22997             fieldLabel: 'Email',
22998             name: 'email',
22999             vtype:'email'
23000         }, {
23001             xtype: 'textarea',
23002             hideLabel: true,     // override hideLabels layout config
23003             name: 'msg',
23004             anchor: '100% -53'
23005         }
23006     ],
23007     buttons: [
23008         {text: 'Save'},
23009         {text: 'Cancel'}
23010     ],
23011     layoutConfig: {
23012         {@link #labelSeparator}: '~' // superseded by assignment below
23013     },
23014     // config options applicable to container when layout='form':
23015     hideLabels: false,
23016     labelAlign: 'left',   // or 'right' or 'top'
23017     {@link Ext.form.FormPanel#labelSeparator labelSeparator}: '>>', // takes precedence over layoutConfig value
23018     labelWidth: 65,       // defaults to 100
23019     labelPad: 8           // defaults to 5, must specify labelWidth to be honored
23020 });
23021 </code></pre>
23022  */
23023 Ext.layout.FormLayout = Ext.extend(Ext.layout.AnchorLayout, {
23024
23025     /**
23026      * @cfg {String} labelSeparator
23027      * See {@link Ext.form.FormPanel}.{@link Ext.form.FormPanel#labelSeparator labelSeparator}.  Configuration
23028      * of this property at the <b>container</b> level takes precedence.
23029      */
23030     labelSeparator : ':',
23031
23032     /**
23033      * Read only. The CSS style specification string added to field labels in this layout if not
23034      * otherwise {@link Ext.Component#labelStyle specified by each contained field}.
23035      * @type String
23036      * @property labelStyle
23037      */
23038
23039     /**
23040      * @cfg {Boolean} trackLabels
23041      * True to show/hide the field label when the field is hidden. Defaults to <tt>true</tt>.
23042      */
23043     trackLabels: true,
23044
23045     type: 'form',
23046
23047     onRemove: function(c){
23048         Ext.layout.FormLayout.superclass.onRemove.call(this, c);
23049         if(this.trackLabels){
23050             c.un('show', this.onFieldShow, this);
23051             c.un('hide', this.onFieldHide, this);
23052         }
23053         // check for itemCt, since we may be removing a fieldset or something similar
23054         var el = c.getPositionEl(),
23055             ct = c.getItemCt && c.getItemCt();
23056         if (c.rendered && ct) {
23057             if (el && el.dom) {
23058                 el.insertAfter(ct);
23059             }
23060             Ext.destroy(ct);
23061             Ext.destroyMembers(c, 'label', 'itemCt');
23062             if (c.customItemCt) {
23063                 Ext.destroyMembers(c, 'getItemCt', 'customItemCt');
23064             }
23065         }
23066     },
23067
23068     // private
23069     setContainer : function(ct){
23070         Ext.layout.FormLayout.superclass.setContainer.call(this, ct);
23071         if(ct.labelAlign){
23072             ct.addClass('x-form-label-'+ct.labelAlign);
23073         }
23074
23075         if(ct.hideLabels){
23076             Ext.apply(this, {
23077                 labelStyle: 'display:none',
23078                 elementStyle: 'padding-left:0;',
23079                 labelAdjust: 0
23080             });
23081         }else{
23082             this.labelSeparator = Ext.isDefined(ct.labelSeparator) ? ct.labelSeparator : this.labelSeparator;
23083             ct.labelWidth = ct.labelWidth || 100;
23084             if(Ext.isNumber(ct.labelWidth)){
23085                 var pad = Ext.isNumber(ct.labelPad) ? ct.labelPad : 5;
23086                 Ext.apply(this, {
23087                     labelAdjust: ct.labelWidth + pad,
23088                     labelStyle: 'width:' + ct.labelWidth + 'px;',
23089                     elementStyle: 'padding-left:' + (ct.labelWidth + pad) + 'px'
23090                 });
23091             }
23092             if(ct.labelAlign == 'top'){
23093                 Ext.apply(this, {
23094                     labelStyle: 'width:auto;',
23095                     labelAdjust: 0,
23096                     elementStyle: 'padding-left:0;'
23097                 });
23098             }
23099         }
23100     },
23101
23102     // private
23103     isHide: function(c){
23104         return c.hideLabel || this.container.hideLabels;
23105     },
23106
23107     onFieldShow: function(c){
23108         c.getItemCt().removeClass('x-hide-' + c.hideMode);
23109
23110         // Composite fields will need to layout after the container is made visible
23111         if (c.isComposite) {
23112             c.doLayout();
23113         }
23114     },
23115
23116     onFieldHide: function(c){
23117         c.getItemCt().addClass('x-hide-' + c.hideMode);
23118     },
23119
23120     //private
23121     getLabelStyle: function(s){
23122         var ls = '', items = [this.labelStyle, s];
23123         for (var i = 0, len = items.length; i < len; ++i){
23124             if (items[i]){
23125                 ls += items[i];
23126                 if (ls.substr(-1, 1) != ';'){
23127                     ls += ';';
23128                 }
23129             }
23130         }
23131         return ls;
23132     },
23133
23134     /**
23135      * @cfg {Ext.Template} fieldTpl
23136      * A {@link Ext.Template#compile compile}d {@link Ext.Template} for rendering
23137      * the fully wrapped, labeled and styled form Field. Defaults to:</p><pre><code>
23138 new Ext.Template(
23139     &#39;&lt;div class="x-form-item {itemCls}" tabIndex="-1">&#39;,
23140         &#39;&lt;&#108;abel for="{id}" style="{labelStyle}" class="x-form-item-&#108;abel">{&#108;abel}{labelSeparator}&lt;/&#108;abel>&#39;,
23141         &#39;&lt;div class="x-form-element" id="x-form-el-{id}" style="{elementStyle}">&#39;,
23142         &#39;&lt;/div>&lt;div class="{clearCls}">&lt;/div>&#39;,
23143     '&lt;/div>'
23144 );
23145 </code></pre>
23146      * <p>This may be specified to produce a different DOM structure when rendering form Fields.</p>
23147      * <p>A description of the properties within the template follows:</p><div class="mdetail-params"><ul>
23148      * <li><b><tt>itemCls</tt></b> : String<div class="sub-desc">The CSS class applied to the outermost div wrapper
23149      * that contains this field label and field element (the default class is <tt>'x-form-item'</tt> and <tt>itemCls</tt>
23150      * will be added to that). If supplied, <tt>itemCls</tt> at the field level will override the default <tt>itemCls</tt>
23151      * supplied at the container level.</div></li>
23152      * <li><b><tt>id</tt></b> : String<div class="sub-desc">The id of the Field</div></li>
23153      * <li><b><tt>{@link #labelStyle}</tt></b> : String<div class="sub-desc">
23154      * A CSS style specification string to add to the field label for this field (defaults to <tt>''</tt> or the
23155      * {@link #labelStyle layout's value for <tt>labelStyle</tt>}).</div></li>
23156      * <li><b><tt>label</tt></b> : String<div class="sub-desc">The text to display as the label for this
23157      * field (defaults to <tt>''</tt>)</div></li>
23158      * <li><b><tt>{@link #labelSeparator}</tt></b> : String<div class="sub-desc">The separator to display after
23159      * the text of the label for this field (defaults to a colon <tt>':'</tt> or the
23160      * {@link #labelSeparator layout's value for labelSeparator}). To hide the separator use empty string ''.</div></li>
23161      * <li><b><tt>elementStyle</tt></b> : String<div class="sub-desc">The styles text for the input element's wrapper.</div></li>
23162      * <li><b><tt>clearCls</tt></b> : String<div class="sub-desc">The CSS class to apply to the special clearing div
23163      * rendered directly after each form field wrapper (defaults to <tt>'x-form-clear-left'</tt>)</div></li>
23164      * </ul></div>
23165      * <p>Also see <tt>{@link #getTemplateArgs}</tt></p>
23166      */
23167
23168     /**
23169      * @private
23170      *
23171      */
23172     renderItem : function(c, position, target){
23173         if(c && (c.isFormField || c.fieldLabel) && c.inputType != 'hidden'){
23174             var args = this.getTemplateArgs(c);
23175             if(Ext.isNumber(position)){
23176                 position = target.dom.childNodes[position] || null;
23177             }
23178             if(position){
23179                 c.itemCt = this.fieldTpl.insertBefore(position, args, true);
23180             }else{
23181                 c.itemCt = this.fieldTpl.append(target, args, true);
23182             }
23183             if(!c.getItemCt){
23184                 // Non form fields don't have getItemCt, apply it here
23185                 // This will get cleaned up in onRemove
23186                 Ext.apply(c, {
23187                     getItemCt: function(){
23188                         return c.itemCt;
23189                     },
23190                     customItemCt: true
23191                 });
23192             }
23193             c.label = c.getItemCt().child('label.x-form-item-label');
23194             if(!c.rendered){
23195                 c.render('x-form-el-' + c.id);
23196             }else if(!this.isValidParent(c, target)){
23197                 Ext.fly('x-form-el-' + c.id).appendChild(c.getPositionEl());
23198             }
23199             if(this.trackLabels){
23200                 if(c.hidden){
23201                     this.onFieldHide(c);
23202                 }
23203                 c.on({
23204                     scope: this,
23205                     show: this.onFieldShow,
23206                     hide: this.onFieldHide
23207                 });
23208             }
23209             this.configureItem(c);
23210         }else {
23211             Ext.layout.FormLayout.superclass.renderItem.apply(this, arguments);
23212         }
23213     },
23214
23215     /**
23216      * <p>Provides template arguments for rendering the fully wrapped, labeled and styled form Field.</p>
23217      * <p>This method returns an object hash containing properties used by the layout's {@link #fieldTpl}
23218      * to create a correctly wrapped, labeled and styled form Field. This may be overriden to
23219      * create custom layouts. The properties which must be returned are:</p><div class="mdetail-params"><ul>
23220      * <li><b><tt>itemCls</tt></b> : String<div class="sub-desc">The CSS class applied to the outermost div wrapper
23221      * that contains this field label and field element (the default class is <tt>'x-form-item'</tt> and <tt>itemCls</tt>
23222      * will be added to that). If supplied, <tt>itemCls</tt> at the field level will override the default <tt>itemCls</tt>
23223      * supplied at the container level.</div></li>
23224      * <li><b><tt>id</tt></b> : String<div class="sub-desc">The id of the Field</div></li>
23225      * <li><b><tt>{@link #labelStyle}</tt></b> : String<div class="sub-desc">
23226      * A CSS style specification string to add to the field label for this field (defaults to <tt>''</tt> or the
23227      * {@link #labelStyle layout's value for <tt>labelStyle</tt>}).</div></li>
23228      * <li><b><tt>label</tt></b> : String<div class="sub-desc">The text to display as the label for this
23229      * field (defaults to the field's configured fieldLabel property)</div></li>
23230      * <li><b><tt>{@link #labelSeparator}</tt></b> : String<div class="sub-desc">The separator to display after
23231      * the text of the label for this field (defaults to a colon <tt>':'</tt> or the
23232      * {@link #labelSeparator layout's value for labelSeparator}). To hide the separator use empty string ''.</div></li>
23233      * <li><b><tt>elementStyle</tt></b> : String<div class="sub-desc">The styles text for the input element's wrapper.</div></li>
23234      * <li><b><tt>clearCls</tt></b> : String<div class="sub-desc">The CSS class to apply to the special clearing div
23235      * rendered directly after each form field wrapper (defaults to <tt>'x-form-clear-left'</tt>)</div></li>
23236      * </ul></div>
23237      * @param (Ext.form.Field} field The {@link Ext.form.Field Field} being rendered.
23238      * @return {Object} An object hash containing the properties required to render the Field.
23239      */
23240     getTemplateArgs: function(field) {
23241         var noLabelSep = !field.fieldLabel || field.hideLabel;
23242
23243         return {
23244             id            : field.id,
23245             label         : field.fieldLabel,
23246             itemCls       : (field.itemCls || this.container.itemCls || '') + (field.hideLabel ? ' x-hide-label' : ''),
23247             clearCls      : field.clearCls || 'x-form-clear-left',
23248             labelStyle    : this.getLabelStyle(field.labelStyle),
23249             elementStyle  : this.elementStyle || '',
23250             labelSeparator: noLabelSep ? '' : (Ext.isDefined(field.labelSeparator) ? field.labelSeparator : this.labelSeparator)
23251         };
23252     },
23253
23254     // private
23255     adjustWidthAnchor: function(value, c){
23256         if(c.label && !this.isHide(c) && (this.container.labelAlign != 'top')){
23257             var adjust = Ext.isIE6 || (Ext.isIE && !Ext.isStrict);
23258             return value - this.labelAdjust + (adjust ? -3 : 0);
23259         }
23260         return value;
23261     },
23262
23263     adjustHeightAnchor : function(value, c){
23264         if(c.label && !this.isHide(c) && (this.container.labelAlign == 'top')){
23265             return value - c.label.getHeight();
23266         }
23267         return value;
23268     },
23269
23270     // private
23271     isValidParent : function(c, target){
23272         return target && this.container.getEl().contains(c.getPositionEl());
23273     }
23274
23275     /**
23276      * @property activeItem
23277      * @hide
23278      */
23279 });
23280
23281 Ext.Container.LAYOUTS['form'] = Ext.layout.FormLayout;
23282 /**
23283  * @class Ext.layout.AccordionLayout
23284  * @extends Ext.layout.FitLayout
23285  * <p>This is a layout that manages multiple Panels in an expandable accordion style such that only
23286  * <b>one Panel can be expanded at any given time</b>. Each Panel has built-in support for expanding and collapsing.</p>
23287  * <p>Note: Only Ext.Panels <b>and all subclasses of Ext.Panel</b> may be used in an accordion layout Container.</p>
23288  * <p>This class is intended to be extended or created via the <tt><b>{@link Ext.Container#layout layout}</b></tt>
23289  * configuration property.  See <tt><b>{@link Ext.Container#layout}</b></tt> for additional details.</p>
23290  * <p>Example usage:</p>
23291  * <pre><code>
23292 var accordion = new Ext.Panel({
23293     title: 'Accordion Layout',
23294     layout:'accordion',
23295     defaults: {
23296         // applied to each contained panel
23297         bodyStyle: 'padding:15px'
23298     },
23299     layoutConfig: {
23300         // layout-specific configs go here
23301         titleCollapse: false,
23302         animate: true,
23303         activeOnTop: true
23304     },
23305     items: [{
23306         title: 'Panel 1',
23307         html: '&lt;p&gt;Panel content!&lt;/p&gt;'
23308     },{
23309         title: 'Panel 2',
23310         html: '&lt;p&gt;Panel content!&lt;/p&gt;'
23311     },{
23312         title: 'Panel 3',
23313         html: '&lt;p&gt;Panel content!&lt;/p&gt;'
23314     }]
23315 });
23316 </code></pre>
23317  */
23318 Ext.layout.AccordionLayout = Ext.extend(Ext.layout.FitLayout, {
23319     /**
23320      * @cfg {Boolean} fill
23321      * True to adjust the active item's height to fill the available space in the container, false to use the
23322      * item's current height, or auto height if not explicitly set (defaults to true).
23323      */
23324     fill : true,
23325     /**
23326      * @cfg {Boolean} autoWidth
23327      * True to set each contained item's width to 'auto', false to use the item's current width (defaults to true).
23328      * Note that some components, in particular the {@link Ext.grid.GridPanel grid}, will not function properly within
23329      * layouts if they have auto width, so in such cases this config should be set to false.
23330      */
23331     autoWidth : true,
23332     /**
23333      * @cfg {Boolean} titleCollapse
23334      * True to allow expand/collapse of each contained panel by clicking anywhere on the title bar, false to allow
23335      * expand/collapse only when the toggle tool button is clicked (defaults to true).  When set to false,
23336      * {@link #hideCollapseTool} should be false also.
23337      */
23338     titleCollapse : true,
23339     /**
23340      * @cfg {Boolean} hideCollapseTool
23341      * True to hide the contained panels' collapse/expand toggle buttons, false to display them (defaults to false).
23342      * When set to true, {@link #titleCollapse} should be true also.
23343      */
23344     hideCollapseTool : false,
23345     /**
23346      * @cfg {Boolean} collapseFirst
23347      * True to make sure the collapse/expand toggle button always renders first (to the left of) any other tools
23348      * in the contained panels' title bars, false to render it last (defaults to false).
23349      */
23350     collapseFirst : false,
23351     /**
23352      * @cfg {Boolean} animate
23353      * True to slide the contained panels open and closed during expand/collapse using animation, false to open and
23354      * close directly with no animation (defaults to false).  Note: to defer to the specific config setting of each
23355      * contained panel for this property, set this to undefined at the layout level.
23356      */
23357     animate : false,
23358     /**
23359      * @cfg {Boolean} sequence
23360      * <b>Experimental</b>. If animate is set to true, this will result in each animation running in sequence.
23361      */
23362     sequence : false,
23363     /**
23364      * @cfg {Boolean} activeOnTop
23365      * True to swap the position of each panel as it is expanded so that it becomes the first item in the container,
23366      * false to keep the panels in the rendered order. <b>This is NOT compatible with "animate:true"</b> (defaults to false).
23367      */
23368     activeOnTop : false,
23369
23370     type: 'accordion',
23371
23372     renderItem : function(c){
23373         if(this.animate === false){
23374             c.animCollapse = false;
23375         }
23376         c.collapsible = true;
23377         if(this.autoWidth){
23378             c.autoWidth = true;
23379         }
23380         if(this.titleCollapse){
23381             c.titleCollapse = true;
23382         }
23383         if(this.hideCollapseTool){
23384             c.hideCollapseTool = true;
23385         }
23386         if(this.collapseFirst !== undefined){
23387             c.collapseFirst = this.collapseFirst;
23388         }
23389         if(!this.activeItem && !c.collapsed){
23390             this.setActiveItem(c, true);
23391         }else if(this.activeItem && this.activeItem != c){
23392             c.collapsed = true;
23393         }
23394         Ext.layout.AccordionLayout.superclass.renderItem.apply(this, arguments);
23395         c.header.addClass('x-accordion-hd');
23396         c.on('beforeexpand', this.beforeExpand, this);
23397     },
23398
23399     onRemove: function(c){
23400         Ext.layout.AccordionLayout.superclass.onRemove.call(this, c);
23401         if(c.rendered){
23402             c.header.removeClass('x-accordion-hd');
23403         }
23404         c.un('beforeexpand', this.beforeExpand, this);
23405     },
23406
23407     // private
23408     beforeExpand : function(p, anim){
23409         var ai = this.activeItem;
23410         if(ai){
23411             if(this.sequence){
23412                 delete this.activeItem;
23413                 if (!ai.collapsed){
23414                     ai.collapse({callback:function(){
23415                         p.expand(anim || true);
23416                     }, scope: this});
23417                     return false;
23418                 }
23419             }else{
23420                 ai.collapse(this.animate);
23421             }
23422         }
23423         this.setActive(p);
23424         if(this.activeOnTop){
23425             p.el.dom.parentNode.insertBefore(p.el.dom, p.el.dom.parentNode.firstChild);
23426         }
23427         // Items have been hidden an possibly rearranged, we need to get the container size again.
23428         this.layout();
23429     },
23430
23431     // private
23432     setItemSize : function(item, size){
23433         if(this.fill && item){
23434             var hh = 0, i, ct = this.getRenderedItems(this.container), len = ct.length, p;
23435             // Add up all the header heights
23436             for (i = 0; i < len; i++) {
23437                 if((p = ct[i]) != item && !p.hidden){
23438                     hh += p.header.getHeight();
23439                 }
23440             };
23441             // Subtract the header heights from the container size
23442             size.height -= hh;
23443             // Call setSize on the container to set the correct height.  For Panels, deferedHeight
23444             // will simply store this size for when the expansion is done.
23445             item.setSize(size);
23446         }
23447     },
23448
23449     /**
23450      * Sets the active (expanded) item in the layout.
23451      * @param {String/Number} item The string component id or numeric index of the item to activate
23452      */
23453     setActiveItem : function(item){
23454         this.setActive(item, true);
23455     },
23456
23457     // private
23458     setActive : function(item, expand){
23459         var ai = this.activeItem;
23460         item = this.container.getComponent(item);
23461         if(ai != item){
23462             if(item.rendered && item.collapsed && expand){
23463                 item.expand();
23464             }else{
23465                 if(ai){
23466                    ai.fireEvent('deactivate', ai);
23467                 }
23468                 this.activeItem = item;
23469                 item.fireEvent('activate', item);
23470             }
23471         }
23472     }
23473 });
23474 Ext.Container.LAYOUTS.accordion = Ext.layout.AccordionLayout;
23475
23476 //backwards compat
23477 Ext.layout.Accordion = Ext.layout.AccordionLayout;/**
23478  * @class Ext.layout.TableLayout
23479  * @extends Ext.layout.ContainerLayout
23480  * <p>This layout allows you to easily render content into an HTML table.  The total number of columns can be
23481  * specified, and rowspan and colspan can be used to create complex layouts within the table.
23482  * This class is intended to be extended or created via the layout:'table' {@link Ext.Container#layout} config,
23483  * and should generally not need to be created directly via the new keyword.</p>
23484  * <p>Note that when creating a layout via config, the layout-specific config properties must be passed in via
23485  * the {@link Ext.Container#layoutConfig} object which will then be applied internally to the layout.  In the
23486  * case of TableLayout, the only valid layout config property is {@link #columns}.  However, the items added to a
23487  * TableLayout can supply the following table-specific config properties:</p>
23488  * <ul>
23489  * <li><b>rowspan</b> Applied to the table cell containing the item.</li>
23490  * <li><b>colspan</b> Applied to the table cell containing the item.</li>
23491  * <li><b>cellId</b> An id applied to the table cell containing the item.</li>
23492  * <li><b>cellCls</b> A CSS class name added to the table cell containing the item.</li>
23493  * </ul>
23494  * <p>The basic concept of building up a TableLayout is conceptually very similar to building up a standard
23495  * HTML table.  You simply add each panel (or "cell") that you want to include along with any span attributes
23496  * specified as the special config properties of rowspan and colspan which work exactly like their HTML counterparts.
23497  * Rather than explicitly creating and nesting rows and columns as you would in HTML, you simply specify the
23498  * total column count in the layoutConfig and start adding panels in their natural order from left to right,
23499  * top to bottom.  The layout will automatically figure out, based on the column count, rowspans and colspans,
23500  * how to position each panel within the table.  Just like with HTML tables, your rowspans and colspans must add
23501  * up correctly in your overall layout or you'll end up with missing and/or extra cells!  Example usage:</p>
23502  * <pre><code>
23503 // This code will generate a layout table that is 3 columns by 2 rows
23504 // with some spanning included.  The basic layout will be:
23505 // +--------+-----------------+
23506 // |   A    |   B             |
23507 // |        |--------+--------|
23508 // |        |   C    |   D    |
23509 // +--------+--------+--------+
23510 var table = new Ext.Panel({
23511     title: 'Table Layout',
23512     layout:'table',
23513     defaults: {
23514         // applied to each contained panel
23515         bodyStyle:'padding:20px'
23516     },
23517     layoutConfig: {
23518         // The total column count must be specified here
23519         columns: 3
23520     },
23521     items: [{
23522         html: '&lt;p&gt;Cell A content&lt;/p&gt;',
23523         rowspan: 2
23524     },{
23525         html: '&lt;p&gt;Cell B content&lt;/p&gt;',
23526         colspan: 2
23527     },{
23528         html: '&lt;p&gt;Cell C content&lt;/p&gt;',
23529         cellCls: 'highlight'
23530     },{
23531         html: '&lt;p&gt;Cell D content&lt;/p&gt;'
23532     }]
23533 });
23534 </code></pre>
23535  */
23536 Ext.layout.TableLayout = Ext.extend(Ext.layout.ContainerLayout, {
23537     /**
23538      * @cfg {Number} columns
23539      * The total number of columns to create in the table for this layout.  If not specified, all Components added to
23540      * this layout will be rendered into a single row using one column per Component.
23541      */
23542
23543     // private
23544     monitorResize:false,
23545
23546     type: 'table',
23547
23548     targetCls: 'x-table-layout-ct',
23549
23550     /**
23551      * @cfg {Object} tableAttrs
23552      * <p>An object containing properties which are added to the {@link Ext.DomHelper DomHelper} specification
23553      * used to create the layout's <tt>&lt;table&gt;</tt> element. Example:</p><pre><code>
23554 {
23555     xtype: 'panel',
23556     layout: 'table',
23557     layoutConfig: {
23558         tableAttrs: {
23559             style: {
23560                 width: '100%'
23561             }
23562         },
23563         columns: 3
23564     }
23565 }</code></pre>
23566      */
23567     tableAttrs:null,
23568
23569     // private
23570     setContainer : function(ct){
23571         Ext.layout.TableLayout.superclass.setContainer.call(this, ct);
23572
23573         this.currentRow = 0;
23574         this.currentColumn = 0;
23575         this.cells = [];
23576     },
23577     
23578     // private
23579     onLayout : function(ct, target){
23580         var cs = ct.items.items, len = cs.length, c, i;
23581
23582         if(!this.table){
23583             target.addClass('x-table-layout-ct');
23584
23585             this.table = target.createChild(
23586                 Ext.apply({tag:'table', cls:'x-table-layout', cellspacing: 0, cn: {tag: 'tbody'}}, this.tableAttrs), null, true);
23587         }
23588         this.renderAll(ct, target);
23589     },
23590
23591     // private
23592     getRow : function(index){
23593         var row = this.table.tBodies[0].childNodes[index];
23594         if(!row){
23595             row = document.createElement('tr');
23596             this.table.tBodies[0].appendChild(row);
23597         }
23598         return row;
23599     },
23600
23601     // private
23602     getNextCell : function(c){
23603         var cell = this.getNextNonSpan(this.currentColumn, this.currentRow);
23604         var curCol = this.currentColumn = cell[0], curRow = this.currentRow = cell[1];
23605         for(var rowIndex = curRow; rowIndex < curRow + (c.rowspan || 1); rowIndex++){
23606             if(!this.cells[rowIndex]){
23607                 this.cells[rowIndex] = [];
23608             }
23609             for(var colIndex = curCol; colIndex < curCol + (c.colspan || 1); colIndex++){
23610                 this.cells[rowIndex][colIndex] = true;
23611             }
23612         }
23613         var td = document.createElement('td');
23614         if(c.cellId){
23615             td.id = c.cellId;
23616         }
23617         var cls = 'x-table-layout-cell';
23618         if(c.cellCls){
23619             cls += ' ' + c.cellCls;
23620         }
23621         td.className = cls;
23622         if(c.colspan){
23623             td.colSpan = c.colspan;
23624         }
23625         if(c.rowspan){
23626             td.rowSpan = c.rowspan;
23627         }
23628         this.getRow(curRow).appendChild(td);
23629         return td;
23630     },
23631
23632     // private
23633     getNextNonSpan: function(colIndex, rowIndex){
23634         var cols = this.columns;
23635         while((cols && colIndex >= cols) || (this.cells[rowIndex] && this.cells[rowIndex][colIndex])) {
23636             if(cols && colIndex >= cols){
23637                 rowIndex++;
23638                 colIndex = 0;
23639             }else{
23640                 colIndex++;
23641             }
23642         }
23643         return [colIndex, rowIndex];
23644     },
23645
23646     // private
23647     renderItem : function(c, position, target){
23648         // Ensure we have our inner table to get cells to render into.
23649         if(!this.table){
23650             this.table = target.createChild(
23651                 Ext.apply({tag:'table', cls:'x-table-layout', cellspacing: 0, cn: {tag: 'tbody'}}, this.tableAttrs), null, true);
23652         }
23653         if(c && !c.rendered){
23654             c.render(this.getNextCell(c));
23655             this.configureItem(c);
23656         }else if(c && !this.isValidParent(c, target)){
23657             var container = this.getNextCell(c);
23658             container.insertBefore(c.getPositionEl().dom, null);
23659             c.container = Ext.get(container);
23660             this.configureItem(c);
23661         }
23662     },
23663
23664     // private
23665     isValidParent : function(c, target){
23666         return c.getPositionEl().up('table', 5).dom.parentNode === (target.dom || target);
23667     },
23668     
23669     destroy: function(){
23670         delete this.table;
23671         Ext.layout.TableLayout.superclass.destroy.call(this);
23672     }
23673
23674     /**
23675      * @property activeItem
23676      * @hide
23677      */
23678 });
23679
23680 Ext.Container.LAYOUTS['table'] = Ext.layout.TableLayout;/**
23681  * @class Ext.layout.AbsoluteLayout
23682  * @extends Ext.layout.AnchorLayout
23683  * <p>This is a layout that inherits the anchoring of <b>{@link Ext.layout.AnchorLayout}</b> and adds the
23684  * ability for x/y positioning using the standard x and y component config options.</p>
23685  * <p>This class is intended to be extended or created via the <tt><b>{@link Ext.Container#layout layout}</b></tt>
23686  * configuration property.  See <tt><b>{@link Ext.Container#layout}</b></tt> for additional details.</p>
23687  * <p>Example usage:</p>
23688  * <pre><code>
23689 var form = new Ext.form.FormPanel({
23690     title: 'Absolute Layout',
23691     layout:'absolute',
23692     layoutConfig: {
23693         // layout-specific configs go here
23694         extraCls: 'x-abs-layout-item',
23695     },
23696     baseCls: 'x-plain',
23697     url:'save-form.php',
23698     defaultType: 'textfield',
23699     items: [{
23700         x: 0,
23701         y: 5,
23702         xtype:'label',
23703         text: 'Send To:'
23704     },{
23705         x: 60,
23706         y: 0,
23707         name: 'to',
23708         anchor:'100%'  // anchor width by percentage
23709     },{
23710         x: 0,
23711         y: 35,
23712         xtype:'label',
23713         text: 'Subject:'
23714     },{
23715         x: 60,
23716         y: 30,
23717         name: 'subject',
23718         anchor: '100%'  // anchor width by percentage
23719     },{
23720         x:0,
23721         y: 60,
23722         xtype: 'textarea',
23723         name: 'msg',
23724         anchor: '100% 100%'  // anchor width and height
23725     }]
23726 });
23727 </code></pre>
23728  */
23729 Ext.layout.AbsoluteLayout = Ext.extend(Ext.layout.AnchorLayout, {
23730
23731     extraCls: 'x-abs-layout-item',
23732
23733     type: 'absolute',
23734
23735     onLayout : function(ct, target){
23736         target.position();
23737         this.paddingLeft = target.getPadding('l');
23738         this.paddingTop = target.getPadding('t');
23739         Ext.layout.AbsoluteLayout.superclass.onLayout.call(this, ct, target);
23740     },
23741
23742     // private
23743     adjustWidthAnchor : function(value, comp){
23744         return value ? value - comp.getPosition(true)[0] + this.paddingLeft : value;
23745     },
23746
23747     // private
23748     adjustHeightAnchor : function(value, comp){
23749         return  value ? value - comp.getPosition(true)[1] + this.paddingTop : value;
23750     }
23751     /**
23752      * @property activeItem
23753      * @hide
23754      */
23755 });
23756 Ext.Container.LAYOUTS['absolute'] = Ext.layout.AbsoluteLayout;
23757 /**
23758  * @class Ext.layout.BoxLayout
23759  * @extends Ext.layout.ContainerLayout
23760  * <p>Base Class for HBoxLayout and VBoxLayout Classes. Generally it should not need to be used directly.</p>
23761  */
23762 Ext.layout.BoxLayout = Ext.extend(Ext.layout.ContainerLayout, {
23763     /**
23764      * @cfg {Object} defaultMargins
23765      * <p>If the individual contained items do not have a <tt>margins</tt>
23766      * property specified, the default margins from this property will be
23767      * applied to each item.</p>
23768      * <br><p>This property may be specified as an object containing margins
23769      * to apply in the format:</p><pre><code>
23770 {
23771     top: (top margin),
23772     right: (right margin),
23773     bottom: (bottom margin),
23774     left: (left margin)
23775 }</code></pre>
23776      * <p>This property may also be specified as a string containing
23777      * space-separated, numeric margin values. The order of the sides associated
23778      * with each value matches the way CSS processes margin values:</p>
23779      * <div class="mdetail-params"><ul>
23780      * <li>If there is only one value, it applies to all sides.</li>
23781      * <li>If there are two values, the top and bottom borders are set to the
23782      * first value and the right and left are set to the second.</li>
23783      * <li>If there are three values, the top is set to the first value, the left
23784      * and right are set to the second, and the bottom is set to the third.</li>
23785      * <li>If there are four values, they apply to the top, right, bottom, and
23786      * left, respectively.</li>
23787      * </ul></div>
23788      * <p>Defaults to:</p><pre><code>
23789      * {top:0, right:0, bottom:0, left:0}
23790      * </code></pre>
23791      */
23792     defaultMargins : {left:0,top:0,right:0,bottom:0},
23793     /**
23794      * @cfg {String} padding
23795      * <p>Sets the padding to be applied to all child items managed by this layout.</p>
23796      * <p>This property must be specified as a string containing
23797      * space-separated, numeric padding values. The order of the sides associated
23798      * with each value matches the way CSS processes padding values:</p>
23799      * <div class="mdetail-params"><ul>
23800      * <li>If there is only one value, it applies to all sides.</li>
23801      * <li>If there are two values, the top and bottom borders are set to the
23802      * first value and the right and left are set to the second.</li>
23803      * <li>If there are three values, the top is set to the first value, the left
23804      * and right are set to the second, and the bottom is set to the third.</li>
23805      * <li>If there are four values, they apply to the top, right, bottom, and
23806      * left, respectively.</li>
23807      * </ul></div>
23808      * <p>Defaults to: <code>"0"</code></p>
23809      */
23810     padding : '0',
23811     // documented in subclasses
23812     pack : 'start',
23813
23814     // private
23815     monitorResize : true,
23816     type: 'box',
23817     scrollOffset : 0,
23818     extraCls : 'x-box-item',
23819     targetCls : 'x-box-layout-ct',
23820     innerCls : 'x-box-inner',
23821
23822     constructor : function(config){
23823         Ext.layout.BoxLayout.superclass.constructor.call(this, config);
23824
23825         if (Ext.isString(this.defaultMargins)) {
23826             this.defaultMargins = this.parseMargins(this.defaultMargins);
23827         }
23828         
23829         var handler = this.overflowHandler;
23830         
23831         if (typeof handler == 'string') {
23832             handler = {
23833                 type: handler
23834             };
23835         }
23836         
23837         var handlerType = 'none';
23838         if (handler && handler.type != undefined) {
23839             handlerType = handler.type;
23840         }
23841         
23842         var constructor = Ext.layout.boxOverflow[handlerType];
23843         if (constructor[this.type]) {
23844             constructor = constructor[this.type];
23845         }
23846         
23847         this.overflowHandler = new constructor(this, handler);
23848     },
23849
23850     /**
23851      * @private
23852      * Runs the child box calculations and caches them in childBoxCache. Subclasses can used these cached values
23853      * when laying out
23854      */
23855     onLayout: function(container, target) {
23856         Ext.layout.BoxLayout.superclass.onLayout.call(this, container, target);
23857
23858         var tSize = this.getLayoutTargetSize(),
23859             items = this.getVisibleItems(container),
23860             calcs = this.calculateChildBoxes(items, tSize),
23861             boxes = calcs.boxes,
23862             meta  = calcs.meta;
23863         
23864         //invoke the overflow handler, if one is configured
23865         if (tSize.width > 0) {
23866             var handler = this.overflowHandler,
23867                 method  = meta.tooNarrow ? 'handleOverflow' : 'clearOverflow';
23868             
23869             var results = handler[method](calcs, tSize);
23870             
23871             if (results) {
23872                 if (results.targetSize) {
23873                     tSize = results.targetSize;
23874                 }
23875                 
23876                 if (results.recalculate) {
23877                     items = this.getVisibleItems(container);
23878                     calcs = this.calculateChildBoxes(items, tSize);
23879                     boxes = calcs.boxes;
23880                 }
23881             }
23882         }
23883         
23884         /**
23885          * @private
23886          * @property layoutTargetLastSize
23887          * @type Object
23888          * Private cache of the last measured size of the layout target. This should never be used except by
23889          * BoxLayout subclasses during their onLayout run.
23890          */
23891         this.layoutTargetLastSize = tSize;
23892         
23893         /**
23894          * @private
23895          * @property childBoxCache
23896          * @type Array
23897          * Array of the last calculated height, width, top and left positions of each visible rendered component
23898          * within the Box layout.
23899          */
23900         this.childBoxCache = calcs;
23901         
23902         this.updateInnerCtSize(tSize, calcs);
23903         this.updateChildBoxes(boxes);
23904
23905         // Putting a box layout into an overflowed container is NOT correct and will make a second layout pass necessary.
23906         this.handleTargetOverflow(tSize, container, target);
23907     },
23908
23909     /**
23910      * Resizes and repositions each child component
23911      * @param {Array} boxes The box measurements
23912      */
23913     updateChildBoxes: function(boxes) {
23914         for (var i = 0, length = boxes.length; i < length; i++) {
23915             var box  = boxes[i],
23916                 comp = box.component;
23917             
23918             if (box.dirtySize) {
23919                 comp.setSize(box.width, box.height);
23920             }
23921             // Don't set positions to NaN
23922             if (isNaN(box.left) || isNaN(box.top)) {
23923                 continue;
23924             }
23925             
23926             comp.setPosition(box.left, box.top);
23927         }
23928     },
23929
23930     /**
23931      * @private
23932      * Called by onRender just before the child components are sized and positioned. This resizes the innerCt
23933      * to make sure all child items fit within it. We call this before sizing the children because if our child
23934      * items are larger than the previous innerCt size the browser will insert scrollbars and then remove them
23935      * again immediately afterwards, giving a performance hit.
23936      * Subclasses should provide an implementation.
23937      * @param {Object} currentSize The current height and width of the innerCt
23938      * @param {Array} calculations The new box calculations of all items to be laid out
23939      */
23940     updateInnerCtSize: function(tSize, calcs) {
23941         var align   = this.align,
23942             padding = this.padding,
23943             width   = tSize.width,
23944             height  = tSize.height;
23945         
23946         if (this.type == 'hbox') {
23947             var innerCtWidth  = width,
23948                 innerCtHeight = calcs.meta.maxHeight + padding.top + padding.bottom;
23949
23950             if (align == 'stretch') {
23951                 innerCtHeight = height;
23952             } else if (align == 'middle') {
23953                 innerCtHeight = Math.max(height, innerCtHeight);
23954             }
23955         } else {
23956             var innerCtHeight = height,
23957                 innerCtWidth  = calcs.meta.maxWidth + padding.left + padding.right;
23958
23959             if (align == 'stretch') {
23960                 innerCtWidth = width;
23961             } else if (align == 'center') {
23962                 innerCtWidth = Math.max(width, innerCtWidth);
23963             }
23964         }
23965
23966         this.innerCt.setSize(innerCtWidth || undefined, innerCtHeight || undefined);
23967     },
23968
23969     /**
23970      * @private
23971      * This should be called after onLayout of any BoxLayout subclass. If the target's overflow is not set to 'hidden',
23972      * we need to lay out a second time because the scrollbars may have modified the height and width of the layout
23973      * target. Having a Box layout inside such a target is therefore not recommended.
23974      * @param {Object} previousTargetSize The size and height of the layout target before we just laid out
23975      * @param {Ext.Container} container The container
23976      * @param {Ext.Element} target The target element
23977      */
23978     handleTargetOverflow: function(previousTargetSize, container, target) {
23979         var overflow = target.getStyle('overflow');
23980
23981         if (overflow && overflow != 'hidden' &&!this.adjustmentPass) {
23982             var newTargetSize = this.getLayoutTargetSize();
23983             if (newTargetSize.width != previousTargetSize.width || newTargetSize.height != previousTargetSize.height){
23984                 this.adjustmentPass = true;
23985                 this.onLayout(container, target);
23986             }
23987         }
23988
23989         delete this.adjustmentPass;
23990     },
23991
23992     // private
23993     isValidParent : function(c, target) {
23994         return this.innerCt && c.getPositionEl().dom.parentNode == this.innerCt.dom;
23995     },
23996
23997     /**
23998      * @private
23999      * Returns all items that are both rendered and visible
24000      * @return {Array} All matching items
24001      */
24002     getVisibleItems: function(ct) {
24003         var ct  = ct || this.container,
24004             t   = ct.getLayoutTarget(),
24005             cti = ct.items.items,
24006             len = cti.length,
24007
24008             i, c, items = [];
24009
24010         for (i = 0; i < len; i++) {
24011             if((c = cti[i]).rendered && this.isValidParent(c, t) && c.hidden !== true  && c.collapsed !== true && c.shouldLayout !== false){
24012                 items.push(c);
24013             }
24014         }
24015
24016         return items;
24017     },
24018
24019     // private
24020     renderAll : function(ct, target) {
24021         if (!this.innerCt) {
24022             // the innerCt prevents wrapping and shuffling while the container is resizing
24023             this.innerCt = target.createChild({cls:this.innerCls});
24024             this.padding = this.parseMargins(this.padding);
24025         }
24026         Ext.layout.BoxLayout.superclass.renderAll.call(this, ct, this.innerCt);
24027     },
24028
24029     getLayoutTargetSize : function() {
24030         var target = this.container.getLayoutTarget(), ret;
24031         
24032         if (target) {
24033             ret = target.getViewSize();
24034
24035             // IE in strict mode will return a width of 0 on the 1st pass of getViewSize.
24036             // Use getStyleSize to verify the 0 width, the adjustment pass will then work properly
24037             // with getViewSize
24038             if (Ext.isIE && Ext.isStrict && ret.width == 0){
24039                 ret =  target.getStyleSize();
24040             }
24041
24042             ret.width  -= target.getPadding('lr');
24043             ret.height -= target.getPadding('tb');
24044         }
24045         
24046         return ret;
24047     },
24048
24049     // private
24050     renderItem : function(c) {
24051         if(Ext.isString(c.margins)){
24052             c.margins = this.parseMargins(c.margins);
24053         }else if(!c.margins){
24054             c.margins = this.defaultMargins;
24055         }
24056         Ext.layout.BoxLayout.superclass.renderItem.apply(this, arguments);
24057     },
24058     
24059     /**
24060      * @private
24061      */
24062     destroy: function() {
24063         Ext.destroy(this.overflowHandler);
24064         
24065         Ext.layout.BoxLayout.superclass.destroy.apply(this, arguments);
24066     }
24067 });
24068
24069
24070
24071 Ext.ns('Ext.layout.boxOverflow');
24072
24073 /**
24074  * @class Ext.layout.boxOverflow.None
24075  * @extends Object
24076  * Base class for Box Layout overflow handlers. These specialized classes are invoked when a Box Layout
24077  * (either an HBox or a VBox) has child items that are either too wide (for HBox) or too tall (for VBox)
24078  * for its container.
24079  */
24080
24081 Ext.layout.boxOverflow.None = Ext.extend(Object, {
24082     constructor: function(layout, config) {
24083         this.layout = layout;
24084         
24085         Ext.apply(this, config || {});
24086     },
24087     
24088     handleOverflow: Ext.emptyFn,
24089     
24090     clearOverflow: Ext.emptyFn
24091 });
24092
24093
24094 Ext.layout.boxOverflow.none = Ext.layout.boxOverflow.None;
24095 /**
24096  * @class Ext.layout.boxOverflow.Menu
24097  * @extends Ext.layout.boxOverflow.None
24098  * Description
24099  */
24100 Ext.layout.boxOverflow.Menu = Ext.extend(Ext.layout.boxOverflow.None, {
24101     /**
24102      * @cfg afterCls
24103      * @type String
24104      * CSS class added to the afterCt element. This is the element that holds any special items such as scrollers,
24105      * which must always be present at the rightmost edge of the Container
24106      */
24107     afterCls: 'x-strip-right',
24108     
24109     /**
24110      * @property noItemsMenuText
24111      * @type String
24112      * HTML fragment to render into the toolbar overflow menu if there are no items to display
24113      */
24114     noItemsMenuText : '<div class="x-toolbar-no-items">(None)</div>',
24115     
24116     constructor: function(layout) {
24117         Ext.layout.boxOverflow.Menu.superclass.constructor.apply(this, arguments);
24118         
24119         /**
24120          * @property menuItems
24121          * @type Array
24122          * Array of all items that are currently hidden and should go into the dropdown menu
24123          */
24124         this.menuItems = [];
24125     },
24126     
24127     /**
24128      * @private
24129      * Creates the beforeCt, innerCt and afterCt elements if they have not already been created
24130      * @param {Ext.Container} container The Container attached to this Layout instance
24131      * @param {Ext.Element} target The target Element
24132      */
24133     createInnerElements: function() {
24134         if (!this.afterCt) {
24135             this.afterCt  = this.layout.innerCt.insertSibling({cls: this.afterCls},  'before');
24136         }
24137     },
24138     
24139     /**
24140      * @private
24141      */
24142     clearOverflow: function(calculations, targetSize) {
24143         var newWidth = targetSize.width + (this.afterCt ? this.afterCt.getWidth() : 0),
24144             items    = this.menuItems;
24145         
24146         this.hideTrigger();
24147         
24148         for (var index = 0, length = items.length; index < length; index++) {
24149             items.pop().component.show();
24150         }
24151         
24152         return {
24153             targetSize: {
24154                 height: targetSize.height,
24155                 width : newWidth
24156             }
24157         };
24158     },
24159     
24160     /**
24161      * @private
24162      */
24163     showTrigger: function() {
24164         this.createMenu();
24165         this.menuTrigger.show();
24166     },
24167     
24168     /**
24169      * @private
24170      */
24171     hideTrigger: function() {
24172         if (this.menuTrigger != undefined) {
24173             this.menuTrigger.hide();
24174         }
24175     },
24176     
24177     /**
24178      * @private
24179      * Called before the overflow menu is shown. This constructs the menu's items, caching them for as long as it can.
24180      */
24181     beforeMenuShow: function(menu) {
24182         var items = this.menuItems,
24183             len   = items.length,
24184             item,
24185             prev;
24186
24187         var needsSep = function(group, item){
24188             return group.isXType('buttongroup') && !(item instanceof Ext.Toolbar.Separator);
24189         };
24190         
24191         this.clearMenu();
24192         menu.removeAll();
24193         
24194         for (var i = 0; i < len; i++) {
24195             item = items[i].component;
24196             
24197             if (prev && (needsSep(item, prev) || needsSep(prev, item))) {
24198                 menu.add('-');
24199             }
24200             
24201             this.addComponentToMenu(menu, item);
24202             prev = item;
24203         }
24204
24205         // put something so the menu isn't empty if no compatible items found
24206         if (menu.items.length < 1) {
24207             menu.add(this.noItemsMenuText);
24208         }
24209     },
24210     
24211     /**
24212      * @private
24213      * Returns a menu config for a given component. This config is used to create a menu item
24214      * to be added to the expander menu
24215      * @param {Ext.Component} component The component to create the config for
24216      * @param {Boolean} hideOnClick Passed through to the menu item
24217      */
24218     createMenuConfig : function(component, hideOnClick){
24219         var config = Ext.apply({}, component.initialConfig),
24220             group  = component.toggleGroup;
24221
24222         Ext.copyTo(config, component, [
24223             'iconCls', 'icon', 'itemId', 'disabled', 'handler', 'scope', 'menu'
24224         ]);
24225
24226         Ext.apply(config, {
24227             text       : component.overflowText || component.text,
24228             hideOnClick: hideOnClick
24229         });
24230
24231         if (group || component.enableToggle) {
24232             Ext.apply(config, {
24233                 group  : group,
24234                 checked: component.pressed,
24235                 listeners: {
24236                     checkchange: function(item, checked){
24237                         component.toggle(checked);
24238                     }
24239                 }
24240             });
24241         }
24242
24243         delete config.ownerCt;
24244         delete config.xtype;
24245         delete config.id;
24246
24247         return config;
24248     },
24249
24250     /**
24251      * @private
24252      * Adds the given Toolbar item to the given menu. Buttons inside a buttongroup are added individually.
24253      * @param {Ext.menu.Menu} menu The menu to add to
24254      * @param {Ext.Component} component The component to add
24255      */
24256     addComponentToMenu : function(menu, component) {
24257         if (component instanceof Ext.Toolbar.Separator) {
24258             menu.add('-');
24259
24260         } else if (Ext.isFunction(component.isXType)) {
24261             if (component.isXType('splitbutton')) {
24262                 menu.add(this.createMenuConfig(component, true));
24263
24264             } else if (component.isXType('button')) {
24265                 menu.add(this.createMenuConfig(component, !component.menu));
24266
24267             } else if (component.isXType('buttongroup')) {
24268                 component.items.each(function(item){
24269                      this.addComponentToMenu(menu, item);
24270                 }, this);
24271             }
24272         }
24273     },
24274     
24275     /**
24276      * @private
24277      * Deletes the sub-menu of each item in the expander menu. Submenus are created for items such as
24278      * splitbuttons and buttongroups, where the Toolbar item cannot be represented by a single menu item
24279      */
24280     clearMenu : function(){
24281         var menu = this.moreMenu;
24282         if (menu && menu.items) {
24283             menu.items.each(function(item){
24284                 delete item.menu;
24285             });
24286         }
24287     },
24288     
24289     /**
24290      * @private
24291      * Creates the overflow trigger and menu used when enableOverflow is set to true and the items
24292      * in the layout are too wide to fit in the space available
24293      */
24294     createMenu: function() {
24295         if (!this.menuTrigger) {
24296             this.createInnerElements();
24297             
24298             /**
24299              * @private
24300              * @property menu
24301              * @type Ext.menu.Menu
24302              * The expand menu - holds items for every item that cannot be shown
24303              * because the container is currently not large enough.
24304              */
24305             this.menu = new Ext.menu.Menu({
24306                 ownerCt : this.layout.container,
24307                 listeners: {
24308                     scope: this,
24309                     beforeshow: this.beforeMenuShow
24310                 }
24311             });
24312
24313             /**
24314              * @private
24315              * @property menuTrigger
24316              * @type Ext.Button
24317              * The expand button which triggers the overflow menu to be shown
24318              */
24319             this.menuTrigger = new Ext.Button({
24320                 iconCls : 'x-toolbar-more-icon',
24321                 cls     : 'x-toolbar-more',
24322                 menu    : this.menu,
24323                 renderTo: this.afterCt
24324             });
24325         }
24326     },
24327     
24328     /**
24329      * @private
24330      */
24331     destroy: function() {
24332         Ext.destroy(this.menu, this.menuTrigger);
24333     }
24334 });
24335
24336 Ext.layout.boxOverflow.menu = Ext.layout.boxOverflow.Menu;
24337
24338
24339 /**
24340  * @class Ext.layout.boxOverflow.HorizontalMenu
24341  * @extends Ext.layout.boxOverflow.Menu
24342  * Description
24343  */
24344 Ext.layout.boxOverflow.HorizontalMenu = Ext.extend(Ext.layout.boxOverflow.Menu, {
24345     
24346     constructor: function() {
24347         Ext.layout.boxOverflow.HorizontalMenu.superclass.constructor.apply(this, arguments);
24348         
24349         var me = this,
24350             layout = me.layout,
24351             origFunction = layout.calculateChildBoxes;
24352         
24353         layout.calculateChildBoxes = function(visibleItems, targetSize) {
24354             var calcs = origFunction.apply(layout, arguments),
24355                 meta  = calcs.meta,
24356                 items = me.menuItems;
24357             
24358             //calculate the width of the items currently hidden solely because there is not enough space
24359             //to display them
24360             var hiddenWidth = 0;
24361             for (var index = 0, length = items.length; index < length; index++) {
24362                 hiddenWidth += items[index].width;
24363             }
24364             
24365             meta.minimumWidth += hiddenWidth;
24366             meta.tooNarrow = meta.minimumWidth > targetSize.width;
24367             
24368             return calcs;
24369         };        
24370     },
24371     
24372     handleOverflow: function(calculations, targetSize) {
24373         this.showTrigger();
24374         
24375         var newWidth    = targetSize.width - this.afterCt.getWidth(),
24376             boxes       = calculations.boxes,
24377             usedWidth   = 0,
24378             recalculate = false;
24379         
24380         //calculate the width of all visible items and any spare width
24381         for (var index = 0, length = boxes.length; index < length; index++) {
24382             usedWidth += boxes[index].width;
24383         }
24384         
24385         var spareWidth = newWidth - usedWidth,
24386             showCount  = 0;
24387         
24388         //see if we can re-show any of the hidden components
24389         for (var index = 0, length = this.menuItems.length; index < length; index++) {
24390             var hidden = this.menuItems[index],
24391                 comp   = hidden.component,
24392                 width  = hidden.width;
24393             
24394             if (width < spareWidth) {
24395                 comp.show();
24396                 
24397                 spareWidth -= width;
24398                 showCount ++;
24399                 recalculate = true;
24400             } else {
24401                 break;
24402             }
24403         }
24404                 
24405         if (recalculate) {
24406             this.menuItems = this.menuItems.slice(showCount);
24407         } else {
24408             for (var i = boxes.length - 1; i >= 0; i--) {
24409                 var item  = boxes[i].component,
24410                     right = boxes[i].left + boxes[i].width;
24411
24412                 if (right >= newWidth) {
24413                     this.menuItems.unshift({
24414                         component: item,
24415                         width    : boxes[i].width
24416                     });
24417
24418                     item.hide();
24419                 } else {
24420                     break;
24421                 }
24422             }
24423         }
24424         
24425         if (this.menuItems.length == 0) {
24426             this.hideTrigger();
24427         }
24428         
24429         return {
24430             targetSize: {
24431                 height: targetSize.height,
24432                 width : newWidth
24433             },
24434             recalculate: recalculate
24435         };
24436     }
24437 });
24438
24439 Ext.layout.boxOverflow.menu.hbox = Ext.layout.boxOverflow.HorizontalMenu;/**
24440  * @class Ext.layout.boxOverflow.Scroller
24441  * @extends Ext.layout.boxOverflow.None
24442  * Description
24443  */
24444 Ext.layout.boxOverflow.Scroller = Ext.extend(Ext.layout.boxOverflow.None, {
24445     /**
24446      * @cfg animateScroll
24447      * @type Boolean
24448      * True to animate the scrolling of items within the layout (defaults to true, ignored if enableScroll is false)
24449      */
24450     animateScroll: true,
24451     
24452     /**
24453      * @cfg scrollIncrement
24454      * @type Number
24455      * The number of pixels to scroll by on scroller click (defaults to 100)
24456      */
24457     scrollIncrement: 100,
24458     
24459     /**
24460      * @cfg wheelIncrement
24461      * @type Number
24462      * The number of pixels to increment on mouse wheel scrolling (defaults to <tt>3</tt>).
24463      */
24464     wheelIncrement: 3,
24465     
24466     /**
24467      * @cfg scrollRepeatInterval
24468      * @type Number
24469      * Number of milliseconds between each scroll while a scroller button is held down (defaults to 400)
24470      */
24471     scrollRepeatInterval: 400,
24472     
24473     /**
24474      * @cfg scrollDuration
24475      * @type Number
24476      * Number of seconds that each scroll animation lasts (defaults to 0.4)
24477      */
24478     scrollDuration: 0.4,
24479     
24480     /**
24481      * @cfg beforeCls
24482      * @type String
24483      * CSS class added to the beforeCt element. This is the element that holds any special items such as scrollers,
24484      * which must always be present at the leftmost edge of the Container
24485      */
24486     beforeCls: 'x-strip-left',
24487     
24488     /**
24489      * @cfg afterCls
24490      * @type String
24491      * CSS class added to the afterCt element. This is the element that holds any special items such as scrollers,
24492      * which must always be present at the rightmost edge of the Container
24493      */
24494     afterCls: 'x-strip-right',
24495     
24496     /**
24497      * @cfg scrollerCls
24498      * @type String
24499      * CSS class added to both scroller elements if enableScroll is used
24500      */
24501     scrollerCls: 'x-strip-scroller',
24502     
24503     /**
24504      * @cfg beforeScrollerCls
24505      * @type String
24506      * CSS class added to the left scroller element if enableScroll is used
24507      */
24508     beforeScrollerCls: 'x-strip-scroller-left',
24509     
24510     /**
24511      * @cfg afterScrollerCls
24512      * @type String
24513      * CSS class added to the right scroller element if enableScroll is used
24514      */
24515     afterScrollerCls: 'x-strip-scroller-right',
24516     
24517     /**
24518      * @private
24519      * Sets up an listener to scroll on the layout's innerCt mousewheel event
24520      */
24521     createWheelListener: function() {
24522         this.layout.innerCt.on({
24523             scope     : this,
24524             mousewheel: function(e) {
24525                 e.stopEvent();
24526
24527                 this.scrollBy(e.getWheelDelta() * this.wheelIncrement * -1, false);
24528             }
24529         });
24530     },
24531     
24532     /**
24533      * @private
24534      * Most of the heavy lifting is done in the subclasses
24535      */
24536     handleOverflow: function(calculations, targetSize) {
24537         this.createInnerElements();
24538         this.showScrollers();
24539     },
24540     
24541     /**
24542      * @private
24543      */
24544     clearOverflow: function() {
24545         this.hideScrollers();
24546     },
24547     
24548     /**
24549      * @private
24550      * Shows the scroller elements in the beforeCt and afterCt. Creates the scrollers first if they are not already
24551      * present. 
24552      */
24553     showScrollers: function() {
24554         this.createScrollers();
24555         
24556         this.beforeScroller.show();
24557         this.afterScroller.show();
24558         
24559         this.updateScrollButtons();
24560     },
24561     
24562     /**
24563      * @private
24564      * Hides the scroller elements in the beforeCt and afterCt
24565      */
24566     hideScrollers: function() {
24567         if (this.beforeScroller != undefined) {
24568             this.beforeScroller.hide();
24569             this.afterScroller.hide();          
24570         }
24571     },
24572     
24573     /**
24574      * @private
24575      * Creates the clickable scroller elements and places them into the beforeCt and afterCt
24576      */
24577     createScrollers: function() {
24578         if (!this.beforeScroller && !this.afterScroller) {
24579             var before = this.beforeCt.createChild({
24580                 cls: String.format("{0} {1} ", this.scrollerCls, this.beforeScrollerCls)
24581             });
24582             
24583             var after = this.afterCt.createChild({
24584                 cls: String.format("{0} {1}", this.scrollerCls, this.afterScrollerCls)
24585             });
24586             
24587             before.addClassOnOver(this.beforeScrollerCls + '-hover');
24588             after.addClassOnOver(this.afterScrollerCls + '-hover');
24589             
24590             before.setVisibilityMode(Ext.Element.DISPLAY);
24591             after.setVisibilityMode(Ext.Element.DISPLAY);
24592             
24593             this.beforeRepeater = new Ext.util.ClickRepeater(before, {
24594                 interval: this.scrollRepeatInterval,
24595                 handler : this.scrollLeft,
24596                 scope   : this
24597             });
24598             
24599             this.afterRepeater = new Ext.util.ClickRepeater(after, {
24600                 interval: this.scrollRepeatInterval,
24601                 handler : this.scrollRight,
24602                 scope   : this
24603             });
24604             
24605             /**
24606              * @property beforeScroller
24607              * @type Ext.Element
24608              * The left scroller element. Only created when needed.
24609              */
24610             this.beforeScroller = before;
24611             
24612             /**
24613              * @property afterScroller
24614              * @type Ext.Element
24615              * The left scroller element. Only created when needed.
24616              */
24617             this.afterScroller = after;
24618         }
24619     },
24620     
24621     /**
24622      * @private
24623      */
24624     destroy: function() {
24625         Ext.destroy(this.beforeScroller, this.afterScroller, this.beforeRepeater, this.afterRepeater, this.beforeCt, this.afterCt);
24626     },
24627     
24628     /**
24629      * @private
24630      * Scrolls left or right by the number of pixels specified
24631      * @param {Number} delta Number of pixels to scroll to the right by. Use a negative number to scroll left
24632      */
24633     scrollBy: function(delta, animate) {
24634         this.scrollTo(this.getScrollPosition() + delta, animate);
24635     },
24636     
24637     /**
24638      * @private
24639      * Normalizes an item reference, string id or numerical index into a reference to the item
24640      * @param {Ext.Component|String|Number} item The item reference, id or index
24641      * @return {Ext.Component} The item
24642      */
24643     getItem: function(item) {
24644         if (Ext.isString(item)) {
24645             item = Ext.getCmp(item);
24646         } else if (Ext.isNumber(item)) {
24647             item = this.items[item];
24648         }
24649         
24650         return item;
24651     },
24652     
24653     /**
24654      * @private
24655      * @return {Object} Object passed to scrollTo when scrolling
24656      */
24657     getScrollAnim: function() {
24658         return {
24659             duration: this.scrollDuration, 
24660             callback: this.updateScrollButtons, 
24661             scope   : this
24662         };
24663     },
24664     
24665     /**
24666      * @private
24667      * Enables or disables each scroller button based on the current scroll position
24668      */
24669     updateScrollButtons: function() {
24670         if (this.beforeScroller == undefined || this.afterScroller == undefined) {
24671             return;
24672         }
24673         
24674         var beforeMeth = this.atExtremeBefore()  ? 'addClass' : 'removeClass',
24675             afterMeth  = this.atExtremeAfter() ? 'addClass' : 'removeClass',
24676             beforeCls  = this.beforeScrollerCls + '-disabled',
24677             afterCls   = this.afterScrollerCls  + '-disabled';
24678         
24679         this.beforeScroller[beforeMeth](beforeCls);
24680         this.afterScroller[afterMeth](afterCls);
24681         this.scrolling = false;
24682     },
24683     
24684     /**
24685      * @private
24686      * Returns true if the innerCt scroll is already at its left-most point
24687      * @return {Boolean} True if already at furthest left point
24688      */
24689     atExtremeBefore: function() {
24690         return this.getScrollPosition() === 0;
24691     },
24692     
24693     /**
24694      * @private
24695      * Scrolls to the left by the configured amount
24696      */
24697     scrollLeft: function(animate) {
24698         this.scrollBy(-this.scrollIncrement, animate);
24699     },
24700     
24701     /**
24702      * @private
24703      * Scrolls to the right by the configured amount
24704      */
24705     scrollRight: function(animate) {
24706         this.scrollBy(this.scrollIncrement, animate);
24707     },
24708     
24709     /**
24710      * Scrolls to the given component.
24711      * @param {String|Number|Ext.Component} item The item to scroll to. Can be a numerical index, component id 
24712      * or a reference to the component itself.
24713      * @param {Boolean} animate True to animate the scrolling
24714      */
24715     scrollToItem: function(item, animate) {
24716         item = this.getItem(item);
24717         
24718         if (item != undefined) {
24719             var visibility = this.getItemVisibility(item);
24720             
24721             if (!visibility.fullyVisible) {
24722                 var box  = item.getBox(true, true),
24723                     newX = box.x;
24724                     
24725                 if (visibility.hiddenRight) {
24726                     newX -= (this.layout.innerCt.getWidth() - box.width);
24727                 }
24728                 
24729                 this.scrollTo(newX, animate);
24730             }
24731         }
24732     },
24733     
24734     /**
24735      * @private
24736      * For a given item in the container, return an object with information on whether the item is visible
24737      * with the current innerCt scroll value.
24738      * @param {Ext.Component} item The item
24739      * @return {Object} Values for fullyVisible, hiddenLeft and hiddenRight
24740      */
24741     getItemVisibility: function(item) {
24742         var box         = this.getItem(item).getBox(true, true),
24743             itemLeft    = box.x,
24744             itemRight   = box.x + box.width,
24745             scrollLeft  = this.getScrollPosition(),
24746             scrollRight = this.layout.innerCt.getWidth() + scrollLeft;
24747         
24748         return {
24749             hiddenLeft  : itemLeft < scrollLeft,
24750             hiddenRight : itemRight > scrollRight,
24751             fullyVisible: itemLeft > scrollLeft && itemRight < scrollRight
24752         };
24753     }
24754 });
24755
24756 Ext.layout.boxOverflow.scroller = Ext.layout.boxOverflow.Scroller;
24757
24758
24759 /**\r
24760  * @class Ext.layout.boxOverflow.VerticalScroller\r
24761  * @extends Ext.layout.boxOverflow.Scroller\r
24762  * Description\r
24763  */\r
24764 Ext.layout.boxOverflow.VerticalScroller = Ext.extend(Ext.layout.boxOverflow.Scroller, {
24765     scrollIncrement: 75,
24766     wheelIncrement : 2,
24767     
24768     handleOverflow: function(calculations, targetSize) {
24769         Ext.layout.boxOverflow.VerticalScroller.superclass.handleOverflow.apply(this, arguments);
24770         
24771         return {
24772             targetSize: {
24773                 height: targetSize.height - (this.beforeCt.getHeight() + this.afterCt.getHeight()),
24774                 width : targetSize.width
24775             }
24776         };
24777     },
24778     
24779     /**
24780      * @private
24781      * Creates the beforeCt and afterCt elements if they have not already been created
24782      */
24783     createInnerElements: function() {
24784         var target = this.layout.innerCt;
24785         
24786         //normal items will be rendered to the innerCt. beforeCt and afterCt allow for fixed positioning of
24787         //special items such as scrollers or dropdown menu triggers
24788         if (!this.beforeCt) {
24789             this.beforeCt = target.insertSibling({cls: this.beforeCls}, 'before');
24790             this.afterCt  = target.insertSibling({cls: this.afterCls},  'after');
24791
24792             this.createWheelListener();
24793         }
24794     },
24795     
24796     /**
24797      * @private
24798      * Scrolls to the given position. Performs bounds checking.
24799      * @param {Number} position The position to scroll to. This is constrained.
24800      * @param {Boolean} animate True to animate. If undefined, falls back to value of this.animateScroll
24801      */
24802     scrollTo: function(position, animate) {
24803         var oldPosition = this.getScrollPosition(),
24804             newPosition = position.constrain(0, this.getMaxScrollBottom());
24805         
24806         if (newPosition != oldPosition && !this.scrolling) {
24807             if (animate == undefined) {
24808                 animate = this.animateScroll;
24809             }
24810             
24811             this.layout.innerCt.scrollTo('top', newPosition, animate ? this.getScrollAnim() : false);
24812             
24813             if (animate) {
24814                 this.scrolling = true;
24815             } else {
24816                 this.scrolling = false;
24817                 this.updateScrollButtons();
24818             }
24819         }
24820     },
24821     
24822     /**
24823      * Returns the current scroll position of the innerCt element
24824      * @return {Number} The current scroll position
24825      */
24826     getScrollPosition: function(){
24827         return parseInt(this.layout.innerCt.dom.scrollTop, 10) || 0;
24828     },
24829     
24830     /**
24831      * @private
24832      * Returns the maximum value we can scrollTo
24833      * @return {Number} The max scroll value
24834      */
24835     getMaxScrollBottom: function() {
24836         return this.layout.innerCt.dom.scrollHeight - this.layout.innerCt.getHeight();
24837     },
24838     
24839     /**
24840      * @private
24841      * Returns true if the innerCt scroll is already at its right-most point
24842      * @return {Boolean} True if already at furthest right point
24843      */
24844     atExtremeAfter: function() {
24845         return this.getScrollPosition() >= this.getMaxScrollBottom();
24846     }
24847 });
24848
24849 Ext.layout.boxOverflow.scroller.vbox = Ext.layout.boxOverflow.VerticalScroller;
24850
24851
24852 /**
24853  * @class Ext.layout.boxOverflow.HorizontalScroller
24854  * @extends Ext.layout.boxOverflow.Scroller
24855  * Description
24856  */
24857 Ext.layout.boxOverflow.HorizontalScroller = Ext.extend(Ext.layout.boxOverflow.Scroller, {
24858     handleOverflow: function(calculations, targetSize) {
24859         Ext.layout.boxOverflow.HorizontalScroller.superclass.handleOverflow.apply(this, arguments);
24860         
24861         return {
24862             targetSize: {
24863                 height: targetSize.height,
24864                 width : targetSize.width - (this.beforeCt.getWidth() + this.afterCt.getWidth())
24865             }
24866         };
24867     },
24868     
24869     /**
24870      * @private
24871      * Creates the beforeCt and afterCt elements if they have not already been created
24872      */
24873     createInnerElements: function() {
24874         var target = this.layout.innerCt;
24875         
24876         //normal items will be rendered to the innerCt. beforeCt and afterCt allow for fixed positioning of
24877         //special items such as scrollers or dropdown menu triggers
24878         if (!this.beforeCt) {
24879             this.afterCt  = target.insertSibling({cls: this.afterCls},  'before');
24880             this.beforeCt = target.insertSibling({cls: this.beforeCls}, 'before');
24881             
24882             this.createWheelListener();
24883         }
24884     },
24885     
24886     /**
24887      * @private
24888      * Scrolls to the given position. Performs bounds checking.
24889      * @param {Number} position The position to scroll to. This is constrained.
24890      * @param {Boolean} animate True to animate. If undefined, falls back to value of this.animateScroll
24891      */
24892     scrollTo: function(position, animate) {
24893         var oldPosition = this.getScrollPosition(),
24894             newPosition = position.constrain(0, this.getMaxScrollRight());
24895         
24896         if (newPosition != oldPosition && !this.scrolling) {
24897             if (animate == undefined) {
24898                 animate = this.animateScroll;
24899             }
24900             
24901             this.layout.innerCt.scrollTo('left', newPosition, animate ? this.getScrollAnim() : false);
24902             
24903             if (animate) {
24904                 this.scrolling = true;
24905             } else {
24906                 this.scrolling = false;
24907                 this.updateScrollButtons();
24908             }
24909         }
24910     },
24911     
24912     /**
24913      * Returns the current scroll position of the innerCt element
24914      * @return {Number} The current scroll position
24915      */
24916     getScrollPosition: function(){
24917         return parseInt(this.layout.innerCt.dom.scrollLeft, 10) || 0;
24918     },
24919     
24920     /**
24921      * @private
24922      * Returns the maximum value we can scrollTo
24923      * @return {Number} The max scroll value
24924      */
24925     getMaxScrollRight: function() {
24926         return this.layout.innerCt.dom.scrollWidth - this.layout.innerCt.getWidth();
24927     },
24928     
24929     /**
24930      * @private
24931      * Returns true if the innerCt scroll is already at its right-most point
24932      * @return {Boolean} True if already at furthest right point
24933      */
24934     atExtremeAfter: function() {
24935         return this.getScrollPosition() >= this.getMaxScrollRight();
24936     }
24937 });
24938
24939 Ext.layout.boxOverflow.scroller.hbox = Ext.layout.boxOverflow.HorizontalScroller;/**
24940  * @class Ext.layout.HBoxLayout
24941  * @extends Ext.layout.BoxLayout
24942  * <p>A layout that arranges items horizontally across a Container. This layout optionally divides available horizontal
24943  * space between child items containing a numeric <code>flex</code> configuration.</p>
24944  * This layout may also be used to set the heights of child items by configuring it with the {@link #align} option.
24945  */
24946 Ext.layout.HBoxLayout = Ext.extend(Ext.layout.BoxLayout, {
24947     /**
24948      * @cfg {String} align
24949      * Controls how the child items of the container are aligned. Acceptable configuration values for this
24950      * property are:
24951      * <div class="mdetail-params"><ul>
24952      * <li><b><tt>top</tt></b> : <b>Default</b><div class="sub-desc">child items are aligned vertically
24953      * at the <b>top</b> of the container</div></li>
24954      * <li><b><tt>middle</tt></b> : <div class="sub-desc">child items are aligned vertically in the
24955      * <b>middle</b> of the container</div></li>
24956      * <li><b><tt>stretch</tt></b> : <div class="sub-desc">child items are stretched vertically to fill
24957      * the height of the container</div></li>
24958      * <li><b><tt>stretchmax</tt></b> : <div class="sub-desc">child items are stretched vertically to
24959      * the height of the largest item.</div></li>
24960      */
24961     align: 'top', // top, middle, stretch, strechmax
24962
24963     type : 'hbox',
24964
24965     /**
24966      * @cfg {String} pack
24967      * Controls how the child items of the container are packed together. Acceptable configuration values
24968      * for this property are:
24969      * <div class="mdetail-params"><ul>
24970      * <li><b><tt>start</tt></b> : <b>Default</b><div class="sub-desc">child items are packed together at
24971      * <b>left</b> side of container</div></li>
24972      * <li><b><tt>center</tt></b> : <div class="sub-desc">child items are packed together at
24973      * <b>mid-width</b> of container</div></li>
24974      * <li><b><tt>end</tt></b> : <div class="sub-desc">child items are packed together at <b>right</b>
24975      * side of container</div></li>
24976      * </ul></div>
24977      */
24978     /**
24979      * @cfg {Number} flex
24980      * This configuation option is to be applied to <b>child <tt>items</tt></b> of the container managed
24981      * by this layout. Each child item with a <tt>flex</tt> property will be flexed <b>horizontally</b>
24982      * according to each item's <b>relative</b> <tt>flex</tt> value compared to the sum of all items with
24983      * a <tt>flex</tt> value specified.  Any child items that have either a <tt>flex = 0</tt> or
24984      * <tt>flex = undefined</tt> will not be 'flexed' (the initial size will not be changed).
24985      */
24986
24987     /**
24988      * @private
24989      * Calculates the size and positioning of each item in the HBox. This iterates over all of the rendered,
24990      * visible items and returns a height, width, top and left for each, as well as a reference to each. Also
24991      * returns meta data such as maxHeight which are useful when resizing layout wrappers such as this.innerCt.
24992      * @param {Array} visibleItems The array of all rendered, visible items to be calculated for
24993      * @param {Object} targetSize Object containing target size and height
24994      * @return {Object} Object containing box measurements for each child, plus meta data
24995      */
24996     calculateChildBoxes: function(visibleItems, targetSize) {
24997         var visibleCount = visibleItems.length,
24998
24999             padding      = this.padding,
25000             topOffset    = padding.top,
25001             leftOffset   = padding.left,
25002             paddingVert  = topOffset  + padding.bottom,
25003             paddingHoriz = leftOffset + padding.right,
25004
25005             width        = targetSize.width - this.scrollOffset,
25006             height       = targetSize.height,
25007             availHeight  = Math.max(0, height - paddingVert),
25008
25009             isStart      = this.pack == 'start',
25010             isCenter     = this.pack == 'center',
25011             isEnd        = this.pack == 'end',
25012
25013             nonFlexWidth = 0,
25014             maxHeight    = 0,
25015             totalFlex    = 0,
25016             desiredWidth = 0,
25017             minimumWidth = 0,
25018
25019             //used to cache the calculated size and position values for each child item
25020             boxes        = [],
25021
25022             //used in the for loops below, just declared here for brevity
25023             child, childWidth, childHeight, childSize, childMargins, canLayout, i, calcs, flexedWidth, 
25024             horizMargins, vertMargins, stretchHeight;
25025
25026         //gather the total flex of all flexed items and the width taken up by fixed width items
25027         for (i = 0; i < visibleCount; i++) {
25028             child       = visibleItems[i];
25029             childHeight = child.height;
25030             childWidth  = child.width;
25031             canLayout   = !child.hasLayout && typeof child.doLayout == 'function';
25032
25033             // Static width (numeric) requires no calcs
25034             if (typeof childWidth != 'number') {
25035
25036                 // flex and not 'auto' width
25037                 if (child.flex && !childWidth) {
25038                     totalFlex += child.flex;
25039
25040                 // Not flexed or 'auto' width or undefined width
25041                 } else {
25042                     //Render and layout sub-containers without a flex or width defined, as otherwise we
25043                     //don't know how wide the sub-container should be and cannot calculate flexed widths
25044                     if (!childWidth && canLayout) {
25045                         child.doLayout();
25046                     }
25047
25048                     childSize   = child.getSize();
25049                     childWidth  = childSize.width;
25050                     childHeight = childSize.height;
25051                 }
25052             }
25053
25054             childMargins = child.margins;
25055             horizMargins = childMargins.left + childMargins.right;
25056
25057             nonFlexWidth += horizMargins + (childWidth || 0);
25058             desiredWidth += horizMargins + (child.flex ? child.minWidth || 0 : childWidth);
25059             minimumWidth += horizMargins + (child.minWidth || childWidth || 0);
25060
25061             // Max height for align - force layout of non-laid out subcontainers without a numeric height
25062             if (typeof childHeight != 'number') {
25063                 if (canLayout) {
25064                     child.doLayout();
25065                 }
25066                 childHeight = child.getHeight();
25067             }
25068
25069             maxHeight = Math.max(maxHeight, childHeight + childMargins.top + childMargins.bottom);
25070
25071             //cache the size of each child component. Don't set height or width to 0, keep undefined instead
25072             boxes.push({
25073                 component: child,
25074                 height   : childHeight || undefined,
25075                 width    : childWidth  || undefined
25076             });
25077         }
25078                 
25079         var shortfall = desiredWidth - width,
25080             tooNarrow = minimumWidth > width;
25081             
25082         //the width available to the flexed items
25083         var availableWidth = Math.max(0, width - nonFlexWidth - paddingHoriz);
25084         
25085         if (tooNarrow) {
25086             for (i = 0; i < visibleCount; i++) {
25087                 boxes[i].width = visibleItems[i].minWidth || visibleItems[i].width || boxes[i].width;
25088             }
25089         } else {
25090             //all flexed items should be sized to their minimum width, other items should be shrunk down until
25091             //the shortfall has been accounted for
25092             if (shortfall > 0) {
25093                 var minWidths = [];
25094                 
25095                 /**
25096                  * When we have a shortfall but are not tooNarrow, we need to shrink the width of each non-flexed item.
25097                  * Flexed items are immediately reduced to their minWidth and anything already at minWidth is ignored.
25098                  * The remaining items are collected into the minWidths array, which is later used to distribute the shortfall.
25099                  */
25100                 for (var index = 0, length = visibleCount; index < length; index++) {
25101                     var item     = visibleItems[index],
25102                         minWidth = item.minWidth || 0;
25103
25104                     //shrink each non-flex tab by an equal amount to make them all fit. Flexed items are all
25105                     //shrunk to their minWidth because they're flexible and should be the first to lose width
25106                     if (item.flex) {
25107                         boxes[index].width = minWidth;
25108                     } else {
25109                         minWidths.push({
25110                             minWidth : minWidth,
25111                             available: boxes[index].width - minWidth,
25112                             index    : index
25113                         });
25114                     }
25115                 }
25116                 
25117                 //sort by descending amount of width remaining before minWidth is reached
25118                 minWidths.sort(function(a, b) {
25119                     return a.available > b.available ? 1 : -1;
25120                 });
25121                 
25122                 /*
25123                  * Distribute the shortfall (difference between total desired with of all items and actual width available)
25124                  * between the non-flexed items. We try to distribute the shortfall evenly, but apply it to items with the
25125                  * smallest difference between their width and minWidth first, so that if reducing the width by the average
25126                  * amount would make that item less than its minWidth, we carry the remainder over to the next item.
25127                  */
25128                 for (var i = 0, length = minWidths.length; i < length; i++) {
25129                     var itemIndex = minWidths[i].index;
25130                     
25131                     if (itemIndex == undefined) {
25132                         continue;
25133                     }
25134                         
25135                     var item      = visibleItems[itemIndex],
25136                         box       = boxes[itemIndex],
25137                         oldWidth  = box.width,
25138                         minWidth  = item.minWidth,
25139                         newWidth  = Math.max(minWidth, oldWidth - Math.ceil(shortfall / (length - i))),
25140                         reduction = oldWidth - newWidth;
25141                     
25142                     boxes[itemIndex].width = newWidth;
25143                     shortfall -= reduction;                    
25144                 }
25145             } else {
25146                 //temporary variables used in the flex width calculations below
25147                 var remainingWidth = availableWidth,
25148                     remainingFlex  = totalFlex;
25149
25150                 //calculate the widths of each flexed item
25151                 for (i = 0; i < visibleCount; i++) {
25152                     child = visibleItems[i];
25153                     calcs = boxes[i];
25154
25155                     childMargins = child.margins;
25156                     vertMargins  = childMargins.top + childMargins.bottom;
25157
25158                     if (isStart && child.flex && !child.width) {
25159                         flexedWidth     = Math.ceil((child.flex / remainingFlex) * remainingWidth);
25160                         remainingWidth -= flexedWidth;
25161                         remainingFlex  -= child.flex;
25162
25163                         calcs.width = flexedWidth;
25164                         calcs.dirtySize = true;
25165                     }
25166                 }
25167             }
25168         }
25169         
25170         if (isCenter) {
25171             leftOffset += availableWidth / 2;
25172         } else if (isEnd) {
25173             leftOffset += availableWidth;
25174         }
25175         
25176         //finally, calculate the left and top position of each item
25177         for (i = 0; i < visibleCount; i++) {
25178             child = visibleItems[i];
25179             calcs = boxes[i];
25180             
25181             childMargins = child.margins;
25182             leftOffset  += childMargins.left;
25183             vertMargins  = childMargins.top + childMargins.bottom;
25184             
25185             calcs.left = leftOffset;
25186             calcs.top  = topOffset + childMargins.top;
25187
25188             switch (this.align) {
25189                 case 'stretch':
25190                     stretchHeight = availHeight - vertMargins;
25191                     calcs.height  = stretchHeight.constrain(child.minHeight || 0, child.maxHeight || 1000000);
25192                     calcs.dirtySize = true;
25193                     break;
25194                 case 'stretchmax':
25195                     stretchHeight = maxHeight - vertMargins;
25196                     calcs.height  = stretchHeight.constrain(child.minHeight || 0, child.maxHeight || 1000000);
25197                     calcs.dirtySize = true;
25198                     break;
25199                 case 'middle':
25200                     var diff = availHeight - calcs.height - vertMargins;
25201                     if (diff > 0) {
25202                         calcs.top = topOffset + vertMargins + (diff / 2);
25203                     }
25204             }
25205             
25206             leftOffset += calcs.width + childMargins.right;
25207         }
25208
25209         return {
25210             boxes: boxes,
25211             meta : {
25212                 maxHeight   : maxHeight,
25213                 nonFlexWidth: nonFlexWidth,
25214                 desiredWidth: desiredWidth,
25215                 minimumWidth: minimumWidth,
25216                 shortfall   : desiredWidth - width,
25217                 tooNarrow   : tooNarrow
25218             }
25219         };
25220     }
25221 });
25222
25223 Ext.Container.LAYOUTS.hbox = Ext.layout.HBoxLayout;/**
25224  * @class Ext.layout.VBoxLayout
25225  * @extends Ext.layout.BoxLayout
25226  * <p>A layout that arranges items vertically down a Container. This layout optionally divides available vertical
25227  * space between child items containing a numeric <code>flex</code> configuration.</p>
25228  * This layout may also be used to set the widths of child items by configuring it with the {@link #align} option.
25229  */
25230 Ext.layout.VBoxLayout = Ext.extend(Ext.layout.BoxLayout, {
25231     /**
25232      * @cfg {String} align
25233      * Controls how the child items of the container are aligned. Acceptable configuration values for this
25234      * property are:
25235      * <div class="mdetail-params"><ul>
25236      * <li><b><tt>left</tt></b> : <b>Default</b><div class="sub-desc">child items are aligned horizontally
25237      * at the <b>left</b> side of the container</div></li>
25238      * <li><b><tt>center</tt></b> : <div class="sub-desc">child items are aligned horizontally at the
25239      * <b>mid-width</b> of the container</div></li>
25240      * <li><b><tt>stretch</tt></b> : <div class="sub-desc">child items are stretched horizontally to fill
25241      * the width of the container</div></li>
25242      * <li><b><tt>stretchmax</tt></b> : <div class="sub-desc">child items are stretched horizontally to
25243      * the size of the largest item.</div></li>
25244      * </ul></div>
25245      */
25246     align : 'left', // left, center, stretch, strechmax
25247     type: 'vbox',
25248
25249     /**
25250      * @cfg {String} pack
25251      * Controls how the child items of the container are packed together. Acceptable configuration values
25252      * for this property are:
25253      * <div class="mdetail-params"><ul>
25254      * <li><b><tt>start</tt></b> : <b>Default</b><div class="sub-desc">child items are packed together at
25255      * <b>top</b> side of container</div></li>
25256      * <li><b><tt>center</tt></b> : <div class="sub-desc">child items are packed together at
25257      * <b>mid-height</b> of container</div></li>
25258      * <li><b><tt>end</tt></b> : <div class="sub-desc">child items are packed together at <b>bottom</b>
25259      * side of container</div></li>
25260      * </ul></div>
25261      */
25262
25263     /**
25264      * @cfg {Number} flex
25265      * This configuation option is to be applied to <b>child <tt>items</tt></b> of the container managed
25266      * by this layout. Each child item with a <tt>flex</tt> property will be flexed <b>vertically</b>
25267      * according to each item's <b>relative</b> <tt>flex</tt> value compared to the sum of all items with
25268      * a <tt>flex</tt> value specified.  Any child items that have either a <tt>flex = 0</tt> or
25269      * <tt>flex = undefined</tt> will not be 'flexed' (the initial size will not be changed).
25270      */
25271
25272     /**
25273      * @private
25274      * Calculates the size and positioning of each item in the VBox. This iterates over all of the rendered,
25275      * visible items and returns a height, width, top and left for each, as well as a reference to each. Also
25276      * returns meta data such as maxHeight which are useful when resizing layout wrappers such as this.innerCt.
25277      * @param {Array} visibleItems The array of all rendered, visible items to be calculated for
25278      * @param {Object} targetSize Object containing target size and height
25279      * @return {Object} Object containing box measurements for each child, plus meta data
25280      */
25281     calculateChildBoxes: function(visibleItems, targetSize) {
25282         var visibleCount = visibleItems.length,
25283
25284             padding      = this.padding,
25285             topOffset    = padding.top,
25286             leftOffset   = padding.left,
25287             paddingVert  = topOffset  + padding.bottom,
25288             paddingHoriz = leftOffset + padding.right,
25289
25290             width        = targetSize.width - this.scrollOffset,
25291             height       = targetSize.height,
25292             availWidth   = Math.max(0, width - paddingHoriz),
25293
25294             isStart      = this.pack == 'start',
25295             isCenter     = this.pack == 'center',
25296             isEnd        = this.pack == 'end',
25297
25298             nonFlexHeight= 0,
25299             maxWidth     = 0,
25300             totalFlex    = 0,
25301             desiredHeight= 0,
25302             minimumHeight= 0,
25303
25304             //used to cache the calculated size and position values for each child item
25305             boxes        = [],
25306             
25307             //used in the for loops below, just declared here for brevity
25308             child, childWidth, childHeight, childSize, childMargins, canLayout, i, calcs, flexedWidth, 
25309             horizMargins, vertMargins, stretchWidth;
25310
25311         //gather the total flex of all flexed items and the width taken up by fixed width items
25312         for (i = 0; i < visibleCount; i++) {
25313             child = visibleItems[i];
25314             childHeight = child.height;
25315             childWidth  = child.width;
25316             canLayout   = !child.hasLayout && typeof child.doLayout == 'function';
25317
25318             // Static height (numeric) requires no calcs
25319             if (typeof childHeight != 'number') {
25320
25321                 // flex and not 'auto' height
25322                 if (child.flex && !childHeight) {
25323                     totalFlex += child.flex;
25324
25325                 // Not flexed or 'auto' height or undefined height
25326                 } else {
25327                     //Render and layout sub-containers without a flex or width defined, as otherwise we
25328                     //don't know how wide the sub-container should be and cannot calculate flexed widths
25329                     if (!childHeight && canLayout) {
25330                         child.doLayout();
25331                     }
25332
25333                     childSize = child.getSize();
25334                     childWidth = childSize.width;
25335                     childHeight = childSize.height;
25336                 }
25337             }
25338             
25339             childMargins = child.margins;
25340             vertMargins  = childMargins.top + childMargins.bottom;
25341
25342             nonFlexHeight += vertMargins + (childHeight || 0);
25343             desiredHeight += vertMargins + (child.flex ? child.minHeight || 0 : childHeight);
25344             minimumHeight += vertMargins + (child.minHeight || childHeight || 0);
25345
25346             // Max width for align - force layout of non-layed out subcontainers without a numeric width
25347             if (typeof childWidth != 'number') {
25348                 if (canLayout) {
25349                     child.doLayout();
25350                 }
25351                 childWidth = child.getWidth();
25352             }
25353
25354             maxWidth = Math.max(maxWidth, childWidth + childMargins.left + childMargins.right);
25355
25356             //cache the size of each child component
25357             boxes.push({
25358                 component: child,
25359                 height   : childHeight || undefined,
25360                 width    : childWidth || undefined
25361             });
25362         }
25363                 
25364         var shortfall = desiredHeight - height,
25365             tooNarrow = minimumHeight > height;
25366
25367         //the height available to the flexed items
25368         var availableHeight = Math.max(0, (height - nonFlexHeight - paddingVert));
25369         
25370         if (tooNarrow) {
25371             for (i = 0, length = visibleCount; i < length; i++) {
25372                 boxes[i].height = visibleItems[i].minHeight || visibleItems[i].height || boxes[i].height;
25373             }
25374         } else {
25375             //all flexed items should be sized to their minimum width, other items should be shrunk down until
25376             //the shortfall has been accounted for
25377             if (shortfall > 0) {
25378                 var minHeights = [];
25379
25380                 /**
25381                  * When we have a shortfall but are not tooNarrow, we need to shrink the height of each non-flexed item.
25382                  * Flexed items are immediately reduced to their minHeight and anything already at minHeight is ignored.
25383                  * The remaining items are collected into the minHeights array, which is later used to distribute the shortfall.
25384                  */
25385                 for (var index = 0, length = visibleCount; index < length; index++) {
25386                     var item      = visibleItems[index],
25387                         minHeight = item.minHeight || 0;
25388
25389                     //shrink each non-flex tab by an equal amount to make them all fit. Flexed items are all
25390                     //shrunk to their minHeight because they're flexible and should be the first to lose height
25391                     if (item.flex) {
25392                         boxes[index].height = minHeight;
25393                     } else {
25394                         minHeights.push({
25395                             minHeight: minHeight, 
25396                             available: boxes[index].height - minHeight,
25397                             index    : index
25398                         });
25399                     }
25400                 }
25401
25402                 //sort by descending minHeight value
25403                 minHeights.sort(function(a, b) {
25404                     return a.available > b.available ? 1 : -1;
25405                 });
25406
25407                 /*
25408                  * Distribute the shortfall (difference between total desired with of all items and actual height available)
25409                  * between the non-flexed items. We try to distribute the shortfall evenly, but apply it to items with the
25410                  * smallest difference between their height and minHeight first, so that if reducing the height by the average
25411                  * amount would make that item less than its minHeight, we carry the remainder over to the next item.
25412                  */
25413                 for (var i = 0, length = minHeights.length; i < length; i++) {
25414                     var itemIndex = minHeights[i].index;
25415
25416                     if (itemIndex == undefined) {
25417                         continue;
25418                     }
25419
25420                     var item      = visibleItems[itemIndex],
25421                         box       = boxes[itemIndex],
25422                         oldHeight  = box.height,
25423                         minHeight  = item.minHeight,
25424                         newHeight  = Math.max(minHeight, oldHeight - Math.ceil(shortfall / (length - i))),
25425                         reduction = oldHeight - newHeight;
25426
25427                     boxes[itemIndex].height = newHeight;
25428                     shortfall -= reduction;
25429                 }
25430             } else {
25431                 //temporary variables used in the flex height calculations below
25432                 var remainingHeight = availableHeight,
25433                     remainingFlex   = totalFlex;
25434                 
25435                 //calculate the height of each flexed item
25436                 for (i = 0; i < visibleCount; i++) {
25437                     child = visibleItems[i];
25438                     calcs = boxes[i];
25439
25440                     childMargins = child.margins;
25441                     horizMargins = childMargins.left + childMargins.right;
25442
25443                     if (isStart && child.flex && !child.height) {
25444                         flexedHeight     = Math.ceil((child.flex / remainingFlex) * remainingHeight);
25445                         remainingHeight -= flexedHeight;
25446                         remainingFlex   -= child.flex;
25447
25448                         calcs.height = flexedHeight;
25449                         calcs.dirtySize = true;
25450                     }
25451                 }
25452             }
25453         }
25454
25455         if (isCenter) {
25456             topOffset += availableHeight / 2;
25457         } else if (isEnd) {
25458             topOffset += availableHeight;
25459         }
25460
25461         //finally, calculate the left and top position of each item
25462         for (i = 0; i < visibleCount; i++) {
25463             child = visibleItems[i];
25464             calcs = boxes[i];
25465
25466             childMargins = child.margins;
25467             topOffset   += childMargins.top;
25468             horizMargins = childMargins.left + childMargins.right;
25469             
25470
25471             calcs.left = leftOffset + childMargins.left;
25472             calcs.top  = topOffset;
25473             
25474             switch (this.align) {
25475                 case 'stretch':
25476                     stretchWidth = availWidth - horizMargins;
25477                     calcs.width  = stretchWidth.constrain(child.minWidth || 0, child.maxWidth || 1000000);
25478                     calcs.dirtySize = true;
25479                     break;
25480                 case 'stretchmax':
25481                     stretchWidth = maxWidth - horizMargins;
25482                     calcs.width  = stretchWidth.constrain(child.minWidth || 0, child.maxWidth || 1000000);
25483                     calcs.dirtySize = true;
25484                     break;
25485                 case 'center':
25486                     var diff = availWidth - calcs.width - horizMargins;
25487                     if (diff > 0) {
25488                         calcs.left = leftOffset + horizMargins + (diff / 2);
25489                     }
25490             }
25491
25492             topOffset += calcs.height + childMargins.bottom;
25493         }
25494         
25495         return {
25496             boxes: boxes,
25497             meta : {
25498                 maxWidth     : maxWidth,
25499                 nonFlexHeight: nonFlexHeight,
25500                 desiredHeight: desiredHeight,
25501                 minimumHeight: minimumHeight,
25502                 shortfall    : desiredHeight - height,
25503                 tooNarrow    : tooNarrow
25504             }
25505         };
25506     }
25507 });
25508
25509 Ext.Container.LAYOUTS.vbox = Ext.layout.VBoxLayout;
25510 /**
25511  * @class Ext.layout.ToolbarLayout
25512  * @extends Ext.layout.ContainerLayout
25513  * Layout manager used by Ext.Toolbar. This is highly specialised for use by Toolbars and would not
25514  * usually be used by any other class.
25515  */
25516 Ext.layout.ToolbarLayout = Ext.extend(Ext.layout.ContainerLayout, {
25517     monitorResize : true,
25518
25519     type: 'toolbar',
25520
25521     /**
25522      * @property triggerWidth
25523      * @type Number
25524      * The width allocated for the menu trigger at the extreme right end of the Toolbar
25525      */
25526     triggerWidth: 18,
25527
25528     /**
25529      * @property noItemsMenuText
25530      * @type String
25531      * HTML fragment to render into the toolbar overflow menu if there are no items to display
25532      */
25533     noItemsMenuText : '<div class="x-toolbar-no-items">(None)</div>',
25534
25535     /**
25536      * @private
25537      * @property lastOverflow
25538      * @type Boolean
25539      * Used internally to record whether the last layout caused an overflow or not
25540      */
25541     lastOverflow: false,
25542
25543     /**
25544      * @private
25545      * @property tableHTML
25546      * @type String
25547      * String used to build the HTML injected to support the Toolbar's layout. The align property is
25548      * injected into this string inside the td.x-toolbar-left element during onLayout.
25549      */
25550     tableHTML: [
25551         '<table cellspacing="0" class="x-toolbar-ct">',
25552             '<tbody>',
25553                 '<tr>',
25554                     '<td class="x-toolbar-left" align="{0}">',
25555                         '<table cellspacing="0">',
25556                             '<tbody>',
25557                                 '<tr class="x-toolbar-left-row"></tr>',
25558                             '</tbody>',
25559                         '</table>',
25560                     '</td>',
25561                     '<td class="x-toolbar-right" align="right">',
25562                         '<table cellspacing="0" class="x-toolbar-right-ct">',
25563                             '<tbody>',
25564                                 '<tr>',
25565                                     '<td>',
25566                                         '<table cellspacing="0">',
25567                                             '<tbody>',
25568                                                 '<tr class="x-toolbar-right-row"></tr>',
25569                                             '</tbody>',
25570                                         '</table>',
25571                                     '</td>',
25572                                     '<td>',
25573                                         '<table cellspacing="0">',
25574                                             '<tbody>',
25575                                                 '<tr class="x-toolbar-extras-row"></tr>',
25576                                             '</tbody>',
25577                                         '</table>',
25578                                     '</td>',
25579                                 '</tr>',
25580                             '</tbody>',
25581                         '</table>',
25582                     '</td>',
25583                 '</tr>',
25584             '</tbody>',
25585         '</table>'
25586     ].join(""),
25587
25588     /**
25589      * @private
25590      * Create the wrapping Toolbar HTML and render/move all the items into the correct places
25591      */
25592     onLayout : function(ct, target) {
25593         //render the Toolbar <table> HTML if it's not already present
25594         if (!this.leftTr) {
25595             var align = ct.buttonAlign == 'center' ? 'center' : 'left';
25596
25597             target.addClass('x-toolbar-layout-ct');
25598             target.insertHtml('beforeEnd', String.format(this.tableHTML, align));
25599
25600             this.leftTr   = target.child('tr.x-toolbar-left-row', true);
25601             this.rightTr  = target.child('tr.x-toolbar-right-row', true);
25602             this.extrasTr = target.child('tr.x-toolbar-extras-row', true);
25603
25604             if (this.hiddenItem == undefined) {
25605                 /**
25606                  * @property hiddenItems
25607                  * @type Array
25608                  * Holds all items that are currently hidden due to there not being enough space to render them
25609                  * These items will appear on the expand menu.
25610                  */
25611                 this.hiddenItems = [];
25612             }
25613         }
25614
25615         var side     = ct.buttonAlign == 'right' ? this.rightTr : this.leftTr,
25616             items    = ct.items.items,
25617             position = 0;
25618
25619         //render each item if not already rendered, place it into the correct (left or right) target
25620         for (var i = 0, len = items.length, c; i < len; i++, position++) {
25621             c = items[i];
25622
25623             if (c.isFill) {
25624                 side   = this.rightTr;
25625                 position = -1;
25626             } else if (!c.rendered) {
25627                 c.render(this.insertCell(c, side, position));
25628                 this.configureItem(c);
25629             } else {
25630                 if (!c.xtbHidden && !this.isValidParent(c, side.childNodes[position])) {
25631                     var td = this.insertCell(c, side, position);
25632                     td.appendChild(c.getPositionEl().dom);
25633                     c.container = Ext.get(td);
25634                 }
25635             }
25636         }
25637
25638         //strip extra empty cells
25639         this.cleanup(this.leftTr);
25640         this.cleanup(this.rightTr);
25641         this.cleanup(this.extrasTr);
25642         this.fitToSize(target);
25643     },
25644
25645     /**
25646      * @private
25647      * Removes any empty nodes from the given element
25648      * @param {Ext.Element} el The element to clean up
25649      */
25650     cleanup : function(el) {
25651         var cn = el.childNodes, i, c;
25652
25653         for (i = cn.length-1; i >= 0 && (c = cn[i]); i--) {
25654             if (!c.firstChild) {
25655                 el.removeChild(c);
25656             }
25657         }
25658     },
25659
25660     /**
25661      * @private
25662      * Inserts the given Toolbar item into the given element
25663      * @param {Ext.Component} c The component to add
25664      * @param {Ext.Element} target The target to add the component to
25665      * @param {Number} position The position to add the component at
25666      */
25667     insertCell : function(c, target, position) {
25668         var td = document.createElement('td');
25669         td.className = 'x-toolbar-cell';
25670
25671         target.insertBefore(td, target.childNodes[position] || null);
25672
25673         return td;
25674     },
25675
25676     /**
25677      * @private
25678      * Hides an item because it will not fit in the available width. The item will be unhidden again
25679      * if the Toolbar is resized to be large enough to show it
25680      * @param {Ext.Component} item The item to hide
25681      */
25682     hideItem : function(item) {
25683         this.hiddenItems.push(item);
25684
25685         item.xtbHidden = true;
25686         item.xtbWidth = item.getPositionEl().dom.parentNode.offsetWidth;
25687         item.hide();
25688     },
25689
25690     /**
25691      * @private
25692      * Unhides an item that was previously hidden due to there not being enough space left on the Toolbar
25693      * @param {Ext.Component} item The item to show
25694      */
25695     unhideItem : function(item) {
25696         item.show();
25697         item.xtbHidden = false;
25698         this.hiddenItems.remove(item);
25699     },
25700
25701     /**
25702      * @private
25703      * Returns the width of the given toolbar item. If the item is currently hidden because there
25704      * is not enough room to render it, its previous width is returned
25705      * @param {Ext.Component} c The component to measure
25706      * @return {Number} The width of the item
25707      */
25708     getItemWidth : function(c) {
25709         return c.hidden ? (c.xtbWidth || 0) : c.getPositionEl().dom.parentNode.offsetWidth;
25710     },
25711
25712     /**
25713      * @private
25714      * Called at the end of onLayout. At this point the Toolbar has already been resized, so we need
25715      * to fit the items into the available width. We add up the width required by all of the items in
25716      * the toolbar - if we don't have enough space we hide the extra items and render the expand menu
25717      * trigger.
25718      * @param {Ext.Element} target The Element the Toolbar is currently laid out within
25719      */
25720     fitToSize : function(target) {
25721         if (this.container.enableOverflow === false) {
25722             return;
25723         }
25724
25725         var width       = target.dom.clientWidth,
25726             tableWidth  = target.dom.firstChild.offsetWidth,
25727             clipWidth   = width - this.triggerWidth,
25728             lastWidth   = this.lastWidth || 0,
25729
25730             hiddenItems = this.hiddenItems,
25731             hasHiddens  = hiddenItems.length != 0,
25732             isLarger    = width >= lastWidth;
25733
25734         this.lastWidth  = width;
25735
25736         if (tableWidth > width || (hasHiddens && isLarger)) {
25737             var items     = this.container.items.items,
25738                 len       = items.length,
25739                 loopWidth = 0,
25740                 item;
25741
25742             for (var i = 0; i < len; i++) {
25743                 item = items[i];
25744
25745                 if (!item.isFill) {
25746                     loopWidth += this.getItemWidth(item);
25747                     if (loopWidth > clipWidth) {
25748                         if (!(item.hidden || item.xtbHidden)) {
25749                             this.hideItem(item);
25750                         }
25751                     } else if (item.xtbHidden) {
25752                         this.unhideItem(item);
25753                     }
25754                 }
25755             }
25756         }
25757
25758         //test for number of hidden items again here because they may have changed above
25759         hasHiddens = hiddenItems.length != 0;
25760
25761         if (hasHiddens) {
25762             this.initMore();
25763
25764             if (!this.lastOverflow) {
25765                 this.container.fireEvent('overflowchange', this.container, true);
25766                 this.lastOverflow = true;
25767             }
25768         } else if (this.more) {
25769             this.clearMenu();
25770             this.more.destroy();
25771             delete this.more;
25772
25773             if (this.lastOverflow) {
25774                 this.container.fireEvent('overflowchange', this.container, false);
25775                 this.lastOverflow = false;
25776             }
25777         }
25778     },
25779
25780     /**
25781      * @private
25782      * Returns a menu config for a given component. This config is used to create a menu item
25783      * to be added to the expander menu
25784      * @param {Ext.Component} component The component to create the config for
25785      * @param {Boolean} hideOnClick Passed through to the menu item
25786      */
25787     createMenuConfig : function(component, hideOnClick){
25788         var config = Ext.apply({}, component.initialConfig),
25789             group  = component.toggleGroup;
25790
25791         Ext.copyTo(config, component, [
25792             'iconCls', 'icon', 'itemId', 'disabled', 'handler', 'scope', 'menu'
25793         ]);
25794
25795         Ext.apply(config, {
25796             text       : component.overflowText || component.text,
25797             hideOnClick: hideOnClick
25798         });
25799
25800         if (group || component.enableToggle) {
25801             Ext.apply(config, {
25802                 group  : group,
25803                 checked: component.pressed,
25804                 listeners: {
25805                     checkchange: function(item, checked){
25806                         component.toggle(checked);
25807                     }
25808                 }
25809             });
25810         }
25811
25812         delete config.ownerCt;
25813         delete config.xtype;
25814         delete config.id;
25815
25816         return config;
25817     },
25818
25819     /**
25820      * @private
25821      * Adds the given Toolbar item to the given menu. Buttons inside a buttongroup are added individually.
25822      * @param {Ext.menu.Menu} menu The menu to add to
25823      * @param {Ext.Component} component The component to add
25824      */
25825     addComponentToMenu : function(menu, component) {
25826         if (component instanceof Ext.Toolbar.Separator) {
25827             menu.add('-');
25828
25829         } else if (Ext.isFunction(component.isXType)) {
25830             if (component.isXType('splitbutton')) {
25831                 menu.add(this.createMenuConfig(component, true));
25832
25833             } else if (component.isXType('button')) {
25834                 menu.add(this.createMenuConfig(component, !component.menu));
25835
25836             } else if (component.isXType('buttongroup')) {
25837                 component.items.each(function(item){
25838                      this.addComponentToMenu(menu, item);
25839                 }, this);
25840             }
25841         }
25842     },
25843
25844     /**
25845      * @private
25846      * Deletes the sub-menu of each item in the expander menu. Submenus are created for items such as
25847      * splitbuttons and buttongroups, where the Toolbar item cannot be represented by a single menu item
25848      */
25849     clearMenu : function(){
25850         var menu = this.moreMenu;
25851         if (menu && menu.items) {
25852             menu.items.each(function(item){
25853                 delete item.menu;
25854             });
25855         }
25856     },
25857
25858     /**
25859      * @private
25860      * Called before the expand menu is shown, this rebuilds the menu since it was last shown because
25861      * it is possible that the items hidden due to space limitations on the Toolbar have changed since.
25862      * @param {Ext.menu.Menu} m The menu
25863      */
25864     beforeMoreShow : function(menu) {
25865         var items = this.container.items.items,
25866             len   = items.length,
25867             item,
25868             prev;
25869
25870         var needsSep = function(group, item){
25871             return group.isXType('buttongroup') && !(item instanceof Ext.Toolbar.Separator);
25872         };
25873
25874         this.clearMenu();
25875         menu.removeAll();
25876         for (var i = 0; i < len; i++) {
25877             item = items[i];
25878             if (item.xtbHidden) {
25879                 if (prev && (needsSep(item, prev) || needsSep(prev, item))) {
25880                     menu.add('-');
25881                 }
25882                 this.addComponentToMenu(menu, item);
25883                 prev = item;
25884             }
25885         }
25886
25887         // put something so the menu isn't empty if no compatible items found
25888         if (menu.items.length < 1) {
25889             menu.add(this.noItemsMenuText);
25890         }
25891     },
25892
25893     /**
25894      * @private
25895      * Creates the expand trigger and menu, adding them to the <tr> at the extreme right of the
25896      * Toolbar table
25897      */
25898     initMore : function(){
25899         if (!this.more) {
25900             /**
25901              * @private
25902              * @property moreMenu
25903              * @type Ext.menu.Menu
25904              * The expand menu - holds items for every Toolbar item that cannot be shown
25905              * because the Toolbar is currently not wide enough.
25906              */
25907             this.moreMenu = new Ext.menu.Menu({
25908                 ownerCt : this.container,
25909                 listeners: {
25910                     beforeshow: this.beforeMoreShow,
25911                     scope: this
25912                 }
25913             });
25914
25915             /**
25916              * @private
25917              * @property more
25918              * @type Ext.Button
25919              * The expand button which triggers the overflow menu to be shown
25920              */
25921             this.more = new Ext.Button({
25922                 iconCls: 'x-toolbar-more-icon',
25923                 cls    : 'x-toolbar-more',
25924                 menu   : this.moreMenu,
25925                 ownerCt: this.container
25926             });
25927
25928             var td = this.insertCell(this.more, this.extrasTr, 100);
25929             this.more.render(td);
25930         }
25931     },
25932
25933     destroy : function(){
25934         Ext.destroy(this.more, this.moreMenu);
25935         delete this.leftTr;
25936         delete this.rightTr;
25937         delete this.extrasTr;
25938         Ext.layout.ToolbarLayout.superclass.destroy.call(this);
25939     }
25940 });
25941
25942 Ext.Container.LAYOUTS.toolbar = Ext.layout.ToolbarLayout;
25943 /**
25944  * @class Ext.layout.MenuLayout
25945  * @extends Ext.layout.ContainerLayout
25946  * <p>Layout manager used by {@link Ext.menu.Menu}. Generally this class should not need to be used directly.</p>
25947  */
25948  Ext.layout.MenuLayout = Ext.extend(Ext.layout.ContainerLayout, {
25949     monitorResize : true,
25950
25951     type: 'menu',
25952
25953     setContainer : function(ct){
25954         this.monitorResize = !ct.floating;
25955         // This event is only fired by the menu in IE, used so we don't couple
25956         // the menu with the layout.
25957         ct.on('autosize', this.doAutoSize, this);
25958         Ext.layout.MenuLayout.superclass.setContainer.call(this, ct);
25959     },
25960
25961     renderItem : function(c, position, target){
25962         if (!this.itemTpl) {
25963             this.itemTpl = Ext.layout.MenuLayout.prototype.itemTpl = new Ext.XTemplate(
25964                 '<li id="{itemId}" class="{itemCls}">',
25965                     '<tpl if="needsIcon">',
25966                         '<img alt="{altText}" src="{icon}" class="{iconCls}"/>',
25967                     '</tpl>',
25968                 '</li>'
25969             );
25970         }
25971
25972         if(c && !c.rendered){
25973             if(Ext.isNumber(position)){
25974                 position = target.dom.childNodes[position];
25975             }
25976             var a = this.getItemArgs(c);
25977
25978 //          The Component's positionEl is the <li> it is rendered into
25979             c.render(c.positionEl = position ?
25980                 this.itemTpl.insertBefore(position, a, true) :
25981                 this.itemTpl.append(target, a, true));
25982
25983 //          Link the containing <li> to the item.
25984             c.positionEl.menuItemId = c.getItemId();
25985
25986 //          If rendering a regular Component, and it needs an icon,
25987 //          move the Component rightwards.
25988             if (!a.isMenuItem && a.needsIcon) {
25989                 c.positionEl.addClass('x-menu-list-item-indent');
25990             }
25991             this.configureItem(c);
25992         }else if(c && !this.isValidParent(c, target)){
25993             if(Ext.isNumber(position)){
25994                 position = target.dom.childNodes[position];
25995             }
25996             target.dom.insertBefore(c.getActionEl().dom, position || null);
25997         }
25998     },
25999
26000     getItemArgs : function(c) {
26001         var isMenuItem = c instanceof Ext.menu.Item,
26002             canHaveIcon = !(isMenuItem || c instanceof Ext.menu.Separator);
26003
26004         return {
26005             isMenuItem: isMenuItem,
26006             needsIcon: canHaveIcon && (c.icon || c.iconCls),
26007             icon: c.icon || Ext.BLANK_IMAGE_URL,
26008             iconCls: 'x-menu-item-icon ' + (c.iconCls || ''),
26009             itemId: 'x-menu-el-' + c.id,
26010             itemCls: 'x-menu-list-item ',
26011             altText: c.altText || ''
26012         };
26013     },
26014
26015     //  Valid if the Component is in a <li> which is part of our target <ul>
26016     isValidParent : function(c, target) {
26017         return c.el.up('li.x-menu-list-item', 5).dom.parentNode === (target.dom || target);
26018     },
26019
26020     onLayout : function(ct, target){
26021         Ext.layout.MenuLayout.superclass.onLayout.call(this, ct, target);
26022         this.doAutoSize();
26023     },
26024
26025     doAutoSize : function(){
26026         var ct = this.container, w = ct.width;
26027         if(ct.floating){
26028             if(w){
26029                 ct.setWidth(w);
26030             }else if(Ext.isIE){
26031                 ct.setWidth(Ext.isStrict && (Ext.isIE7 || Ext.isIE8) ? 'auto' : ct.minWidth);
26032                 var el = ct.getEl(), t = el.dom.offsetWidth; // force recalc
26033                 ct.setWidth(ct.getLayoutTarget().getWidth() + el.getFrameWidth('lr'));
26034             }
26035         }
26036     }
26037 });
26038 Ext.Container.LAYOUTS['menu'] = Ext.layout.MenuLayout;
26039 /**
26040  * @class Ext.Viewport
26041  * @extends Ext.Container
26042  * <p>A specialized container representing the viewable application area (the browser viewport).</p>
26043  * <p>The Viewport renders itself to the document body, and automatically sizes itself to the size of
26044  * the browser viewport and manages window resizing. There may only be one Viewport created
26045  * in a page. Inner layouts are available by virtue of the fact that all {@link Ext.Panel Panel}s
26046  * added to the Viewport, either through its {@link #items}, or through the items, or the {@link #add}
26047  * method of any of its child Panels may themselves have a layout.</p>
26048  * <p>The Viewport does not provide scrolling, so child Panels within the Viewport should provide
26049  * for scrolling if needed using the {@link #autoScroll} config.</p>
26050  * <p>An example showing a classic application border layout:</p><pre><code>
26051 new Ext.Viewport({
26052     layout: 'border',
26053     items: [{
26054         region: 'north',
26055         html: '&lt;h1 class="x-panel-header">Page Title&lt;/h1>',
26056         autoHeight: true,
26057         border: false,
26058         margins: '0 0 5 0'
26059     }, {
26060         region: 'west',
26061         collapsible: true,
26062         title: 'Navigation',
26063         width: 200
26064         // the west region might typically utilize a {@link Ext.tree.TreePanel TreePanel} or a Panel with {@link Ext.layout.AccordionLayout Accordion layout}
26065     }, {
26066         region: 'south',
26067         title: 'Title for Panel',
26068         collapsible: true,
26069         html: 'Information goes here',
26070         split: true,
26071         height: 100,
26072         minHeight: 100
26073     }, {
26074         region: 'east',
26075         title: 'Title for the Grid Panel',
26076         collapsible: true,
26077         split: true,
26078         width: 200,
26079         xtype: 'grid',
26080         // remaining grid configuration not shown ...
26081         // notice that the GridPanel is added directly as the region
26082         // it is not "overnested" inside another Panel
26083     }, {
26084         region: 'center',
26085         xtype: 'tabpanel', // TabPanel itself has no title
26086         items: {
26087             title: 'Default Tab',
26088             html: 'The first tab\'s content. Others may be added dynamically'
26089         }
26090     }]
26091 });
26092 </code></pre>
26093  * @constructor
26094  * Create a new Viewport
26095  * @param {Object} config The config object
26096  * @xtype viewport
26097  */
26098 Ext.Viewport = Ext.extend(Ext.Container, {
26099     /*
26100      * Privatize config options which, if used, would interfere with the
26101      * correct operation of the Viewport as the sole manager of the
26102      * layout of the document body.
26103      */
26104     /**
26105      * @cfg {Mixed} applyTo @hide
26106      */
26107     /**
26108      * @cfg {Boolean} allowDomMove @hide
26109      */
26110     /**
26111      * @cfg {Boolean} hideParent @hide
26112      */
26113     /**
26114      * @cfg {Mixed} renderTo @hide
26115      */
26116     /**
26117      * @cfg {Boolean} hideParent @hide
26118      */
26119     /**
26120      * @cfg {Number} height @hide
26121      */
26122     /**
26123      * @cfg {Number} width @hide
26124      */
26125     /**
26126      * @cfg {Boolean} autoHeight @hide
26127      */
26128     /**
26129      * @cfg {Boolean} autoWidth @hide
26130      */
26131     /**
26132      * @cfg {Boolean} deferHeight @hide
26133      */
26134     /**
26135      * @cfg {Boolean} monitorResize @hide
26136      */
26137
26138     initComponent : function() {
26139         Ext.Viewport.superclass.initComponent.call(this);
26140         document.getElementsByTagName('html')[0].className += ' x-viewport';
26141         this.el = Ext.getBody();
26142         this.el.setHeight = Ext.emptyFn;
26143         this.el.setWidth = Ext.emptyFn;
26144         this.el.setSize = Ext.emptyFn;
26145         this.el.dom.scroll = 'no';
26146         this.allowDomMove = false;
26147         this.autoWidth = true;
26148         this.autoHeight = true;
26149         Ext.EventManager.onWindowResize(this.fireResize, this);
26150         this.renderTo = this.el;
26151     },
26152
26153     fireResize : function(w, h){
26154         this.fireEvent('resize', this, w, h, w, h);
26155     }
26156 });
26157 Ext.reg('viewport', Ext.Viewport);
26158 /**
26159  * @class Ext.Panel
26160  * @extends Ext.Container
26161  * <p>Panel is a container that has specific functionality and structural components that make
26162  * it the perfect building block for application-oriented user interfaces.</p>
26163  * <p>Panels are, by virtue of their inheritance from {@link Ext.Container}, capable
26164  * of being configured with a {@link Ext.Container#layout layout}, and containing child Components.</p>
26165  * <p>When either specifying child {@link Ext.Component#items items} of a Panel, or dynamically {@link Ext.Container#add adding} Components
26166  * to a Panel, remember to consider how you wish the Panel to arrange those child elements, and whether
26167  * those child elements need to be sized using one of Ext's built-in <code><b>{@link Ext.Container#layout layout}</b></code> schemes. By
26168  * default, Panels use the {@link Ext.layout.ContainerLayout ContainerLayout} scheme. This simply renders
26169  * child components, appending them one after the other inside the Container, and <b>does not apply any sizing</b>
26170  * at all.</p>
26171  * <p>A Panel may also contain {@link #bbar bottom} and {@link #tbar top} toolbars, along with separate
26172  * {@link #header}, {@link #footer} and {@link #body} sections (see {@link #frame} for additional
26173  * information).</p>
26174  * <p>Panel also provides built-in {@link #collapsible expandable and collapsible behavior}, along with
26175  * a variety of {@link #tools prebuilt tool buttons} that can be wired up to provide other customized
26176  * behavior.  Panels can be easily dropped into any {@link Ext.Container Container} or layout, and the
26177  * layout and rendering pipeline is {@link Ext.Container#add completely managed by the framework}.</p>
26178  * @constructor
26179  * @param {Object} config The config object
26180  * @xtype panel
26181  */
26182 Ext.Panel = Ext.extend(Ext.Container, {
26183     /**
26184      * The Panel's header {@link Ext.Element Element}. Read-only.
26185      * <p>This Element is used to house the {@link #title} and {@link #tools}</p>
26186      * <br><p><b>Note</b>: see the Note for <code>{@link Ext.Component#el el}</code> also.</p>
26187      * @type Ext.Element
26188      * @property header
26189      */
26190     /**
26191      * The Panel's body {@link Ext.Element Element} which may be used to contain HTML content.
26192      * The content may be specified in the {@link #html} config, or it may be loaded using the
26193      * {@link autoLoad} config, or through the Panel's {@link #getUpdater Updater}. Read-only.
26194      * <p>If this is used to load visible HTML elements in either way, then
26195      * the Panel may not be used as a Layout for hosting nested Panels.</p>
26196      * <p>If this Panel is intended to be used as the host of a Layout (See {@link #layout}
26197      * then the body Element must not be loaded or changed - it is under the control
26198      * of the Panel's Layout.
26199      * <br><p><b>Note</b>: see the Note for <code>{@link Ext.Component#el el}</code> also.</p>
26200      * @type Ext.Element
26201      * @property body
26202      */
26203     /**
26204      * The Panel's bwrap {@link Ext.Element Element} used to contain other Panel elements
26205      * (tbar, body, bbar, footer). See {@link #bodyCfg}. Read-only.
26206      * @type Ext.Element
26207      * @property bwrap
26208      */
26209     /**
26210      * True if this panel is collapsed. Read-only.
26211      * @type Boolean
26212      * @property collapsed
26213      */
26214     /**
26215      * @cfg {Object} bodyCfg
26216      * <p>A {@link Ext.DomHelper DomHelper} element specification object may be specified for any
26217      * Panel Element.</p>
26218      * <p>By default, the Default element in the table below will be used for the html markup to
26219      * create a child element with the commensurate Default class name (<code>baseCls</code> will be
26220      * replaced by <code>{@link #baseCls}</code>):</p>
26221      * <pre>
26222      * Panel      Default  Default             Custom      Additional       Additional
26223      * Element    element  class               element     class            style
26224      * ========   ==========================   =========   ==============   ===========
26225      * {@link #header}     div      {@link #baseCls}+'-header'   {@link #headerCfg}   headerCssClass   headerStyle
26226      * {@link #bwrap}      div      {@link #baseCls}+'-bwrap'     {@link #bwrapCfg}    bwrapCssClass    bwrapStyle
26227      * + tbar     div      {@link #baseCls}+'-tbar'       {@link #tbarCfg}     tbarCssClass     tbarStyle
26228      * + {@link #body}     div      {@link #baseCls}+'-body'       {@link #bodyCfg}     {@link #bodyCssClass}     {@link #bodyStyle}
26229      * + bbar     div      {@link #baseCls}+'-bbar'       {@link #bbarCfg}     bbarCssClass     bbarStyle
26230      * + {@link #footer}   div      {@link #baseCls}+'-footer'   {@link #footerCfg}   footerCssClass   footerStyle
26231      * </pre>
26232      * <p>Configuring a Custom element may be used, for example, to force the {@link #body} Element
26233      * to use a different form of markup than is created by default. An example of this might be
26234      * to {@link Ext.Element#createChild create a child} Panel containing a custom content, such as
26235      * a header, or forcing centering of all Panel content by having the body be a &lt;center&gt;
26236      * element:</p>
26237      * <pre><code>
26238 new Ext.Panel({
26239     title: 'Message Title',
26240     renderTo: Ext.getBody(),
26241     width: 200, height: 130,
26242     <b>bodyCfg</b>: {
26243         tag: 'center',
26244         cls: 'x-panel-body',  // Default class not applied if Custom element specified
26245         html: 'Message'
26246     },
26247     footerCfg: {
26248         tag: 'h2',
26249         cls: 'x-panel-footer',        // same as the Default class
26250         html: 'footer html'
26251     },
26252     footerCssClass: 'custom-footer', // additional css class, see {@link Ext.element#addClass addClass}
26253     footerStyle:    'background-color:red' // see {@link #bodyStyle}
26254 });
26255      * </code></pre>
26256      * <p>The example above also explicitly creates a <code>{@link #footer}</code> with custom markup and
26257      * styling applied.</p>
26258      */
26259     /**
26260      * @cfg {Object} headerCfg
26261      * <p>A {@link Ext.DomHelper DomHelper} element specification object specifying the element structure
26262      * of this Panel's {@link #header} Element.  See <code>{@link #bodyCfg}</code> also.</p>
26263      */
26264     /**
26265      * @cfg {Object} bwrapCfg
26266      * <p>A {@link Ext.DomHelper DomHelper} element specification object specifying the element structure
26267      * of this Panel's {@link #bwrap} Element.  See <code>{@link #bodyCfg}</code> also.</p>
26268      */
26269     /**
26270      * @cfg {Object} tbarCfg
26271      * <p>A {@link Ext.DomHelper DomHelper} element specification object specifying the element structure
26272      * of this Panel's {@link #tbar} Element.  See <code>{@link #bodyCfg}</code> also.</p>
26273      */
26274     /**
26275      * @cfg {Object} bbarCfg
26276      * <p>A {@link Ext.DomHelper DomHelper} element specification object specifying the element structure
26277      * of this Panel's {@link #bbar} Element.  See <code>{@link #bodyCfg}</code> also.</p>
26278      */
26279     /**
26280      * @cfg {Object} footerCfg
26281      * <p>A {@link Ext.DomHelper DomHelper} element specification object specifying the element structure
26282      * of this Panel's {@link #footer} Element.  See <code>{@link #bodyCfg}</code> also.</p>
26283      */
26284     /**
26285      * @cfg {Boolean} closable
26286      * Panels themselves do not directly support being closed, but some Panel subclasses do (like
26287      * {@link Ext.Window}) or a Panel Class within an {@link Ext.TabPanel}.  Specify <code>true</code>
26288      * to enable closing in such situations. Defaults to <code>false</code>.
26289      */
26290     /**
26291      * The Panel's footer {@link Ext.Element Element}. Read-only.
26292      * <p>This Element is used to house the Panel's <code>{@link #buttons}</code> or <code>{@link #fbar}</code>.</p>
26293      * <br><p><b>Note</b>: see the Note for <code>{@link Ext.Component#el el}</code> also.</p>
26294      * @type Ext.Element
26295      * @property footer
26296      */
26297     /**
26298      * @cfg {Mixed} applyTo
26299      * <p>The id of the node, a DOM node or an existing Element corresponding to a DIV that is already present in
26300      * the document that specifies some panel-specific structural markup.  When <code>applyTo</code> is used,
26301      * constituent parts of the panel can be specified by CSS class name within the main element, and the panel
26302      * will automatically create those components from that markup. Any required components not specified in the
26303      * markup will be autogenerated if necessary.</p>
26304      * <p>The following class names are supported (baseCls will be replaced by {@link #baseCls}):</p>
26305      * <ul><li>baseCls + '-header'</li>
26306      * <li>baseCls + '-header-text'</li>
26307      * <li>baseCls + '-bwrap'</li>
26308      * <li>baseCls + '-tbar'</li>
26309      * <li>baseCls + '-body'</li>
26310      * <li>baseCls + '-bbar'</li>
26311      * <li>baseCls + '-footer'</li></ul>
26312      * <p>Using this config, a call to render() is not required.  If applyTo is specified, any value passed for
26313      * {@link #renderTo} will be ignored and the target element's parent node will automatically be used as the
26314      * panel's container.</p>
26315      */
26316     /**
26317      * @cfg {Object/Array} tbar
26318      * <p>The top toolbar of the panel. This can be a {@link Ext.Toolbar} object, a toolbar config, or an array of
26319      * buttons/button configs to be added to the toolbar.  Note that this is not available as a property after render.
26320      * To access the top toolbar after render, use {@link #getTopToolbar}.</p>
26321      * <p><b>Note:</b> Although a Toolbar may contain Field components, these will <b>not</b> be updated by a load
26322      * of an ancestor FormPanel. A Panel's toolbars are not part of the standard Container->Component hierarchy, and
26323      * so are not scanned to collect form items. However, the values <b>will</b> be submitted because form
26324      * submission parameters are collected from the DOM tree.</p>
26325      */
26326     /**
26327      * @cfg {Object/Array} bbar
26328      * <p>The bottom toolbar of the panel. This can be a {@link Ext.Toolbar} object, a toolbar config, or an array of
26329      * buttons/button configs to be added to the toolbar.  Note that this is not available as a property after render.
26330      * To access the bottom toolbar after render, use {@link #getBottomToolbar}.</p>
26331      * <p><b>Note:</b> Although a Toolbar may contain Field components, these will <b>not</b> be updated by a load
26332      * of an ancestor FormPanel. A Panel's toolbars are not part of the standard Container->Component hierarchy, and
26333      * so are not scanned to collect form items. However, the values <b>will</b> be submitted because form
26334      * submission parameters are collected from the DOM tree.</p>
26335      */
26336     /** @cfg {Object/Array} fbar
26337      * <p>A {@link Ext.Toolbar Toolbar} object, a Toolbar config, or an array of
26338      * {@link Ext.Button Button}s/{@link Ext.Button Button} configs, describing a {@link Ext.Toolbar Toolbar} to be rendered into this Panel's footer element.</p>
26339      * <p>After render, the <code>fbar</code> property will be an {@link Ext.Toolbar Toolbar} instance.</p>
26340      * <p>If <code>{@link #buttons}</code> are specified, they will supersede the <code>fbar</code> configuration property.</p>
26341      * The Panel's <code>{@link #buttonAlign}</code> configuration affects the layout of these items, for example:
26342      * <pre><code>
26343 var w = new Ext.Window({
26344     height: 250,
26345     width: 500,
26346     bbar: new Ext.Toolbar({
26347         items: [{
26348             text: 'bbar Left'
26349         },'->',{
26350             text: 'bbar Right'
26351         }]
26352     }),
26353     {@link #buttonAlign}: 'left', // anything but 'center' or 'right' and you can use '-', and '->'
26354                                   // to control the alignment of fbar items
26355     fbar: [{
26356         text: 'fbar Left'
26357     },'->',{
26358         text: 'fbar Right'
26359     }]
26360 }).show();
26361      * </code></pre>
26362      * <p><b>Note:</b> Although a Toolbar may contain Field components, these will <b>not</b> be updated by a load
26363      * of an ancestor FormPanel. A Panel's toolbars are not part of the standard Container->Component hierarchy, and
26364      * so are not scanned to collect form items. However, the values <b>will</b> be submitted because form
26365      * submission parameters are collected from the DOM tree.</p>
26366      */
26367     /**
26368      * @cfg {Boolean} header
26369      * <code>true</code> to create the Panel's header element explicitly, <code>false</code> to skip creating
26370      * it.  If a <code>{@link #title}</code> is set the header will be created automatically, otherwise it will not.
26371      * If a <code>{@link #title}</code> is set but <code>header</code> is explicitly set to <code>false</code>, the header
26372      * will not be rendered.
26373      */
26374     /**
26375      * @cfg {Boolean} footer
26376      * <code>true</code> to create the footer element explicitly, false to skip creating it. The footer
26377      * will be created automatically if <code>{@link #buttons}</code> or a <code>{@link #fbar}</code> have
26378      * been configured.  See <code>{@link #bodyCfg}</code> for an example.
26379      */
26380     /**
26381      * @cfg {String} title
26382      * The title text to be used as innerHTML (html tags are accepted) to display in the panel
26383      * <code>{@link #header}</code> (defaults to ''). When a <code>title</code> is specified the
26384      * <code>{@link #header}</code> element will automatically be created and displayed unless
26385      * {@link #header} is explicitly set to <code>false</code>.  If you do not want to specify a
26386      * <code>title</code> at config time, but you may want one later, you must either specify a non-empty
26387      * <code>title</code> (a blank space ' ' will do) or <code>header:true</code> so that the container
26388      * element will get created.
26389      */
26390     /**
26391      * @cfg {Array} buttons
26392      * <code>buttons</code> will be used as <code>{@link Ext.Container#items items}</code> for the toolbar in
26393      * the footer (<code>{@link #fbar}</code>). Typically the value of this configuration property will be
26394      * an array of {@link Ext.Button}s or {@link Ext.Button} configuration objects.
26395      * If an item is configured with <code>minWidth</code> or the Panel is configured with <code>minButtonWidth</code>,
26396      * that width will be applied to the item.
26397      */
26398     /**
26399      * @cfg {Object/String/Function} autoLoad
26400      * A valid url spec according to the Updater {@link Ext.Updater#update} method.
26401      * If autoLoad is not null, the panel will attempt to load its contents
26402      * immediately upon render.<p>
26403      * The URL will become the default URL for this panel's {@link #body} element,
26404      * so it may be {@link Ext.Element#refresh refresh}ed at any time.</p>
26405      */
26406     /**
26407      * @cfg {Boolean} frame
26408      * <code>false</code> by default to render with plain 1px square borders. <code>true</code> to render with
26409      * 9 elements, complete with custom rounded corners (also see {@link Ext.Element#boxWrap}).
26410      * <p>The template generated for each condition is depicted below:</p><pre><code>
26411      *
26412 // frame = false
26413 &lt;div id="developer-specified-id-goes-here" class="x-panel">
26414
26415     &lt;div class="x-panel-header">&lt;span class="x-panel-header-text">Title: (frame:false)&lt;/span>&lt;/div>
26416
26417     &lt;div class="x-panel-bwrap">
26418         &lt;div class="x-panel-body">&lt;p>html value goes here&lt;/p>&lt;/div>
26419     &lt;/div>
26420 &lt;/div>
26421
26422 // frame = true (create 9 elements)
26423 &lt;div id="developer-specified-id-goes-here" class="x-panel">
26424     &lt;div class="x-panel-tl">&lt;div class="x-panel-tr">&lt;div class="x-panel-tc">
26425         &lt;div class="x-panel-header">&lt;span class="x-panel-header-text">Title: (frame:true)&lt;/span>&lt;/div>
26426     &lt;/div>&lt;/div>&lt;/div>
26427
26428     &lt;div class="x-panel-bwrap">
26429         &lt;div class="x-panel-ml">&lt;div class="x-panel-mr">&lt;div class="x-panel-mc">
26430             &lt;div class="x-panel-body">&lt;p>html value goes here&lt;/p>&lt;/div>
26431         &lt;/div>&lt;/div>&lt;/div>
26432
26433         &lt;div class="x-panel-bl">&lt;div class="x-panel-br">&lt;div class="x-panel-bc"/>
26434         &lt;/div>&lt;/div>&lt;/div>
26435 &lt;/div>
26436      * </code></pre>
26437      */
26438     /**
26439      * @cfg {Boolean} border
26440      * True to display the borders of the panel's body element, false to hide them (defaults to true).  By default,
26441      * the border is a 2px wide inset border, but this can be further altered by setting {@link #bodyBorder} to false.
26442      */
26443     /**
26444      * @cfg {Boolean} bodyBorder
26445      * True to display an interior border on the body element of the panel, false to hide it (defaults to true).
26446      * This only applies when {@link #border} == true.  If border == true and bodyBorder == false, the border will display
26447      * as a 1px wide inset border, giving the entire body element an inset appearance.
26448      */
26449     /**
26450      * @cfg {String/Object/Function} bodyCssClass
26451      * Additional css class selector to be applied to the {@link #body} element in the format expected by
26452      * {@link Ext.Element#addClass} (defaults to null). See {@link #bodyCfg}.
26453      */
26454     /**
26455      * @cfg {String/Object/Function} bodyStyle
26456      * Custom CSS styles to be applied to the {@link #body} element in the format expected by
26457      * {@link Ext.Element#applyStyles} (defaults to null). See {@link #bodyCfg}.
26458      */
26459     /**
26460      * @cfg {String} iconCls
26461      * The CSS class selector that specifies a background image to be used as the header icon (defaults to '').
26462      * <p>An example of specifying a custom icon class would be something like:
26463      * </p><pre><code>
26464 // specify the property in the config for the class:
26465      ...
26466      iconCls: 'my-icon'
26467
26468 // css class that specifies background image to be used as the icon image:
26469 .my-icon { background-image: url(../images/my-icon.gif) 0 6px no-repeat !important; }
26470 </code></pre>
26471      */
26472     /**
26473      * @cfg {Boolean} collapsible
26474      * True to make the panel collapsible and have the expand/collapse toggle button automatically rendered into
26475      * the header tool button area, false to keep the panel statically sized with no button (defaults to false).
26476      */
26477     /**
26478      * @cfg {Array} tools
26479      * An array of tool button configs to be added to the header tool area. When rendered, each tool is
26480      * stored as an {@link Ext.Element Element} referenced by a public property called <code><b></b>tools.<i>&lt;tool-type&gt;</i></code>
26481      * <p>Each tool config may contain the following properties:
26482      * <div class="mdetail-params"><ul>
26483      * <li><b>id</b> : String<div class="sub-desc"><b>Required.</b> The type
26484      * of tool to create. By default, this assigns a CSS class of the form <code>x-tool-<i>&lt;tool-type&gt;</i></code> to the
26485      * resulting tool Element. Ext provides CSS rules, and an icon sprite containing images for the tool types listed below.
26486      * The developer may implement custom tools by supplying alternate CSS rules and background images:
26487      * <ul>
26488      * <div class="x-tool x-tool-toggle" style="float:left; margin-right:5;"> </div><div><code> toggle</code> (Created by default when {@link #collapsible} is <code>true</code>)</div>
26489      * <div class="x-tool x-tool-close" style="float:left; margin-right:5;"> </div><div><code> close</code></div>
26490      * <div class="x-tool x-tool-minimize" style="float:left; margin-right:5;"> </div><div><code> minimize</code></div>
26491      * <div class="x-tool x-tool-maximize" style="float:left; margin-right:5;"> </div><div><code> maximize</code></div>
26492      * <div class="x-tool x-tool-restore" style="float:left; margin-right:5;"> </div><div><code> restore</code></div>
26493      * <div class="x-tool x-tool-gear" style="float:left; margin-right:5;"> </div><div><code> gear</code></div>
26494      * <div class="x-tool x-tool-pin" style="float:left; margin-right:5;"> </div><div><code> pin</code></div>
26495      * <div class="x-tool x-tool-unpin" style="float:left; margin-right:5;"> </div><div><code> unpin</code></div>
26496      * <div class="x-tool x-tool-right" style="float:left; margin-right:5;"> </div><div><code> right</code></div>
26497      * <div class="x-tool x-tool-left" style="float:left; margin-right:5;"> </div><div><code> left</code></div>
26498      * <div class="x-tool x-tool-up" style="float:left; margin-right:5;"> </div><div><code> up</code></div>
26499      * <div class="x-tool x-tool-down" style="float:left; margin-right:5;"> </div><div><code> down</code></div>
26500      * <div class="x-tool x-tool-refresh" style="float:left; margin-right:5;"> </div><div><code> refresh</code></div>
26501      * <div class="x-tool x-tool-minus" style="float:left; margin-right:5;"> </div><div><code> minus</code></div>
26502      * <div class="x-tool x-tool-plus" style="float:left; margin-right:5;"> </div><div><code> plus</code></div>
26503      * <div class="x-tool x-tool-help" style="float:left; margin-right:5;"> </div><div><code> help</code></div>
26504      * <div class="x-tool x-tool-search" style="float:left; margin-right:5;"> </div><div><code> search</code></div>
26505      * <div class="x-tool x-tool-save" style="float:left; margin-right:5;"> </div><div><code> save</code></div>
26506      * <div class="x-tool x-tool-print" style="float:left; margin-right:5;"> </div><div><code> print</code></div>
26507      * </ul></div></li>
26508      * <li><b>handler</b> : Function<div class="sub-desc"><b>Required.</b> The function to
26509      * call when clicked. Arguments passed are:<ul>
26510      * <li><b>event</b> : Ext.EventObject<div class="sub-desc">The click event.</div></li>
26511      * <li><b>toolEl</b> : Ext.Element<div class="sub-desc">The tool Element.</div></li>
26512      * <li><b>panel</b> : Ext.Panel<div class="sub-desc">The host Panel</div></li>
26513      * <li><b>tc</b> : Object<div class="sub-desc">The tool configuration object</div></li>
26514      * </ul></div></li>
26515      * <li><b>stopEvent</b> : Boolean<div class="sub-desc">Defaults to true. Specify as false to allow click event to propagate.</div></li>
26516      * <li><b>scope</b> : Object<div class="sub-desc">The scope in which to call the handler.</div></li>
26517      * <li><b>qtip</b> : String/Object<div class="sub-desc">A tip string, or
26518      * a config argument to {@link Ext.QuickTip#register}</div></li>
26519      * <li><b>hidden</b> : Boolean<div class="sub-desc">True to initially render hidden.</div></li>
26520      * <li><b>on</b> : Object<div class="sub-desc">A listener config object specifiying
26521      * event listeners in the format of an argument to {@link #addListener}</div></li>
26522      * </ul></div>
26523      * <p>Note that, apart from the toggle tool which is provided when a panel is collapsible, these
26524      * tools only provide the visual button. Any required functionality must be provided by adding
26525      * handlers that implement the necessary behavior.</p>
26526      * <p>Example usage:</p>
26527      * <pre><code>
26528 tools:[{
26529     id:'refresh',
26530     qtip: 'Refresh form Data',
26531     // hidden:true,
26532     handler: function(event, toolEl, panel){
26533         // refresh logic
26534     }
26535 },
26536 {
26537     id:'help',
26538     qtip: 'Get Help',
26539     handler: function(event, toolEl, panel){
26540         // whatever
26541     }
26542 }]
26543 </code></pre>
26544      * <p>For the custom id of <code>'help'</code> define two relevant css classes with a link to
26545      * a 15x15 image:</p>
26546      * <pre><code>
26547 .x-tool-help {background-image: url(images/help.png);}
26548 .x-tool-help-over {background-image: url(images/help_over.png);}
26549 // if using an image sprite:
26550 .x-tool-help {background-image: url(images/help.png) no-repeat 0 0;}
26551 .x-tool-help-over {background-position:-15px 0;}
26552 </code></pre>
26553      */
26554     /**
26555      * @cfg {Ext.Template/Ext.XTemplate} toolTemplate
26556      * <p>A Template used to create {@link #tools} in the {@link #header} Element. Defaults to:</p><pre><code>
26557 new Ext.Template('&lt;div class="x-tool x-tool-{id}">&amp;#160;&lt;/div>')</code></pre>
26558      * <p>This may may be overridden to provide a custom DOM structure for tools based upon a more
26559      * complex XTemplate. The template's data is a single tool configuration object (Not the entire Array)
26560      * as specified in {@link #tools}.  In the following example an &lt;a> tag is used to provide a
26561      * visual indication when hovering over the tool:</p><pre><code>
26562 var win = new Ext.Window({
26563     tools: [{
26564         id: 'download',
26565         href: '/MyPdfDoc.pdf'
26566     }],
26567     toolTemplate: new Ext.XTemplate(
26568         '&lt;tpl if="id==\'download\'">',
26569             '&lt;a class="x-tool x-tool-pdf" href="{href}">&lt;/a>',
26570         '&lt;/tpl>',
26571         '&lt;tpl if="id!=\'download\'">',
26572             '&lt;div class="x-tool x-tool-{id}">&amp;#160;&lt;/div>',
26573         '&lt;/tpl>'
26574     ),
26575     width:500,
26576     height:300,
26577     closeAction:'hide'
26578 });</code></pre>
26579      * <p>Note that the CSS class 'x-tool-pdf' should have an associated style rule which provides an
26580      * appropriate background image, something like:</p>
26581     <pre><code>
26582     a.x-tool-pdf {background-image: url(../shared/extjs/images/pdf.gif)!important;}
26583     </code></pre>
26584      */
26585     /**
26586      * @cfg {Boolean} hideCollapseTool
26587      * <code>true</code> to hide the expand/collapse toggle button when <code>{@link #collapsible} == true</code>,
26588      * <code>false</code> to display it (defaults to <code>false</code>).
26589      */
26590     /**
26591      * @cfg {Boolean} titleCollapse
26592      * <code>true</code> to allow expanding and collapsing the panel (when <code>{@link #collapsible} = true</code>)
26593      * by clicking anywhere in the header bar, <code>false</code>) to allow it only by clicking to tool button
26594      * (defaults to <code>false</code>)). If this panel is a child item of a border layout also see the
26595      * {@link Ext.layout.BorderLayout.Region BorderLayout.Region}
26596      * <code>{@link Ext.layout.BorderLayout.Region#floatable floatable}</code> config option.
26597      */
26598
26599     /**
26600      * @cfg {Mixed} floating
26601      * <p>This property is used to configure the underlying {@link Ext.Layer}. Acceptable values for this
26602      * configuration property are:</p><div class="mdetail-params"><ul>
26603      * <li><b><code>false</code></b> : <b>Default.</b><div class="sub-desc">Display the panel inline where it is
26604      * rendered.</div></li>
26605      * <li><b><code>true</code></b> : <div class="sub-desc">Float the panel (absolute position it with automatic
26606      * shimming and shadow).<ul>
26607      * <div class="sub-desc">Setting floating to true will create an Ext.Layer for this panel and display the
26608      * panel at negative offsets so that it is hidden.</div>
26609      * <div class="sub-desc">Since the panel will be absolute positioned, the position must be set explicitly
26610      * <i>after</i> render (e.g., <code>myPanel.setPosition(100,100);</code>).</div>
26611      * <div class="sub-desc"><b>Note</b>: when floating a panel you should always assign a fixed width,
26612      * otherwise it will be auto width and will expand to fill to the right edge of the viewport.</div>
26613      * </ul></div></li>
26614      * <li><b><code>{@link Ext.Layer object}</code></b> : <div class="sub-desc">The specified object will be used
26615      * as the configuration object for the {@link Ext.Layer} that will be created.</div></li>
26616      * </ul></div>
26617      */
26618     /**
26619      * @cfg {Boolean/String} shadow
26620      * <code>true</code> (or a valid Ext.Shadow {@link Ext.Shadow#mode} value) to display a shadow behind the
26621      * panel, <code>false</code> to display no shadow (defaults to <code>'sides'</code>).  Note that this option
26622      * only applies when <code>{@link #floating} = true</code>.
26623      */
26624     /**
26625      * @cfg {Number} shadowOffset
26626      * The number of pixels to offset the shadow if displayed (defaults to <code>4</code>). Note that this
26627      * option only applies when <code>{@link #floating} = true</code>.
26628      */
26629     /**
26630      * @cfg {Boolean} shim
26631      * <code>false</code> to disable the iframe shim in browsers which need one (defaults to <code>true</code>).
26632      * Note that this option only applies when <code>{@link #floating} = true</code>.
26633      */
26634     /**
26635      * @cfg {Object/Array} keys
26636      * A {@link Ext.KeyMap} config object (in the format expected by {@link Ext.KeyMap#addBinding}
26637      * used to assign custom key handling to this panel (defaults to <code>null</code>).
26638      */
26639     /**
26640      * @cfg {Boolean/Object} draggable
26641      * <p><code>true</code> to enable dragging of this Panel (defaults to <code>false</code>).</p>
26642      * <p>For custom drag/drop implementations, an <b>Ext.Panel.DD</b> config could also be passed
26643      * in this config instead of <code>true</code>. Ext.Panel.DD is an internal, undocumented class which
26644      * moves a proxy Element around in place of the Panel's element, but provides no other behaviour
26645      * during dragging or on drop. It is a subclass of {@link Ext.dd.DragSource}, so behaviour may be
26646      * added by implementing the interface methods of {@link Ext.dd.DragDrop} e.g.:
26647      * <pre><code>
26648 new Ext.Panel({
26649     title: 'Drag me',
26650     x: 100,
26651     y: 100,
26652     renderTo: Ext.getBody(),
26653     floating: true,
26654     frame: true,
26655     width: 400,
26656     height: 200,
26657     draggable: {
26658 //      Config option of Ext.Panel.DD class.
26659 //      It&#39;s a floating Panel, so do not show a placeholder proxy in the original position.
26660         insertProxy: false,
26661
26662 //      Called for each mousemove event while dragging the DD object.
26663         onDrag : function(e){
26664 //          Record the x,y position of the drag proxy so that we can
26665 //          position the Panel at end of drag.
26666             var pel = this.proxy.getEl();
26667             this.x = pel.getLeft(true);
26668             this.y = pel.getTop(true);
26669
26670 //          Keep the Shadow aligned if there is one.
26671             var s = this.panel.getEl().shadow;
26672             if (s) {
26673                 s.realign(this.x, this.y, pel.getWidth(), pel.getHeight());
26674             }
26675         },
26676
26677 //      Called on the mouseup event.
26678         endDrag : function(e){
26679             this.panel.setPosition(this.x, this.y);
26680         }
26681     }
26682 }).show();
26683 </code></pre>
26684      */
26685     /**
26686      * @cfg {Boolean} disabled
26687      * Render this panel disabled (default is <code>false</code>). An important note when using the disabled
26688      * config on panels is that IE will often fail to initialize the disabled mask element correectly if
26689      * the panel's layout has not yet completed by the time the Panel is disabled during the render process.
26690      * If you experience this issue, you may need to instead use the {@link #afterlayout} event to initialize
26691      * the disabled state:
26692      * <pre><code>
26693 new Ext.Panel({
26694     ...
26695     listeners: {
26696         'afterlayout': {
26697             fn: function(p){
26698                 p.disable();
26699             },
26700             single: true // important, as many layouts can occur
26701         }
26702     }
26703 });
26704 </code></pre>
26705      */
26706     /**
26707      * @cfg {Boolean} autoHeight
26708      * <code>true</code> to use height:'auto', <code>false</code> to use fixed height (defaults to <code>false</code>).
26709      * <b>Note</b>: Setting <code>autoHeight: true</code> means that the browser will manage the panel's height
26710      * based on its contents, and that Ext will not manage it at all. If the panel is within a layout that
26711      * manages dimensions (<code>fit</code>, <code>border</code>, etc.) then setting <code>autoHeight: true</code>
26712      * can cause issues with scrolling and will not generally work as expected since the panel will take
26713      * on the height of its contents rather than the height required by the Ext layout.
26714      */
26715
26716
26717     /**
26718      * @cfg {String} baseCls
26719      * The base CSS class to apply to this panel's element (defaults to <code>'x-panel'</code>).
26720      * <p>Another option available by default is to specify <code>'x-plain'</code> which strips all styling
26721      * except for required attributes for Ext layouts to function (e.g. overflow:hidden).
26722      * See <code>{@link #unstyled}</code> also.</p>
26723      */
26724     baseCls : 'x-panel',
26725     /**
26726      * @cfg {String} collapsedCls
26727      * A CSS class to add to the panel's element after it has been collapsed (defaults to
26728      * <code>'x-panel-collapsed'</code>).
26729      */
26730     collapsedCls : 'x-panel-collapsed',
26731     /**
26732      * @cfg {Boolean} maskDisabled
26733      * <code>true</code> to mask the panel when it is {@link #disabled}, <code>false</code> to not mask it (defaults
26734      * to <code>true</code>).  Either way, the panel will always tell its contained elements to disable themselves
26735      * when it is disabled, but masking the panel can provide an additional visual cue that the panel is
26736      * disabled.
26737      */
26738     maskDisabled : true,
26739     /**
26740      * @cfg {Boolean} animCollapse
26741      * <code>true</code> to animate the transition when the panel is collapsed, <code>false</code> to skip the
26742      * animation (defaults to <code>true</code> if the {@link Ext.Fx} class is available, otherwise <code>false</code>).
26743      */
26744     animCollapse : Ext.enableFx,
26745     /**
26746      * @cfg {Boolean} headerAsText
26747      * <code>true</code> to display the panel <code>{@link #title}</code> in the <code>{@link #header}</code>,
26748      * <code>false</code> to hide it (defaults to <code>true</code>).
26749      */
26750     headerAsText : true,
26751     /**
26752      * @cfg {String} buttonAlign
26753      * The alignment of any {@link #buttons} added to this panel.  Valid values are <code>'right'</code>,
26754      * <code>'left'</code> and <code>'center'</code> (defaults to <code>'right'</code>).
26755      */
26756     buttonAlign : 'right',
26757     /**
26758      * @cfg {Boolean} collapsed
26759      * <code>true</code> to render the panel collapsed, <code>false</code> to render it expanded (defaults to
26760      * <code>false</code>).
26761      */
26762     collapsed : false,
26763     /**
26764      * @cfg {Boolean} collapseFirst
26765      * <code>true</code> to make sure the collapse/expand toggle button always renders first (to the left of)
26766      * any other tools in the panel's title bar, <code>false</code> to render it last (defaults to <code>true</code>).
26767      */
26768     collapseFirst : true,
26769     /**
26770      * @cfg {Number} minButtonWidth
26771      * Minimum width in pixels of all {@link #buttons} in this panel (defaults to <code>75</code>)
26772      */
26773     minButtonWidth : 75,
26774     /**
26775      * @cfg {Boolean} unstyled
26776      * Overrides the <code>{@link #baseCls}</code> setting to <code>{@link #baseCls} = 'x-plain'</code> which renders
26777      * the panel unstyled except for required attributes for Ext layouts to function (e.g. overflow:hidden).
26778      */
26779     /**
26780      * @cfg {String} elements
26781      * A comma-delimited list of panel elements to initialize when the panel is rendered.  Normally, this list will be
26782      * generated automatically based on the items added to the panel at config time, but sometimes it might be useful to
26783      * make sure a structural element is rendered even if not specified at config time (for example, you may want
26784      * to add a button or toolbar dynamically after the panel has been rendered).  Adding those elements to this
26785      * list will allocate the required placeholders in the panel when it is rendered.  Valid values are<div class="mdetail-params"><ul>
26786      * <li><code>header</code></li>
26787      * <li><code>tbar</code> (top bar)</li>
26788      * <li><code>body</code></li>
26789      * <li><code>bbar</code> (bottom bar)</li>
26790      * <li><code>footer</code></li>
26791      * </ul></div>
26792      * Defaults to '<code>body</code>'.
26793      */
26794     elements : 'body',
26795     /**
26796      * @cfg {Boolean} preventBodyReset
26797      * Defaults to <code>false</code>.  When set to <code>true</code>, an extra css class <code>'x-panel-normal'</code>
26798      * will be added to the panel's element, effectively applying css styles suggested by the W3C
26799      * (see http://www.w3.org/TR/CSS21/sample.html) to the Panel's <b>body</b> element (not the header,
26800      * footer, etc.).
26801      */
26802     preventBodyReset : false,
26803
26804     /**
26805      * @cfg {Number/String} padding
26806      * A shortcut for setting a padding style on the body element. The value can either be
26807      * a number to be applied to all sides, or a normal css string describing padding.
26808      * Defaults to <tt>undefined</tt>.
26809      *
26810      */
26811     padding: undefined,
26812
26813     /** @cfg {String} resizeEvent
26814      * The event to listen to for resizing in layouts. Defaults to <tt>'bodyresize'</tt>.
26815      */
26816     resizeEvent: 'bodyresize',
26817
26818     // protected - these could be used to customize the behavior of the window,
26819     // but changing them would not be useful without further mofifications and
26820     // could lead to unexpected or undesirable results.
26821     toolTarget : 'header',
26822     collapseEl : 'bwrap',
26823     slideAnchor : 't',
26824     disabledClass : '',
26825
26826     // private, notify box this class will handle heights
26827     deferHeight : true,
26828     // private
26829     expandDefaults: {
26830         duration : 0.25
26831     },
26832     // private
26833     collapseDefaults : {
26834         duration : 0.25
26835     },
26836
26837     // private
26838     initComponent : function(){
26839         Ext.Panel.superclass.initComponent.call(this);
26840
26841         this.addEvents(
26842             /**
26843              * @event bodyresize
26844              * Fires after the Panel has been resized.
26845              * @param {Ext.Panel} p the Panel which has been resized.
26846              * @param {Number} width The Panel body's new width.
26847              * @param {Number} height The Panel body's new height.
26848              */
26849             'bodyresize',
26850             /**
26851              * @event titlechange
26852              * Fires after the Panel title has been {@link #title set} or {@link #setTitle changed}.
26853              * @param {Ext.Panel} p the Panel which has had its title changed.
26854              * @param {String} The new title.
26855              */
26856             'titlechange',
26857             /**
26858              * @event iconchange
26859              * Fires after the Panel icon class has been {@link #iconCls set} or {@link #setIconClass changed}.
26860              * @param {Ext.Panel} p the Panel which has had its {@link #iconCls icon class} changed.
26861              * @param {String} The new icon class.
26862              * @param {String} The old icon class.
26863              */
26864             'iconchange',
26865             /**
26866              * @event collapse
26867              * Fires after the Panel has been collapsed.
26868              * @param {Ext.Panel} p the Panel that has been collapsed.
26869              */
26870             'collapse',
26871             /**
26872              * @event expand
26873              * Fires after the Panel has been expanded.
26874              * @param {Ext.Panel} p The Panel that has been expanded.
26875              */
26876             'expand',
26877             /**
26878              * @event beforecollapse
26879              * Fires before the Panel is collapsed.  A handler can return false to cancel the collapse.
26880              * @param {Ext.Panel} p the Panel being collapsed.
26881              * @param {Boolean} animate True if the collapse is animated, else false.
26882              */
26883             'beforecollapse',
26884             /**
26885              * @event beforeexpand
26886              * Fires before the Panel is expanded.  A handler can return false to cancel the expand.
26887              * @param {Ext.Panel} p The Panel being expanded.
26888              * @param {Boolean} animate True if the expand is animated, else false.
26889              */
26890             'beforeexpand',
26891             /**
26892              * @event beforeclose
26893              * Fires before the Panel is closed.  Note that Panels do not directly support being closed, but some
26894              * Panel subclasses do (like {@link Ext.Window}) or a Panel within a Ext.TabPanel.  This event only
26895              * applies to such subclasses.
26896              * A handler can return false to cancel the close.
26897              * @param {Ext.Panel} p The Panel being closed.
26898              */
26899             'beforeclose',
26900             /**
26901              * @event close
26902              * Fires after the Panel is closed.  Note that Panels do not directly support being closed, but some
26903              * Panel subclasses do (like {@link Ext.Window}) or a Panel within a Ext.TabPanel.
26904              * @param {Ext.Panel} p The Panel that has been closed.
26905              */
26906             'close',
26907             /**
26908              * @event activate
26909              * Fires after the Panel has been visually activated.
26910              * Note that Panels do not directly support being activated, but some Panel subclasses
26911              * do (like {@link Ext.Window}). Panels which are child Components of a TabPanel fire the
26912              * activate and deactivate events under the control of the TabPanel.
26913              * @param {Ext.Panel} p The Panel that has been activated.
26914              */
26915             'activate',
26916             /**
26917              * @event deactivate
26918              * Fires after the Panel has been visually deactivated.
26919              * Note that Panels do not directly support being deactivated, but some Panel subclasses
26920              * do (like {@link Ext.Window}). Panels which are child Components of a TabPanel fire the
26921              * activate and deactivate events under the control of the TabPanel.
26922              * @param {Ext.Panel} p The Panel that has been deactivated.
26923              */
26924             'deactivate'
26925         );
26926
26927         if(this.unstyled){
26928             this.baseCls = 'x-plain';
26929         }
26930
26931
26932         this.toolbars = [];
26933         // shortcuts
26934         if(this.tbar){
26935             this.elements += ',tbar';
26936             this.topToolbar = this.createToolbar(this.tbar);
26937             this.tbar = null;
26938
26939         }
26940         if(this.bbar){
26941             this.elements += ',bbar';
26942             this.bottomToolbar = this.createToolbar(this.bbar);
26943             this.bbar = null;
26944         }
26945
26946         if(this.header === true){
26947             this.elements += ',header';
26948             this.header = null;
26949         }else if(this.headerCfg || (this.title && this.header !== false)){
26950             this.elements += ',header';
26951         }
26952
26953         if(this.footerCfg || this.footer === true){
26954             this.elements += ',footer';
26955             this.footer = null;
26956         }
26957
26958         if(this.buttons){
26959             this.fbar = this.buttons;
26960             this.buttons = null;
26961         }
26962         if(this.fbar){
26963             this.createFbar(this.fbar);
26964         }
26965         if(this.autoLoad){
26966             this.on('render', this.doAutoLoad, this, {delay:10});
26967         }
26968     },
26969
26970     // private
26971     createFbar : function(fbar){
26972         var min = this.minButtonWidth;
26973         this.elements += ',footer';
26974         this.fbar = this.createToolbar(fbar, {
26975             buttonAlign: this.buttonAlign,
26976             toolbarCls: 'x-panel-fbar',
26977             enableOverflow: false,
26978             defaults: function(c){
26979                 return {
26980                     minWidth: c.minWidth || min
26981                 };
26982             }
26983         });
26984         // @compat addButton and buttons could possibly be removed
26985         // @target 4.0
26986         /**
26987          * This Panel's Array of buttons as created from the <code>{@link #buttons}</code>
26988          * config property. Read only.
26989          * @type Array
26990          * @property buttons
26991          */
26992         this.fbar.items.each(function(c){
26993             c.minWidth = c.minWidth || this.minButtonWidth;
26994         }, this);
26995         this.buttons = this.fbar.items.items;
26996     },
26997
26998     // private
26999     createToolbar: function(tb, options){
27000         var result;
27001         // Convert array to proper toolbar config
27002         if(Ext.isArray(tb)){
27003             tb = {
27004                 items: tb
27005             };
27006         }
27007         result = tb.events ? Ext.apply(tb, options) : this.createComponent(Ext.apply({}, tb, options), 'toolbar');
27008         this.toolbars.push(result);
27009         return result;
27010     },
27011
27012     // private
27013     createElement : function(name, pnode){
27014         if(this[name]){
27015             pnode.appendChild(this[name].dom);
27016             return;
27017         }
27018
27019         if(name === 'bwrap' || this.elements.indexOf(name) != -1){
27020             if(this[name+'Cfg']){
27021                 this[name] = Ext.fly(pnode).createChild(this[name+'Cfg']);
27022             }else{
27023                 var el = document.createElement('div');
27024                 el.className = this[name+'Cls'];
27025                 this[name] = Ext.get(pnode.appendChild(el));
27026             }
27027             if(this[name+'CssClass']){
27028                 this[name].addClass(this[name+'CssClass']);
27029             }
27030             if(this[name+'Style']){
27031                 this[name].applyStyles(this[name+'Style']);
27032             }
27033         }
27034     },
27035
27036     // private
27037     onRender : function(ct, position){
27038         Ext.Panel.superclass.onRender.call(this, ct, position);
27039         this.createClasses();
27040
27041         var el = this.el,
27042             d = el.dom,
27043             bw,
27044             ts;
27045
27046
27047         if(this.collapsible && !this.hideCollapseTool){
27048             this.tools = this.tools ? this.tools.slice(0) : [];
27049             this.tools[this.collapseFirst?'unshift':'push']({
27050                 id: 'toggle',
27051                 handler : this.toggleCollapse,
27052                 scope: this
27053             });
27054         }
27055
27056         if(this.tools){
27057             ts = this.tools;
27058             this.elements += (this.header !== false) ? ',header' : '';
27059         }
27060         this.tools = {};
27061
27062         el.addClass(this.baseCls);
27063         if(d.firstChild){ // existing markup
27064             this.header = el.down('.'+this.headerCls);
27065             this.bwrap = el.down('.'+this.bwrapCls);
27066             var cp = this.bwrap ? this.bwrap : el;
27067             this.tbar = cp.down('.'+this.tbarCls);
27068             this.body = cp.down('.'+this.bodyCls);
27069             this.bbar = cp.down('.'+this.bbarCls);
27070             this.footer = cp.down('.'+this.footerCls);
27071             this.fromMarkup = true;
27072         }
27073         if (this.preventBodyReset === true) {
27074             el.addClass('x-panel-reset');
27075         }
27076         if(this.cls){
27077             el.addClass(this.cls);
27078         }
27079
27080         if(this.buttons){
27081             this.elements += ',footer';
27082         }
27083
27084         // This block allows for maximum flexibility and performance when using existing markup
27085
27086         // framing requires special markup
27087         if(this.frame){
27088             el.insertHtml('afterBegin', String.format(Ext.Element.boxMarkup, this.baseCls));
27089
27090             this.createElement('header', d.firstChild.firstChild.firstChild);
27091             this.createElement('bwrap', d);
27092
27093             // append the mid and bottom frame to the bwrap
27094             bw = this.bwrap.dom;
27095             var ml = d.childNodes[1], bl = d.childNodes[2];
27096             bw.appendChild(ml);
27097             bw.appendChild(bl);
27098
27099             var mc = bw.firstChild.firstChild.firstChild;
27100             this.createElement('tbar', mc);
27101             this.createElement('body', mc);
27102             this.createElement('bbar', mc);
27103             this.createElement('footer', bw.lastChild.firstChild.firstChild);
27104
27105             if(!this.footer){
27106                 this.bwrap.dom.lastChild.className += ' x-panel-nofooter';
27107             }
27108             /*
27109              * Store a reference to this element so:
27110              * a) We aren't looking it up all the time
27111              * b) The last element is reported incorrectly when using a loadmask
27112              */
27113             this.ft = Ext.get(this.bwrap.dom.lastChild);
27114             this.mc = Ext.get(mc);
27115         }else{
27116             this.createElement('header', d);
27117             this.createElement('bwrap', d);
27118
27119             // append the mid and bottom frame to the bwrap
27120             bw = this.bwrap.dom;
27121             this.createElement('tbar', bw);
27122             this.createElement('body', bw);
27123             this.createElement('bbar', bw);
27124             this.createElement('footer', bw);
27125
27126             if(!this.header){
27127                 this.body.addClass(this.bodyCls + '-noheader');
27128                 if(this.tbar){
27129                     this.tbar.addClass(this.tbarCls + '-noheader');
27130                 }
27131             }
27132         }
27133
27134         if(Ext.isDefined(this.padding)){
27135             this.body.setStyle('padding', this.body.addUnits(this.padding));
27136         }
27137
27138         if(this.border === false){
27139             this.el.addClass(this.baseCls + '-noborder');
27140             this.body.addClass(this.bodyCls + '-noborder');
27141             if(this.header){
27142                 this.header.addClass(this.headerCls + '-noborder');
27143             }
27144             if(this.footer){
27145                 this.footer.addClass(this.footerCls + '-noborder');
27146             }
27147             if(this.tbar){
27148                 this.tbar.addClass(this.tbarCls + '-noborder');
27149             }
27150             if(this.bbar){
27151                 this.bbar.addClass(this.bbarCls + '-noborder');
27152             }
27153         }
27154
27155         if(this.bodyBorder === false){
27156            this.body.addClass(this.bodyCls + '-noborder');
27157         }
27158
27159         this.bwrap.enableDisplayMode('block');
27160
27161         if(this.header){
27162             this.header.unselectable();
27163
27164             // for tools, we need to wrap any existing header markup
27165             if(this.headerAsText){
27166                 this.header.dom.innerHTML =
27167                     '<span class="' + this.headerTextCls + '">'+this.header.dom.innerHTML+'</span>';
27168
27169                 if(this.iconCls){
27170                     this.setIconClass(this.iconCls);
27171                 }
27172             }
27173         }
27174
27175         if(this.floating){
27176             this.makeFloating(this.floating);
27177         }
27178
27179         if(this.collapsible && this.titleCollapse && this.header){
27180             this.mon(this.header, 'click', this.toggleCollapse, this);
27181             this.header.setStyle('cursor', 'pointer');
27182         }
27183         if(ts){
27184             this.addTool.apply(this, ts);
27185         }
27186
27187         // Render Toolbars.
27188         if(this.fbar){
27189             this.footer.addClass('x-panel-btns');
27190             this.fbar.ownerCt = this;
27191             this.fbar.render(this.footer);
27192             this.footer.createChild({cls:'x-clear'});
27193         }
27194         if(this.tbar && this.topToolbar){
27195             this.topToolbar.ownerCt = this;
27196             this.topToolbar.render(this.tbar);
27197         }
27198         if(this.bbar && this.bottomToolbar){
27199             this.bottomToolbar.ownerCt = this;
27200             this.bottomToolbar.render(this.bbar);
27201         }
27202     },
27203
27204     /**
27205      * Sets the CSS class that provides the icon image for this panel.  This method will replace any existing
27206      * icon class if one has already been set and fire the {@link #iconchange} event after completion.
27207      * @param {String} cls The new CSS class name
27208      */
27209     setIconClass : function(cls){
27210         var old = this.iconCls;
27211         this.iconCls = cls;
27212         if(this.rendered && this.header){
27213             if(this.frame){
27214                 this.header.addClass('x-panel-icon');
27215                 this.header.replaceClass(old, this.iconCls);
27216             }else{
27217                 var hd = this.header,
27218                     img = hd.child('img.x-panel-inline-icon');
27219                 if(img){
27220                     Ext.fly(img).replaceClass(old, this.iconCls);
27221                 }else{
27222                     var hdspan = hd.child('span.' + this.headerTextCls);
27223                     if (hdspan) {
27224                         Ext.DomHelper.insertBefore(hdspan.dom, {
27225                             tag:'img', alt: '', src: Ext.BLANK_IMAGE_URL, cls:'x-panel-inline-icon '+this.iconCls
27226                         });
27227                     }
27228                  }
27229             }
27230         }
27231         this.fireEvent('iconchange', this, cls, old);
27232     },
27233
27234     // private
27235     makeFloating : function(cfg){
27236         this.floating = true;
27237         this.el = new Ext.Layer(Ext.apply({}, cfg, {
27238             shadow: Ext.isDefined(this.shadow) ? this.shadow : 'sides',
27239             shadowOffset: this.shadowOffset,
27240             constrain:false,
27241             shim: this.shim === false ? false : undefined
27242         }), this.el);
27243     },
27244
27245     /**
27246      * Returns the {@link Ext.Toolbar toolbar} from the top (<code>{@link #tbar}</code>) section of the panel.
27247      * @return {Ext.Toolbar} The toolbar
27248      */
27249     getTopToolbar : function(){
27250         return this.topToolbar;
27251     },
27252
27253     /**
27254      * Returns the {@link Ext.Toolbar toolbar} from the bottom (<code>{@link #bbar}</code>) section of the panel.
27255      * @return {Ext.Toolbar} The toolbar
27256      */
27257     getBottomToolbar : function(){
27258         return this.bottomToolbar;
27259     },
27260
27261     /**
27262      * Returns the {@link Ext.Toolbar toolbar} from the footer (<code>{@link #fbar}</code>) section of the panel.
27263      * @return {Ext.Toolbar} The toolbar
27264      */
27265     getFooterToolbar : function() {
27266         return this.fbar;
27267     },
27268
27269     /**
27270      * Adds a button to this panel.  Note that this method must be called prior to rendering.  The preferred
27271      * approach is to add buttons via the {@link #buttons} config.
27272      * @param {String/Object} config A valid {@link Ext.Button} config.  A string will become the text for a default
27273      * button config, an object will be treated as a button config object.
27274      * @param {Function} handler The function to be called on button {@link Ext.Button#click}
27275      * @param {Object} scope The scope (<code>this</code> reference) in which the button handler function is executed. Defaults to the Button.
27276      * @return {Ext.Button} The button that was added
27277      */
27278     addButton : function(config, handler, scope){
27279         if(!this.fbar){
27280             this.createFbar([]);
27281         }
27282         if(handler){
27283             if(Ext.isString(config)){
27284                 config = {text: config};
27285             }
27286             config = Ext.apply({
27287                 handler: handler,
27288                 scope: scope
27289             }, config);
27290         }
27291         return this.fbar.add(config);
27292     },
27293
27294     // private
27295     addTool : function(){
27296         if(!this.rendered){
27297             if(!this.tools){
27298                 this.tools = [];
27299             }
27300             Ext.each(arguments, function(arg){
27301                 this.tools.push(arg);
27302             }, this);
27303             return;
27304         }
27305          // nowhere to render tools!
27306         if(!this[this.toolTarget]){
27307             return;
27308         }
27309         if(!this.toolTemplate){
27310             // initialize the global tool template on first use
27311             var tt = new Ext.Template(
27312                  '<div class="x-tool x-tool-{id}">&#160;</div>'
27313             );
27314             tt.disableFormats = true;
27315             tt.compile();
27316             Ext.Panel.prototype.toolTemplate = tt;
27317         }
27318         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
27319             var tc = a[i];
27320             if(!this.tools[tc.id]){
27321                 var overCls = 'x-tool-'+tc.id+'-over';
27322                 var t = this.toolTemplate.insertFirst(this[this.toolTarget], tc, true);
27323                 this.tools[tc.id] = t;
27324                 t.enableDisplayMode('block');
27325                 this.mon(t, 'click',  this.createToolHandler(t, tc, overCls, this));
27326                 if(tc.on){
27327                     this.mon(t, tc.on);
27328                 }
27329                 if(tc.hidden){
27330                     t.hide();
27331                 }
27332                 if(tc.qtip){
27333                     if(Ext.isObject(tc.qtip)){
27334                         Ext.QuickTips.register(Ext.apply({
27335                               target: t.id
27336                         }, tc.qtip));
27337                     } else {
27338                         t.dom.qtip = tc.qtip;
27339                     }
27340                 }
27341                 t.addClassOnOver(overCls);
27342             }
27343         }
27344     },
27345
27346     onLayout : function(shallow, force){
27347         Ext.Panel.superclass.onLayout.apply(this, arguments);
27348         if(this.hasLayout && this.toolbars.length > 0){
27349             Ext.each(this.toolbars, function(tb){
27350                 tb.doLayout(undefined, force);
27351             });
27352             this.syncHeight();
27353         }
27354     },
27355
27356     syncHeight : function(){
27357         var h = this.toolbarHeight,
27358                 bd = this.body,
27359                 lsh = this.lastSize.height,
27360                 sz;
27361
27362         if(this.autoHeight || !Ext.isDefined(lsh) || lsh == 'auto'){
27363             return;
27364         }
27365
27366
27367         if(h != this.getToolbarHeight()){
27368             h = Math.max(0, lsh - this.getFrameHeight());
27369             bd.setHeight(h);
27370             sz = bd.getSize();
27371             this.toolbarHeight = this.getToolbarHeight();
27372             this.onBodyResize(sz.width, sz.height);
27373         }
27374     },
27375
27376     // private
27377     onShow : function(){
27378         if(this.floating){
27379             return this.el.show();
27380         }
27381         Ext.Panel.superclass.onShow.call(this);
27382     },
27383
27384     // private
27385     onHide : function(){
27386         if(this.floating){
27387             return this.el.hide();
27388         }
27389         Ext.Panel.superclass.onHide.call(this);
27390     },
27391
27392     // private
27393     createToolHandler : function(t, tc, overCls, panel){
27394         return function(e){
27395             t.removeClass(overCls);
27396             if(tc.stopEvent !== false){
27397                 e.stopEvent();
27398             }
27399             if(tc.handler){
27400                 tc.handler.call(tc.scope || t, e, t, panel, tc);
27401             }
27402         };
27403     },
27404
27405     // private
27406     afterRender : function(){
27407         if(this.floating && !this.hidden){
27408             this.el.show();
27409         }
27410         if(this.title){
27411             this.setTitle(this.title);
27412         }
27413         Ext.Panel.superclass.afterRender.call(this); // do sizing calcs last
27414         if (this.collapsed) {
27415             this.collapsed = false;
27416             this.collapse(false);
27417         }
27418         this.initEvents();
27419     },
27420
27421     // private
27422     getKeyMap : function(){
27423         if(!this.keyMap){
27424             this.keyMap = new Ext.KeyMap(this.el, this.keys);
27425         }
27426         return this.keyMap;
27427     },
27428
27429     // private
27430     initEvents : function(){
27431         if(this.keys){
27432             this.getKeyMap();
27433         }
27434         if(this.draggable){
27435             this.initDraggable();
27436         }
27437         if(this.toolbars.length > 0){
27438             Ext.each(this.toolbars, function(tb){
27439                 tb.doLayout();
27440                 tb.on({
27441                     scope: this,
27442                     afterlayout: this.syncHeight,
27443                     remove: this.syncHeight
27444                 });
27445             }, this);
27446             this.syncHeight();
27447         }
27448
27449     },
27450
27451     // private
27452     initDraggable : function(){
27453         /**
27454          * <p>If this Panel is configured {@link #draggable}, this property will contain
27455          * an instance of {@link Ext.dd.DragSource} which handles dragging the Panel.</p>
27456          * The developer must provide implementations of the abstract methods of {@link Ext.dd.DragSource}
27457          * in order to supply behaviour for each stage of the drag/drop process. See {@link #draggable}.
27458          * @type Ext.dd.DragSource.
27459          * @property dd
27460          */
27461         this.dd = new Ext.Panel.DD(this, Ext.isBoolean(this.draggable) ? null : this.draggable);
27462     },
27463
27464     // private
27465     beforeEffect : function(anim){
27466         if(this.floating){
27467             this.el.beforeAction();
27468         }
27469         if(anim !== false){
27470             this.el.addClass('x-panel-animated');
27471         }
27472     },
27473
27474     // private
27475     afterEffect : function(anim){
27476         this.syncShadow();
27477         this.el.removeClass('x-panel-animated');
27478     },
27479
27480     // private - wraps up an animation param with internal callbacks
27481     createEffect : function(a, cb, scope){
27482         var o = {
27483             scope:scope,
27484             block:true
27485         };
27486         if(a === true){
27487             o.callback = cb;
27488             return o;
27489         }else if(!a.callback){
27490             o.callback = cb;
27491         }else { // wrap it up
27492             o.callback = function(){
27493                 cb.call(scope);
27494                 Ext.callback(a.callback, a.scope);
27495             };
27496         }
27497         return Ext.applyIf(o, a);
27498     },
27499
27500     /**
27501      * Collapses the panel body so that it becomes hidden.  Fires the {@link #beforecollapse} event which will
27502      * cancel the collapse action if it returns false.
27503      * @param {Boolean} animate True to animate the transition, else false (defaults to the value of the
27504      * {@link #animCollapse} panel config)
27505      * @return {Ext.Panel} this
27506      */
27507     collapse : function(animate){
27508         if(this.collapsed || this.el.hasFxBlock() || this.fireEvent('beforecollapse', this, animate) === false){
27509             return;
27510         }
27511         var doAnim = animate === true || (animate !== false && this.animCollapse);
27512         this.beforeEffect(doAnim);
27513         this.onCollapse(doAnim, animate);
27514         return this;
27515     },
27516
27517     // private
27518     onCollapse : function(doAnim, animArg){
27519         if(doAnim){
27520             this[this.collapseEl].slideOut(this.slideAnchor,
27521                     Ext.apply(this.createEffect(animArg||true, this.afterCollapse, this),
27522                         this.collapseDefaults));
27523         }else{
27524             this[this.collapseEl].hide(this.hideMode);
27525             this.afterCollapse(false);
27526         }
27527     },
27528
27529     // private
27530     afterCollapse : function(anim){
27531         this.collapsed = true;
27532         this.el.addClass(this.collapsedCls);
27533         if(anim !== false){
27534             this[this.collapseEl].hide(this.hideMode);
27535         }
27536         this.afterEffect(anim);
27537
27538         // Reset lastSize of all sub-components so they KNOW they are in a collapsed container
27539         this.cascade(function(c) {
27540             if (c.lastSize) {
27541                 c.lastSize = { width: undefined, height: undefined };
27542             }
27543         });
27544         this.fireEvent('collapse', this);
27545     },
27546
27547     /**
27548      * Expands the panel body so that it becomes visible.  Fires the {@link #beforeexpand} event which will
27549      * cancel the expand action if it returns false.
27550      * @param {Boolean} animate True to animate the transition, else false (defaults to the value of the
27551      * {@link #animCollapse} panel config)
27552      * @return {Ext.Panel} this
27553      */
27554     expand : function(animate){
27555         if(!this.collapsed || this.el.hasFxBlock() || this.fireEvent('beforeexpand', this, animate) === false){
27556             return;
27557         }
27558         var doAnim = animate === true || (animate !== false && this.animCollapse);
27559         this.el.removeClass(this.collapsedCls);
27560         this.beforeEffect(doAnim);
27561         this.onExpand(doAnim, animate);
27562         return this;
27563     },
27564
27565     // private
27566     onExpand : function(doAnim, animArg){
27567         if(doAnim){
27568             this[this.collapseEl].slideIn(this.slideAnchor,
27569                     Ext.apply(this.createEffect(animArg||true, this.afterExpand, this),
27570                         this.expandDefaults));
27571         }else{
27572             this[this.collapseEl].show(this.hideMode);
27573             this.afterExpand(false);
27574         }
27575     },
27576
27577     // private
27578     afterExpand : function(anim){
27579         this.collapsed = false;
27580         if(anim !== false){
27581             this[this.collapseEl].show(this.hideMode);
27582         }
27583         this.afterEffect(anim);
27584         if (this.deferLayout) {
27585             delete this.deferLayout;
27586             this.doLayout(true);
27587         }
27588         this.fireEvent('expand', this);
27589     },
27590
27591     /**
27592      * Shortcut for performing an {@link #expand} or {@link #collapse} based on the current state of the panel.
27593      * @param {Boolean} animate True to animate the transition, else false (defaults to the value of the
27594      * {@link #animCollapse} panel config)
27595      * @return {Ext.Panel} this
27596      */
27597     toggleCollapse : function(animate){
27598         this[this.collapsed ? 'expand' : 'collapse'](animate);
27599         return this;
27600     },
27601
27602     // private
27603     onDisable : function(){
27604         if(this.rendered && this.maskDisabled){
27605             this.el.mask();
27606         }
27607         Ext.Panel.superclass.onDisable.call(this);
27608     },
27609
27610     // private
27611     onEnable : function(){
27612         if(this.rendered && this.maskDisabled){
27613             this.el.unmask();
27614         }
27615         Ext.Panel.superclass.onEnable.call(this);
27616     },
27617
27618     // private
27619     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
27620         var w = adjWidth,
27621             h = adjHeight;
27622
27623         if(Ext.isDefined(w) || Ext.isDefined(h)){
27624             if(!this.collapsed){
27625                 // First, set the the Panel's body width.
27626                 // If we have auto-widthed it, get the resulting full offset width so we can size the Toolbars to match
27627                 // The Toolbars must not buffer this resize operation because we need to know their heights.
27628
27629                 if(Ext.isNumber(w)){
27630                     this.body.setWidth(w = this.adjustBodyWidth(w - this.getFrameWidth()));
27631                 } else if (w == 'auto') {
27632                     w = this.body.setWidth('auto').dom.offsetWidth;
27633                 } else {
27634                     w = this.body.dom.offsetWidth;
27635                 }
27636
27637                 if(this.tbar){
27638                     this.tbar.setWidth(w);
27639                     if(this.topToolbar){
27640                         this.topToolbar.setSize(w);
27641                     }
27642                 }
27643                 if(this.bbar){
27644                     this.bbar.setWidth(w);
27645                     if(this.bottomToolbar){
27646                         this.bottomToolbar.setSize(w);
27647                         // The bbar does not move on resize without this.
27648                         if (Ext.isIE) {
27649                             this.bbar.setStyle('position', 'static');
27650                             this.bbar.setStyle('position', '');
27651                         }
27652                     }
27653                 }
27654                 if(this.footer){
27655                     this.footer.setWidth(w);
27656                     if(this.fbar){
27657                         this.fbar.setSize(Ext.isIE ? (w - this.footer.getFrameWidth('lr')) : 'auto');
27658                     }
27659                 }
27660
27661                 // At this point, the Toolbars must be layed out for getFrameHeight to find a result.
27662                 if(Ext.isNumber(h)){
27663                     h = Math.max(0, h - this.getFrameHeight());
27664                     //h = Math.max(0, h - (this.getHeight() - this.body.getHeight()));
27665                     this.body.setHeight(h);
27666                 }else if(h == 'auto'){
27667                     this.body.setHeight(h);
27668                 }
27669
27670                 if(this.disabled && this.el._mask){
27671                     this.el._mask.setSize(this.el.dom.clientWidth, this.el.getHeight());
27672                 }
27673             }else{
27674                 // Adds an event to set the correct height afterExpand.  This accounts for the deferHeight flag in panel
27675                 this.queuedBodySize = {width: w, height: h};
27676                 if(!this.queuedExpand && this.allowQueuedExpand !== false){
27677                     this.queuedExpand = true;
27678                     this.on('expand', function(){
27679                         delete this.queuedExpand;
27680                         this.onResize(this.queuedBodySize.width, this.queuedBodySize.height);
27681                     }, this, {single:true});
27682                 }
27683             }
27684             this.onBodyResize(w, h);
27685         }
27686         this.syncShadow();
27687         Ext.Panel.superclass.onResize.call(this, adjWidth, adjHeight, rawWidth, rawHeight);
27688
27689     },
27690
27691     // private
27692     onBodyResize: function(w, h){
27693         this.fireEvent('bodyresize', this, w, h);
27694     },
27695
27696     // private
27697     getToolbarHeight: function(){
27698         var h = 0;
27699         if(this.rendered){
27700             Ext.each(this.toolbars, function(tb){
27701                 h += tb.getHeight();
27702             }, this);
27703         }
27704         return h;
27705     },
27706
27707     // deprecate
27708     adjustBodyHeight : function(h){
27709         return h;
27710     },
27711
27712     // private
27713     adjustBodyWidth : function(w){
27714         return w;
27715     },
27716
27717     // private
27718     onPosition : function(){
27719         this.syncShadow();
27720     },
27721
27722     /**
27723      * Returns the width in pixels of the framing elements of this panel (not including the body width).  To
27724      * retrieve the body width see {@link #getInnerWidth}.
27725      * @return {Number} The frame width
27726      */
27727     getFrameWidth : function(){
27728         var w = this.el.getFrameWidth('lr') + this.bwrap.getFrameWidth('lr');
27729
27730         if(this.frame){
27731             var l = this.bwrap.dom.firstChild;
27732             w += (Ext.fly(l).getFrameWidth('l') + Ext.fly(l.firstChild).getFrameWidth('r'));
27733             w += this.mc.getFrameWidth('lr');
27734         }
27735         return w;
27736     },
27737
27738     /**
27739      * Returns the height in pixels of the framing elements of this panel (including any top and bottom bars and
27740      * header and footer elements, but not including the body height).  To retrieve the body height see {@link #getInnerHeight}.
27741      * @return {Number} The frame height
27742      */
27743     getFrameHeight : function() {
27744         var h  = this.el.getFrameWidth('tb') + this.bwrap.getFrameWidth('tb');
27745         h += (this.tbar ? this.tbar.getHeight() : 0) +
27746              (this.bbar ? this.bbar.getHeight() : 0);
27747
27748         if(this.frame){
27749             h += this.el.dom.firstChild.offsetHeight + this.ft.dom.offsetHeight + this.mc.getFrameWidth('tb');
27750         }else{
27751             h += (this.header ? this.header.getHeight() : 0) +
27752                 (this.footer ? this.footer.getHeight() : 0);
27753         }
27754         return h;
27755     },
27756
27757     /**
27758      * Returns the width in pixels of the body element (not including the width of any framing elements).
27759      * For the frame width see {@link #getFrameWidth}.
27760      * @return {Number} The body width
27761      */
27762     getInnerWidth : function(){
27763         return this.getSize().width - this.getFrameWidth();
27764     },
27765
27766     /**
27767      * Returns the height in pixels of the body element (not including the height of any framing elements).
27768      * For the frame height see {@link #getFrameHeight}.
27769      * @return {Number} The body height
27770      */
27771     getInnerHeight : function(){
27772         return this.body.getHeight();
27773         /* Deprecate
27774             return this.getSize().height - this.getFrameHeight();
27775         */
27776     },
27777
27778     // private
27779     syncShadow : function(){
27780         if(this.floating){
27781             this.el.sync(true);
27782         }
27783     },
27784
27785     // private
27786     getLayoutTarget : function(){
27787         return this.body;
27788     },
27789
27790     // private
27791     getContentTarget : function(){
27792         return this.body;
27793     },
27794
27795     /**
27796      * <p>Sets the title text for the panel and optionally the {@link #iconCls icon class}.</p>
27797      * <p>In order to be able to set the title, a header element must have been created
27798      * for the Panel. This is triggered either by configuring the Panel with a non-blank <code>{@link #title}</code>,
27799      * or configuring it with <code><b>{@link #header}: true</b></code>.</p>
27800      * @param {String} title The title text to set
27801      * @param {String} iconCls (optional) {@link #iconCls iconCls} A user-defined CSS class that provides the icon image for this panel
27802      */
27803     setTitle : function(title, iconCls){
27804         this.title = title;
27805         if(this.header && this.headerAsText){
27806             this.header.child('span').update(title);
27807         }
27808         if(iconCls){
27809             this.setIconClass(iconCls);
27810         }
27811         this.fireEvent('titlechange', this, title);
27812         return this;
27813     },
27814
27815     /**
27816      * Get the {@link Ext.Updater} for this panel. Enables you to perform Ajax updates of this panel's body.
27817      * @return {Ext.Updater} The Updater
27818      */
27819     getUpdater : function(){
27820         return this.body.getUpdater();
27821     },
27822
27823      /**
27824      * Loads this content panel immediately with content returned from an XHR call.
27825      * @param {Object/String/Function} config A config object containing any of the following options:
27826 <pre><code>
27827 panel.load({
27828     url: 'your-url.php',
27829     params: {param1: 'foo', param2: 'bar'}, // or a URL encoded string
27830     callback: yourFunction,
27831     scope: yourObject, // optional scope for the callback
27832     discardUrl: false,
27833     nocache: false,
27834     text: 'Loading...',
27835     timeout: 30,
27836     scripts: false
27837 });
27838 </code></pre>
27839      * The only required property is url. The optional properties nocache, text and scripts
27840      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their
27841      * associated property on this panel Updater instance.
27842      * @return {Ext.Panel} this
27843      */
27844     load : function(){
27845         var um = this.body.getUpdater();
27846         um.update.apply(um, arguments);
27847         return this;
27848     },
27849
27850     // private
27851     beforeDestroy : function(){
27852         Ext.Panel.superclass.beforeDestroy.call(this);
27853         if(this.header){
27854             this.header.removeAllListeners();
27855         }
27856         if(this.tools){
27857             for(var k in this.tools){
27858                 Ext.destroy(this.tools[k]);
27859             }
27860         }
27861         if(this.toolbars.length > 0){
27862             Ext.each(this.toolbars, function(tb){
27863                 tb.un('afterlayout', this.syncHeight, this);
27864                 tb.un('remove', this.syncHeight, this);
27865             }, this);
27866         }
27867         if(Ext.isArray(this.buttons)){
27868             while(this.buttons.length) {
27869                 Ext.destroy(this.buttons[0]);
27870             }
27871         }
27872         if(this.rendered){
27873             Ext.destroy(
27874                 this.ft,
27875                 this.header,
27876                 this.footer,
27877                 this.tbar,
27878                 this.bbar,
27879                 this.body,
27880                 this.mc,
27881                 this.bwrap,
27882                 this.dd
27883             );
27884             if (this.fbar) {
27885                 Ext.destroy(
27886                     this.fbar,
27887                     this.fbar.el
27888                 );
27889             }
27890         }
27891         Ext.destroy(this.toolbars);
27892     },
27893
27894     // private
27895     createClasses : function(){
27896         this.headerCls = this.baseCls + '-header';
27897         this.headerTextCls = this.baseCls + '-header-text';
27898         this.bwrapCls = this.baseCls + '-bwrap';
27899         this.tbarCls = this.baseCls + '-tbar';
27900         this.bodyCls = this.baseCls + '-body';
27901         this.bbarCls = this.baseCls + '-bbar';
27902         this.footerCls = this.baseCls + '-footer';
27903     },
27904
27905     // private
27906     createGhost : function(cls, useShim, appendTo){
27907         var el = document.createElement('div');
27908         el.className = 'x-panel-ghost ' + (cls ? cls : '');
27909         if(this.header){
27910             el.appendChild(this.el.dom.firstChild.cloneNode(true));
27911         }
27912         Ext.fly(el.appendChild(document.createElement('ul'))).setHeight(this.bwrap.getHeight());
27913         el.style.width = this.el.dom.offsetWidth + 'px';;
27914         if(!appendTo){
27915             this.container.dom.appendChild(el);
27916         }else{
27917             Ext.getDom(appendTo).appendChild(el);
27918         }
27919         if(useShim !== false && this.el.useShim !== false){
27920             var layer = new Ext.Layer({shadow:false, useDisplay:true, constrain:false}, el);
27921             layer.show();
27922             return layer;
27923         }else{
27924             return new Ext.Element(el);
27925         }
27926     },
27927
27928     // private
27929     doAutoLoad : function(){
27930         var u = this.body.getUpdater();
27931         if(this.renderer){
27932             u.setRenderer(this.renderer);
27933         }
27934         u.update(Ext.isObject(this.autoLoad) ? this.autoLoad : {url: this.autoLoad});
27935     },
27936
27937     /**
27938      * Retrieve a tool by id.
27939      * @param {String} id
27940      * @return {Object} tool
27941      */
27942     getTool : function(id) {
27943         return this.tools[id];
27944     }
27945
27946 /**
27947  * @cfg {String} autoEl @hide
27948  */
27949 });
27950 Ext.reg('panel', Ext.Panel);
27951 /**
27952  * @class Ext.Editor
27953  * @extends Ext.Component
27954  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
27955  * @constructor
27956  * Create a new Editor
27957  * @param {Object} config The config object
27958  * @xtype editor
27959  */
27960 Ext.Editor = function(field, config){
27961     if(field.field){
27962         this.field = Ext.create(field.field, 'textfield');
27963         config = Ext.apply({}, field); // copy so we don't disturb original config
27964         delete config.field;
27965     }else{
27966         this.field = field;
27967     }
27968     Ext.Editor.superclass.constructor.call(this, config);
27969 };
27970
27971 Ext.extend(Ext.Editor, Ext.Component, {
27972     /**
27973     * @cfg {Ext.form.Field} field
27974     * The Field object (or descendant) or config object for field
27975     */
27976     /**
27977      * @cfg {Boolean} allowBlur
27978      * True to {@link #completeEdit complete the editing process} if in edit mode when the
27979      * field is blurred. Defaults to <tt>true</tt>.
27980      */
27981     allowBlur: true,
27982     /**
27983      * @cfg {Boolean/String} autoSize
27984      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
27985      * or "height" to adopt the height only, "none" to always use the field dimensions. (defaults to false)
27986      */
27987     /**
27988      * @cfg {Boolean} revertInvalid
27989      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
27990      * validation fails (defaults to true)
27991      */
27992     /**
27993      * @cfg {Boolean} ignoreNoChange
27994      * True to skip the edit completion process (no save, no events fired) if the user completes an edit and
27995      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
27996      * will never be ignored.
27997      */
27998     /**
27999      * @cfg {Boolean} hideEl
28000      * False to keep the bound element visible while the editor is displayed (defaults to true)
28001      */
28002     /**
28003      * @cfg {Mixed} value
28004      * The data value of the underlying field (defaults to "")
28005      */
28006     value : "",
28007     /**
28008      * @cfg {String} alignment
28009      * The position to align to (see {@link Ext.Element#alignTo} for more details, defaults to "c-c?").
28010      */
28011     alignment: "c-c?",
28012     /**
28013      * @cfg {Array} offsets
28014      * The offsets to use when aligning (see {@link Ext.Element#alignTo} for more details. Defaults to <tt>[0, 0]</tt>.
28015      */
28016     offsets: [0, 0],
28017     /**
28018      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
28019      * for bottom-right shadow (defaults to "frame")
28020      */
28021     shadow : "frame",
28022     /**
28023      * @cfg {Boolean} constrain True to constrain the editor to the viewport
28024      */
28025     constrain : false,
28026     /**
28027      * @cfg {Boolean} swallowKeys Handle the keydown/keypress events so they don't propagate (defaults to true)
28028      */
28029     swallowKeys : true,
28030     /**
28031      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed. Defaults to <tt>true</tt>.
28032      */
28033     completeOnEnter : true,
28034     /**
28035      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed. Defaults to <tt>true</tt>.
28036      */
28037     cancelOnEsc : true,
28038     /**
28039      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
28040      */
28041     updateEl : false,
28042
28043     initComponent : function(){
28044         Ext.Editor.superclass.initComponent.call(this);
28045         this.addEvents(
28046             /**
28047              * @event beforestartedit
28048              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
28049              * false from the handler of this event.
28050              * @param {Editor} this
28051              * @param {Ext.Element} boundEl The underlying element bound to this editor
28052              * @param {Mixed} value The field value being set
28053              */
28054             "beforestartedit",
28055             /**
28056              * @event startedit
28057              * Fires when this editor is displayed
28058              * @param {Ext.Element} boundEl The underlying element bound to this editor
28059              * @param {Mixed} value The starting field value
28060              */
28061             "startedit",
28062             /**
28063              * @event beforecomplete
28064              * Fires after a change has been made to the field, but before the change is reflected in the underlying
28065              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
28066              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
28067              * event will not fire since no edit actually occurred.
28068              * @param {Editor} this
28069              * @param {Mixed} value The current field value
28070              * @param {Mixed} startValue The original field value
28071              */
28072             "beforecomplete",
28073             /**
28074              * @event complete
28075              * Fires after editing is complete and any changed value has been written to the underlying field.
28076              * @param {Editor} this
28077              * @param {Mixed} value The current field value
28078              * @param {Mixed} startValue The original field value
28079              */
28080             "complete",
28081             /**
28082              * @event canceledit
28083              * Fires after editing has been canceled and the editor's value has been reset.
28084              * @param {Editor} this
28085              * @param {Mixed} value The user-entered field value that was discarded
28086              * @param {Mixed} startValue The original field value that was set back into the editor after cancel
28087              */
28088             "canceledit",
28089             /**
28090              * @event specialkey
28091              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
28092              * {@link Ext.EventObject#getKey} to determine which key was pressed.
28093              * @param {Ext.form.Field} this
28094              * @param {Ext.EventObject} e The event object
28095              */
28096             "specialkey"
28097         );
28098     },
28099
28100     // private
28101     onRender : function(ct, position){
28102         this.el = new Ext.Layer({
28103             shadow: this.shadow,
28104             cls: "x-editor",
28105             parentEl : ct,
28106             shim : this.shim,
28107             shadowOffset: this.shadowOffset || 4,
28108             id: this.id,
28109             constrain: this.constrain
28110         });
28111         if(this.zIndex){
28112             this.el.setZIndex(this.zIndex);
28113         }
28114         this.el.setStyle("overflow", Ext.isGecko ? "auto" : "hidden");
28115         if(this.field.msgTarget != 'title'){
28116             this.field.msgTarget = 'qtip';
28117         }
28118         this.field.inEditor = true;
28119         this.mon(this.field, {
28120             scope: this,
28121             blur: this.onBlur,
28122             specialkey: this.onSpecialKey
28123         });
28124         if(this.field.grow){
28125             this.mon(this.field, "autosize", this.el.sync,  this.el, {delay:1});
28126         }
28127         this.field.render(this.el).show();
28128         this.field.getEl().dom.name = '';
28129         if(this.swallowKeys){
28130             this.field.el.swallowEvent([
28131                 'keypress', // *** Opera
28132                 'keydown'   // *** all other browsers
28133             ]);
28134         }
28135     },
28136
28137     // private
28138     onSpecialKey : function(field, e){
28139         var key = e.getKey(),
28140             complete = this.completeOnEnter && key == e.ENTER,
28141             cancel = this.cancelOnEsc && key == e.ESC;
28142         if(complete || cancel){
28143             e.stopEvent();
28144             if(complete){
28145                 this.completeEdit();
28146             }else{
28147                 this.cancelEdit();
28148             }
28149             if(field.triggerBlur){
28150                 field.triggerBlur();
28151             }
28152         }
28153         this.fireEvent('specialkey', field, e);
28154     },
28155
28156     /**
28157      * Starts the editing process and shows the editor.
28158      * @param {Mixed} el The element to edit
28159      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
28160       * to the innerHTML of el.
28161      */
28162     startEdit : function(el, value){
28163         if(this.editing){
28164             this.completeEdit();
28165         }
28166         this.boundEl = Ext.get(el);
28167         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
28168         if(!this.rendered){
28169             this.render(this.parentEl || document.body);
28170         }
28171         if(this.fireEvent("beforestartedit", this, this.boundEl, v) !== false){
28172             this.startValue = v;
28173             this.field.reset();
28174             this.field.setValue(v);
28175             this.realign(true);
28176             this.editing = true;
28177             this.show();
28178         }
28179     },
28180
28181     // private
28182     doAutoSize : function(){
28183         if(this.autoSize){
28184             var sz = this.boundEl.getSize(),
28185                 fs = this.field.getSize();
28186
28187             switch(this.autoSize){
28188                 case "width":
28189                     this.setSize(sz.width, fs.height);
28190                     break;
28191                 case "height":
28192                     this.setSize(fs.width, sz.height);
28193                     break;
28194                 case "none":
28195                     this.setSize(fs.width, fs.height);
28196                     break;
28197                 default:
28198                     this.setSize(sz.width, sz.height);
28199             }
28200         }
28201     },
28202
28203     /**
28204      * Sets the height and width of this editor.
28205      * @param {Number} width The new width
28206      * @param {Number} height The new height
28207      */
28208     setSize : function(w, h){
28209         delete this.field.lastSize;
28210         this.field.setSize(w, h);
28211         if(this.el){
28212             // IE7 in strict mode doesn't size properly.
28213             if(Ext.isGecko2 || Ext.isOpera || (Ext.isIE7 && Ext.isStrict)){
28214                 // prevent layer scrollbars
28215                 this.el.setSize(w, h);
28216             }
28217             this.el.sync();
28218         }
28219     },
28220
28221     /**
28222      * Realigns the editor to the bound field based on the current alignment config value.
28223      * @param {Boolean} autoSize (optional) True to size the field to the dimensions of the bound element.
28224      */
28225     realign : function(autoSize){
28226         if(autoSize === true){
28227             this.doAutoSize();
28228         }
28229         this.el.alignTo(this.boundEl, this.alignment, this.offsets);
28230     },
28231
28232     /**
28233      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
28234      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
28235      */
28236     completeEdit : function(remainVisible){
28237         if(!this.editing){
28238             return;
28239         }
28240         // Assert combo values first
28241         if (this.field.assertValue) {
28242             this.field.assertValue();
28243         }
28244         var v = this.getValue();
28245         if(!this.field.isValid()){
28246             if(this.revertInvalid !== false){
28247                 this.cancelEdit(remainVisible);
28248             }
28249             return;
28250         }
28251         if(String(v) === String(this.startValue) && this.ignoreNoChange){
28252             this.hideEdit(remainVisible);
28253             return;
28254         }
28255         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
28256             v = this.getValue();
28257             if(this.updateEl && this.boundEl){
28258                 this.boundEl.update(v);
28259             }
28260             this.hideEdit(remainVisible);
28261             this.fireEvent("complete", this, v, this.startValue);
28262         }
28263     },
28264
28265     // private
28266     onShow : function(){
28267         this.el.show();
28268         if(this.hideEl !== false){
28269             this.boundEl.hide();
28270         }
28271         this.field.show().focus(false, true);
28272         this.fireEvent("startedit", this.boundEl, this.startValue);
28273     },
28274
28275     /**
28276      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
28277      * reverted to the original starting value.
28278      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
28279      * cancel (defaults to false)
28280      */
28281     cancelEdit : function(remainVisible){
28282         if(this.editing){
28283             var v = this.getValue();
28284             this.setValue(this.startValue);
28285             this.hideEdit(remainVisible);
28286             this.fireEvent("canceledit", this, v, this.startValue);
28287         }
28288     },
28289
28290     // private
28291     hideEdit: function(remainVisible){
28292         if(remainVisible !== true){
28293             this.editing = false;
28294             this.hide();
28295         }
28296     },
28297
28298     // private
28299     onBlur : function(){
28300         // selectSameEditor flag allows the same editor to be started without onBlur firing on itself
28301         if(this.allowBlur === true && this.editing && this.selectSameEditor !== true){
28302             this.completeEdit();
28303         }
28304     },
28305
28306     // private
28307     onHide : function(){
28308         if(this.editing){
28309             this.completeEdit();
28310             return;
28311         }
28312         this.field.blur();
28313         if(this.field.collapse){
28314             this.field.collapse();
28315         }
28316         this.el.hide();
28317         if(this.hideEl !== false){
28318             this.boundEl.show();
28319         }
28320     },
28321
28322     /**
28323      * Sets the data value of the editor
28324      * @param {Mixed} value Any valid value supported by the underlying field
28325      */
28326     setValue : function(v){
28327         this.field.setValue(v);
28328     },
28329
28330     /**
28331      * Gets the data value of the editor
28332      * @return {Mixed} The data value
28333      */
28334     getValue : function(){
28335         return this.field.getValue();
28336     },
28337
28338     beforeDestroy : function(){
28339         Ext.destroyMembers(this, 'field');
28340
28341         delete this.parentEl;
28342         delete this.boundEl;
28343     }
28344 });
28345 Ext.reg('editor', Ext.Editor);
28346 /**
28347  * @class Ext.ColorPalette
28348  * @extends Ext.Component
28349  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
28350  * Here's an example of typical usage:
28351  * <pre><code>
28352 var cp = new Ext.ColorPalette({value:'993300'});  // initial selected color
28353 cp.render('my-div');
28354
28355 cp.on('select', function(palette, selColor){
28356     // do something with selColor
28357 });
28358 </code></pre>
28359  * @constructor
28360  * Create a new ColorPalette
28361  * @param {Object} config The config object
28362  * @xtype colorpalette
28363  */
28364 Ext.ColorPalette = Ext.extend(Ext.Component, {
28365         /**
28366          * @cfg {String} tpl An existing XTemplate instance to be used in place of the default template for rendering the component.
28367          */
28368     /**
28369      * @cfg {String} itemCls
28370      * The CSS class to apply to the containing element (defaults to 'x-color-palette')
28371      */
28372     itemCls : 'x-color-palette',
28373     /**
28374      * @cfg {String} value
28375      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
28376      * the hex codes are case-sensitive.
28377      */
28378     value : null,
28379     /**
28380      * @cfg {String} clickEvent
28381      * The DOM event that will cause a color to be selected. This can be any valid event name (dblclick, contextmenu). 
28382      * Defaults to <tt>'click'</tt>.
28383      */
28384     clickEvent :'click',
28385     // private
28386     ctype : 'Ext.ColorPalette',
28387
28388     /**
28389      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the {@link #select} event
28390      */
28391     allowReselect : false,
28392
28393     /**
28394      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
28395      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
28396      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
28397      * of colors with the width setting until the box is symmetrical.</p>
28398      * <p>You can override individual colors if needed:</p>
28399      * <pre><code>
28400 var cp = new Ext.ColorPalette();
28401 cp.colors[0] = 'FF0000';  // change the first box to red
28402 </code></pre>
28403
28404 Or you can provide a custom array of your own for complete control:
28405 <pre><code>
28406 var cp = new Ext.ColorPalette();
28407 cp.colors = ['000000', '993300', '333300'];
28408 </code></pre>
28409      * @type Array
28410      */
28411     colors : [
28412         '000000', '993300', '333300', '003300', '003366', '000080', '333399', '333333',
28413         '800000', 'FF6600', '808000', '008000', '008080', '0000FF', '666699', '808080',
28414         'FF0000', 'FF9900', '99CC00', '339966', '33CCCC', '3366FF', '800080', '969696',
28415         'FF00FF', 'FFCC00', 'FFFF00', '00FF00', '00FFFF', '00CCFF', '993366', 'C0C0C0',
28416         'FF99CC', 'FFCC99', 'FFFF99', 'CCFFCC', 'CCFFFF', '99CCFF', 'CC99FF', 'FFFFFF'
28417     ],
28418
28419     /**
28420      * @cfg {Function} handler
28421      * Optional. A function that will handle the select event of this palette.
28422      * The handler is passed the following parameters:<div class="mdetail-params"><ul>
28423      * <li><code>palette</code> : ColorPalette<div class="sub-desc">The {@link #palette Ext.ColorPalette}.</div></li>
28424      * <li><code>color</code> : String<div class="sub-desc">The 6-digit color hex code (without the # symbol).</div></li>
28425      * </ul></div>
28426      */
28427     /**
28428      * @cfg {Object} scope
28429      * The scope (<tt><b>this</b></tt> reference) in which the <code>{@link #handler}</code>
28430      * function will be called.  Defaults to this ColorPalette instance.
28431      */
28432     
28433     // private
28434     initComponent : function(){
28435         Ext.ColorPalette.superclass.initComponent.call(this);
28436         this.addEvents(
28437             /**
28438              * @event select
28439              * Fires when a color is selected
28440              * @param {ColorPalette} this
28441              * @param {String} color The 6-digit color hex code (without the # symbol)
28442              */
28443             'select'
28444         );
28445
28446         if(this.handler){
28447             this.on('select', this.handler, this.scope, true);
28448         }    
28449     },
28450
28451     // private
28452     onRender : function(container, position){
28453         this.autoEl = {
28454             tag: 'div',
28455             cls: this.itemCls
28456         };
28457         Ext.ColorPalette.superclass.onRender.call(this, container, position);
28458         var t = this.tpl || new Ext.XTemplate(
28459             '<tpl for="."><a href="#" class="color-{.}" hidefocus="on"><em><span style="background:#{.}" unselectable="on">&#160;</span></em></a></tpl>'
28460         );
28461         t.overwrite(this.el, this.colors);
28462         this.mon(this.el, this.clickEvent, this.handleClick, this, {delegate: 'a'});
28463         if(this.clickEvent != 'click'){
28464                 this.mon(this.el, 'click', Ext.emptyFn, this, {delegate: 'a', preventDefault: true});
28465         }
28466     },
28467
28468     // private
28469     afterRender : function(){
28470         Ext.ColorPalette.superclass.afterRender.call(this);
28471         if(this.value){
28472             var s = this.value;
28473             this.value = null;
28474             this.select(s, true);
28475         }
28476     },
28477
28478     // private
28479     handleClick : function(e, t){
28480         e.preventDefault();
28481         if(!this.disabled){
28482             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
28483             this.select(c.toUpperCase());
28484         }
28485     },
28486
28487     /**
28488      * Selects the specified color in the palette (fires the {@link #select} event)
28489      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
28490      * @param {Boolean} suppressEvent (optional) True to stop the select event from firing. Defaults to <tt>false</tt>.
28491      */
28492     select : function(color, suppressEvent){
28493         color = color.replace('#', '');
28494         if(color != this.value || this.allowReselect){
28495             var el = this.el;
28496             if(this.value){
28497                 el.child('a.color-'+this.value).removeClass('x-color-palette-sel');
28498             }
28499             el.child('a.color-'+color).addClass('x-color-palette-sel');
28500             this.value = color;
28501             if(suppressEvent !== true){
28502                 this.fireEvent('select', this, color);
28503             }
28504         }
28505     }
28506
28507     /**
28508      * @cfg {String} autoEl @hide
28509      */
28510 });
28511 Ext.reg('colorpalette', Ext.ColorPalette);/**
28512  * @class Ext.DatePicker
28513  * @extends Ext.Component
28514  * <p>A popup date picker. This class is used by the {@link Ext.form.DateField DateField} class
28515  * to allow browsing and selection of valid dates.</p>
28516  * <p>All the string values documented below may be overridden by including an Ext locale file in
28517  * your page.</p>
28518  * @constructor
28519  * Create a new DatePicker
28520  * @param {Object} config The config object
28521  * @xtype datepicker
28522  */
28523 Ext.DatePicker = Ext.extend(Ext.BoxComponent, {
28524     /**
28525      * @cfg {String} todayText
28526      * The text to display on the button that selects the current date (defaults to <code>'Today'</code>)
28527      */
28528     todayText : 'Today',
28529     /**
28530      * @cfg {String} okText
28531      * The text to display on the ok button (defaults to <code>'&#160;OK&#160;'</code> to give the user extra clicking room)
28532      */
28533     okText : '&#160;OK&#160;',
28534     /**
28535      * @cfg {String} cancelText
28536      * The text to display on the cancel button (defaults to <code>'Cancel'</code>)
28537      */
28538     cancelText : 'Cancel',
28539     /**
28540      * @cfg {Function} handler
28541      * Optional. A function that will handle the select event of this picker.
28542      * The handler is passed the following parameters:<div class="mdetail-params"><ul>
28543      * <li><code>picker</code> : DatePicker<div class="sub-desc">This DatePicker.</div></li>
28544      * <li><code>date</code> : Date<div class="sub-desc">The selected date.</div></li>
28545      * </ul></div>
28546      */
28547     /**
28548      * @cfg {Object} scope
28549      * The scope (<code><b>this</b></code> reference) in which the <code>{@link #handler}</code>
28550      * function will be called.  Defaults to this DatePicker instance.
28551      */
28552     /**
28553      * @cfg {String} todayTip
28554      * A string used to format the message for displaying in a tooltip over the button that
28555      * selects the current date. Defaults to <code>'{0} (Spacebar)'</code> where
28556      * the <code>{0}</code> token is replaced by today's date.
28557      */
28558     todayTip : '{0} (Spacebar)',
28559     /**
28560      * @cfg {String} minText
28561      * The error text to display if the minDate validation fails (defaults to <code>'This date is before the minimum date'</code>)
28562      */
28563     minText : 'This date is before the minimum date',
28564     /**
28565      * @cfg {String} maxText
28566      * The error text to display if the maxDate validation fails (defaults to <code>'This date is after the maximum date'</code>)
28567      */
28568     maxText : 'This date is after the maximum date',
28569     /**
28570      * @cfg {String} format
28571      * The default date format string which can be overriden for localization support.  The format must be
28572      * valid according to {@link Date#parseDate} (defaults to <code>'m/d/y'</code>).
28573      */
28574     format : 'm/d/y',
28575     /**
28576      * @cfg {String} disabledDaysText
28577      * The tooltip to display when the date falls on a disabled day (defaults to <code>'Disabled'</code>)
28578      */
28579     disabledDaysText : 'Disabled',
28580     /**
28581      * @cfg {String} disabledDatesText
28582      * The tooltip text to display when the date falls on a disabled date (defaults to <code>'Disabled'</code>)
28583      */
28584     disabledDatesText : 'Disabled',
28585     /**
28586      * @cfg {Array} monthNames
28587      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
28588      */
28589     monthNames : Date.monthNames,
28590     /**
28591      * @cfg {Array} dayNames
28592      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
28593      */
28594     dayNames : Date.dayNames,
28595     /**
28596      * @cfg {String} nextText
28597      * The next month navigation button tooltip (defaults to <code>'Next Month (Control+Right)'</code>)
28598      */
28599     nextText : 'Next Month (Control+Right)',
28600     /**
28601      * @cfg {String} prevText
28602      * The previous month navigation button tooltip (defaults to <code>'Previous Month (Control+Left)'</code>)
28603      */
28604     prevText : 'Previous Month (Control+Left)',
28605     /**
28606      * @cfg {String} monthYearText
28607      * The header month selector tooltip (defaults to <code>'Choose a month (Control+Up/Down to move years)'</code>)
28608      */
28609     monthYearText : 'Choose a month (Control+Up/Down to move years)',
28610     /**
28611      * @cfg {Number} startDay
28612      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
28613      */
28614     startDay : 0,
28615     /**
28616      * @cfg {Boolean} showToday
28617      * False to hide the footer area containing the Today button and disable the keyboard handler for spacebar
28618      * that selects the current date (defaults to <code>true</code>).
28619      */
28620     showToday : true,
28621     /**
28622      * @cfg {Date} minDate
28623      * Minimum allowable date (JavaScript date object, defaults to null)
28624      */
28625     /**
28626      * @cfg {Date} maxDate
28627      * Maximum allowable date (JavaScript date object, defaults to null)
28628      */
28629     /**
28630      * @cfg {Array} disabledDays
28631      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
28632      */
28633     /**
28634      * @cfg {RegExp} disabledDatesRE
28635      * JavaScript regular expression used to disable a pattern of dates (defaults to null).  The {@link #disabledDates}
28636      * config will generate this regex internally, but if you specify disabledDatesRE it will take precedence over the
28637      * disabledDates value.
28638      */
28639     /**
28640      * @cfg {Array} disabledDates
28641      * An array of 'dates' to disable, as strings. These strings will be used to build a dynamic regular
28642      * expression so they are very powerful. Some examples:
28643      * <ul>
28644      * <li>['03/08/2003', '09/16/2003'] would disable those exact dates</li>
28645      * <li>['03/08', '09/16'] would disable those days for every year</li>
28646      * <li>['^03/08'] would only match the beginning (useful if you are using short years)</li>
28647      * <li>['03/../2006'] would disable every day in March 2006</li>
28648      * <li>['^03'] would disable every day in every March</li>
28649      * </ul>
28650      * Note that the format of the dates included in the array should exactly match the {@link #format} config.
28651      * In order to support regular expressions, if you are using a date format that has '.' in it, you will have to
28652      * escape the dot when restricting dates. For example: ['03\\.08\\.03'].
28653      */
28654
28655     // private
28656     // Set by other components to stop the picker focus being updated when the value changes.
28657     focusOnSelect: true,
28658
28659     // default value used to initialise each date in the DatePicker
28660     // (note: 12 noon was chosen because it steers well clear of all DST timezone changes)
28661     initHour: 12, // 24-hour format
28662
28663     // private
28664     initComponent : function(){
28665         Ext.DatePicker.superclass.initComponent.call(this);
28666
28667         this.value = this.value ?
28668                  this.value.clearTime(true) : new Date().clearTime();
28669
28670         this.addEvents(
28671             /**
28672              * @event select
28673              * Fires when a date is selected
28674              * @param {DatePicker} this DatePicker
28675              * @param {Date} date The selected date
28676              */
28677             'select'
28678         );
28679
28680         if(this.handler){
28681             this.on('select', this.handler,  this.scope || this);
28682         }
28683
28684         this.initDisabledDays();
28685     },
28686
28687     // private
28688     initDisabledDays : function(){
28689         if(!this.disabledDatesRE && this.disabledDates){
28690             var dd = this.disabledDates,
28691                 len = dd.length - 1,
28692                 re = '(?:';
28693
28694             Ext.each(dd, function(d, i){
28695                 re += Ext.isDate(d) ? '^' + Ext.escapeRe(d.dateFormat(this.format)) + '$' : dd[i];
28696                 if(i != len){
28697                     re += '|';
28698                 }
28699             }, this);
28700             this.disabledDatesRE = new RegExp(re + ')');
28701         }
28702     },
28703
28704     /**
28705      * Replaces any existing disabled dates with new values and refreshes the DatePicker.
28706      * @param {Array/RegExp} disabledDates An array of date strings (see the {@link #disabledDates} config
28707      * for details on supported values), or a JavaScript regular expression used to disable a pattern of dates.
28708      */
28709     setDisabledDates : function(dd){
28710         if(Ext.isArray(dd)){
28711             this.disabledDates = dd;
28712             this.disabledDatesRE = null;
28713         }else{
28714             this.disabledDatesRE = dd;
28715         }
28716         this.initDisabledDays();
28717         this.update(this.value, true);
28718     },
28719
28720     /**
28721      * Replaces any existing disabled days (by index, 0-6) with new values and refreshes the DatePicker.
28722      * @param {Array} disabledDays An array of disabled day indexes. See the {@link #disabledDays} config
28723      * for details on supported values.
28724      */
28725     setDisabledDays : function(dd){
28726         this.disabledDays = dd;
28727         this.update(this.value, true);
28728     },
28729
28730     /**
28731      * Replaces any existing {@link #minDate} with the new value and refreshes the DatePicker.
28732      * @param {Date} value The minimum date that can be selected
28733      */
28734     setMinDate : function(dt){
28735         this.minDate = dt;
28736         this.update(this.value, true);
28737     },
28738
28739     /**
28740      * Replaces any existing {@link #maxDate} with the new value and refreshes the DatePicker.
28741      * @param {Date} value The maximum date that can be selected
28742      */
28743     setMaxDate : function(dt){
28744         this.maxDate = dt;
28745         this.update(this.value, true);
28746     },
28747
28748     /**
28749      * Sets the value of the date field
28750      * @param {Date} value The date to set
28751      */
28752     setValue : function(value){
28753         this.value = value.clearTime(true);
28754         this.update(this.value);
28755     },
28756
28757     /**
28758      * Gets the current selected value of the date field
28759      * @return {Date} The selected date
28760      */
28761     getValue : function(){
28762         return this.value;
28763     },
28764
28765     // private
28766     focus : function(){
28767         this.update(this.activeDate);
28768     },
28769
28770     // private
28771     onEnable: function(initial){
28772         Ext.DatePicker.superclass.onEnable.call(this);
28773         this.doDisabled(false);
28774         this.update(initial ? this.value : this.activeDate);
28775         if(Ext.isIE){
28776             this.el.repaint();
28777         }
28778
28779     },
28780
28781     // private
28782     onDisable : function(){
28783         Ext.DatePicker.superclass.onDisable.call(this);
28784         this.doDisabled(true);
28785         if(Ext.isIE && !Ext.isIE8){
28786             /* Really strange problem in IE6/7, when disabled, have to explicitly
28787              * repaint each of the nodes to get them to display correctly, simply
28788              * calling repaint on the main element doesn't appear to be enough.
28789              */
28790              Ext.each([].concat(this.textNodes, this.el.query('th span')), function(el){
28791                  Ext.fly(el).repaint();
28792              });
28793         }
28794     },
28795
28796     // private
28797     doDisabled : function(disabled){
28798         this.keyNav.setDisabled(disabled);
28799         this.prevRepeater.setDisabled(disabled);
28800         this.nextRepeater.setDisabled(disabled);
28801         if(this.showToday){
28802             this.todayKeyListener.setDisabled(disabled);
28803             this.todayBtn.setDisabled(disabled);
28804         }
28805     },
28806
28807     // private
28808     onRender : function(container, position){
28809         var m = [
28810              '<table cellspacing="0">',
28811                 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'">&#160;</a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'">&#160;</a></td></tr>',
28812                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'],
28813                 dn = this.dayNames,
28814                 i;
28815         for(i = 0; i < 7; i++){
28816             var d = this.startDay+i;
28817             if(d > 6){
28818                 d = d-7;
28819             }
28820             m.push('<th><span>', dn[d].substr(0,1), '</span></th>');
28821         }
28822         m[m.length] = '</tr></thead><tbody><tr>';
28823         for(i = 0; i < 42; i++) {
28824             if(i % 7 === 0 && i !== 0){
28825                 m[m.length] = '</tr><tr>';
28826             }
28827             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
28828         }
28829         m.push('</tr></tbody></table></td></tr>',
28830                 this.showToday ? '<tr><td colspan="3" class="x-date-bottom" align="center"></td></tr>' : '',
28831                 '</table><div class="x-date-mp"></div>');
28832
28833         var el = document.createElement('div');
28834         el.className = 'x-date-picker';
28835         el.innerHTML = m.join('');
28836
28837         container.dom.insertBefore(el, position);
28838
28839         this.el = Ext.get(el);
28840         this.eventEl = Ext.get(el.firstChild);
28841
28842         this.prevRepeater = new Ext.util.ClickRepeater(this.el.child('td.x-date-left a'), {
28843             handler: this.showPrevMonth,
28844             scope: this,
28845             preventDefault:true,
28846             stopDefault:true
28847         });
28848
28849         this.nextRepeater = new Ext.util.ClickRepeater(this.el.child('td.x-date-right a'), {
28850             handler: this.showNextMonth,
28851             scope: this,
28852             preventDefault:true,
28853             stopDefault:true
28854         });
28855
28856         this.monthPicker = this.el.down('div.x-date-mp');
28857         this.monthPicker.enableDisplayMode('block');
28858
28859         this.keyNav = new Ext.KeyNav(this.eventEl, {
28860             'left' : function(e){
28861                 if(e.ctrlKey){
28862                     this.showPrevMonth();
28863                 }else{
28864                     this.update(this.activeDate.add('d', -1));
28865                 }
28866             },
28867
28868             'right' : function(e){
28869                 if(e.ctrlKey){
28870                     this.showNextMonth();
28871                 }else{
28872                     this.update(this.activeDate.add('d', 1));
28873                 }
28874             },
28875
28876             'up' : function(e){
28877                 if(e.ctrlKey){
28878                     this.showNextYear();
28879                 }else{
28880                     this.update(this.activeDate.add('d', -7));
28881                 }
28882             },
28883
28884             'down' : function(e){
28885                 if(e.ctrlKey){
28886                     this.showPrevYear();
28887                 }else{
28888                     this.update(this.activeDate.add('d', 7));
28889                 }
28890             },
28891
28892             'pageUp' : function(e){
28893                 this.showNextMonth();
28894             },
28895
28896             'pageDown' : function(e){
28897                 this.showPrevMonth();
28898             },
28899
28900             'enter' : function(e){
28901                 e.stopPropagation();
28902                 return true;
28903             },
28904
28905             scope : this
28906         });
28907
28908         this.el.unselectable();
28909
28910         this.cells = this.el.select('table.x-date-inner tbody td');
28911         this.textNodes = this.el.query('table.x-date-inner tbody span');
28912
28913         this.mbtn = new Ext.Button({
28914             text: '&#160;',
28915             tooltip: this.monthYearText,
28916             renderTo: this.el.child('td.x-date-middle', true)
28917         });
28918         this.mbtn.el.child('em').addClass('x-btn-arrow');
28919
28920         if(this.showToday){
28921             this.todayKeyListener = this.eventEl.addKeyListener(Ext.EventObject.SPACE, this.selectToday,  this);
28922             var today = (new Date()).dateFormat(this.format);
28923             this.todayBtn = new Ext.Button({
28924                 renderTo: this.el.child('td.x-date-bottom', true),
28925                 text: String.format(this.todayText, today),
28926                 tooltip: String.format(this.todayTip, today),
28927                 handler: this.selectToday,
28928                 scope: this
28929             });
28930         }
28931         this.mon(this.eventEl, 'mousewheel', this.handleMouseWheel, this);
28932         this.mon(this.eventEl, 'click', this.handleDateClick,  this, {delegate: 'a.x-date-date'});
28933         this.mon(this.mbtn, 'click', this.showMonthPicker, this);
28934         this.onEnable(true);
28935     },
28936
28937     // private
28938     createMonthPicker : function(){
28939         if(!this.monthPicker.dom.firstChild){
28940             var buf = ['<table border="0" cellspacing="0">'];
28941             for(var i = 0; i < 6; i++){
28942                 buf.push(
28943                     '<tr><td class="x-date-mp-month"><a href="#">', Date.getShortMonthName(i), '</a></td>',
28944                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', Date.getShortMonthName(i + 6), '</a></td>',
28945                     i === 0 ?
28946                     '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
28947                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
28948                 );
28949             }
28950             buf.push(
28951                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
28952                     this.okText,
28953                     '</button><button type="button" class="x-date-mp-cancel">',
28954                     this.cancelText,
28955                     '</button></td></tr>',
28956                 '</table>'
28957             );
28958             this.monthPicker.update(buf.join(''));
28959
28960             this.mon(this.monthPicker, 'click', this.onMonthClick, this);
28961             this.mon(this.monthPicker, 'dblclick', this.onMonthDblClick, this);
28962
28963             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
28964             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
28965
28966             this.mpMonths.each(function(m, a, i){
28967                 i += 1;
28968                 if((i%2) === 0){
28969                     m.dom.xmonth = 5 + Math.round(i * 0.5);
28970                 }else{
28971                     m.dom.xmonth = Math.round((i-1) * 0.5);
28972                 }
28973             });
28974         }
28975     },
28976
28977     // private
28978     showMonthPicker : function(){
28979         if(!this.disabled){
28980             this.createMonthPicker();
28981             var size = this.el.getSize();
28982             this.monthPicker.setSize(size);
28983             this.monthPicker.child('table').setSize(size);
28984
28985             this.mpSelMonth = (this.activeDate || this.value).getMonth();
28986             this.updateMPMonth(this.mpSelMonth);
28987             this.mpSelYear = (this.activeDate || this.value).getFullYear();
28988             this.updateMPYear(this.mpSelYear);
28989
28990             this.monthPicker.slideIn('t', {duration:0.2});
28991         }
28992     },
28993
28994     // private
28995     updateMPYear : function(y){
28996         this.mpyear = y;
28997         var ys = this.mpYears.elements;
28998         for(var i = 1; i <= 10; i++){
28999             var td = ys[i-1], y2;
29000             if((i%2) === 0){
29001                 y2 = y + Math.round(i * 0.5);
29002                 td.firstChild.innerHTML = y2;
29003                 td.xyear = y2;
29004             }else{
29005                 y2 = y - (5-Math.round(i * 0.5));
29006                 td.firstChild.innerHTML = y2;
29007                 td.xyear = y2;
29008             }
29009             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
29010         }
29011     },
29012
29013     // private
29014     updateMPMonth : function(sm){
29015         this.mpMonths.each(function(m, a, i){
29016             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
29017         });
29018     },
29019
29020     // private
29021     selectMPMonth : function(m){
29022
29023     },
29024
29025     // private
29026     onMonthClick : function(e, t){
29027         e.stopEvent();
29028         var el = new Ext.Element(t), pn;
29029         if(el.is('button.x-date-mp-cancel')){
29030             this.hideMonthPicker();
29031         }
29032         else if(el.is('button.x-date-mp-ok')){
29033             var d = new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate());
29034             if(d.getMonth() != this.mpSelMonth){
29035                 // 'fix' the JS rolling date conversion if needed
29036                 d = new Date(this.mpSelYear, this.mpSelMonth, 1).getLastDateOfMonth();
29037             }
29038             this.update(d);
29039             this.hideMonthPicker();
29040         }
29041         else if((pn = el.up('td.x-date-mp-month', 2))){
29042             this.mpMonths.removeClass('x-date-mp-sel');
29043             pn.addClass('x-date-mp-sel');
29044             this.mpSelMonth = pn.dom.xmonth;
29045         }
29046         else if((pn = el.up('td.x-date-mp-year', 2))){
29047             this.mpYears.removeClass('x-date-mp-sel');
29048             pn.addClass('x-date-mp-sel');
29049             this.mpSelYear = pn.dom.xyear;
29050         }
29051         else if(el.is('a.x-date-mp-prev')){
29052             this.updateMPYear(this.mpyear-10);
29053         }
29054         else if(el.is('a.x-date-mp-next')){
29055             this.updateMPYear(this.mpyear+10);
29056         }
29057     },
29058
29059     // private
29060     onMonthDblClick : function(e, t){
29061         e.stopEvent();
29062         var el = new Ext.Element(t), pn;
29063         if((pn = el.up('td.x-date-mp-month', 2))){
29064             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
29065             this.hideMonthPicker();
29066         }
29067         else if((pn = el.up('td.x-date-mp-year', 2))){
29068             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29069             this.hideMonthPicker();
29070         }
29071     },
29072
29073     // private
29074     hideMonthPicker : function(disableAnim){
29075         if(this.monthPicker){
29076             if(disableAnim === true){
29077                 this.monthPicker.hide();
29078             }else{
29079                 this.monthPicker.slideOut('t', {duration:0.2});
29080             }
29081         }
29082     },
29083
29084     // private
29085     showPrevMonth : function(e){
29086         this.update(this.activeDate.add('mo', -1));
29087     },
29088
29089     // private
29090     showNextMonth : function(e){
29091         this.update(this.activeDate.add('mo', 1));
29092     },
29093
29094     // private
29095     showPrevYear : function(){
29096         this.update(this.activeDate.add('y', -1));
29097     },
29098
29099     // private
29100     showNextYear : function(){
29101         this.update(this.activeDate.add('y', 1));
29102     },
29103
29104     // private
29105     handleMouseWheel : function(e){
29106         e.stopEvent();
29107         if(!this.disabled){
29108             var delta = e.getWheelDelta();
29109             if(delta > 0){
29110                 this.showPrevMonth();
29111             } else if(delta < 0){
29112                 this.showNextMonth();
29113             }
29114         }
29115     },
29116
29117     // private
29118     handleDateClick : function(e, t){
29119         e.stopEvent();
29120         if(!this.disabled && t.dateValue && !Ext.fly(t.parentNode).hasClass('x-date-disabled')){
29121             this.cancelFocus = this.focusOnSelect === false;
29122             this.setValue(new Date(t.dateValue));
29123             delete this.cancelFocus;
29124             this.fireEvent('select', this, this.value);
29125         }
29126     },
29127
29128     // private
29129     selectToday : function(){
29130         if(this.todayBtn && !this.todayBtn.disabled){
29131             this.setValue(new Date().clearTime());
29132             this.fireEvent('select', this, this.value);
29133         }
29134     },
29135
29136     // private
29137     update : function(date, forceRefresh){
29138         if(this.rendered){
29139             var vd = this.activeDate, vis = this.isVisible();
29140             this.activeDate = date;
29141             if(!forceRefresh && vd && this.el){
29142                 var t = date.getTime();
29143                 if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
29144                     this.cells.removeClass('x-date-selected');
29145                     this.cells.each(function(c){
29146                        if(c.dom.firstChild.dateValue == t){
29147                            c.addClass('x-date-selected');
29148                            if(vis && !this.cancelFocus){
29149                                Ext.fly(c.dom.firstChild).focus(50);
29150                            }
29151                            return false;
29152                        }
29153                     }, this);
29154                     return;
29155                 }
29156             }
29157             var days = date.getDaysInMonth(),
29158                 firstOfMonth = date.getFirstDateOfMonth(),
29159                 startingPos = firstOfMonth.getDay()-this.startDay;
29160
29161             if(startingPos < 0){
29162                 startingPos += 7;
29163             }
29164             days += startingPos;
29165
29166             var pm = date.add('mo', -1),
29167                 prevStart = pm.getDaysInMonth()-startingPos,
29168                 cells = this.cells.elements,
29169                 textEls = this.textNodes,
29170                 // convert everything to numbers so it's fast
29171                 d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart, this.initHour)),
29172                 today = new Date().clearTime().getTime(),
29173                 sel = date.clearTime(true).getTime(),
29174                 min = this.minDate ? this.minDate.clearTime(true) : Number.NEGATIVE_INFINITY,
29175                 max = this.maxDate ? this.maxDate.clearTime(true) : Number.POSITIVE_INFINITY,
29176                 ddMatch = this.disabledDatesRE,
29177                 ddText = this.disabledDatesText,
29178                 ddays = this.disabledDays ? this.disabledDays.join('') : false,
29179                 ddaysText = this.disabledDaysText,
29180                 format = this.format;
29181
29182             if(this.showToday){
29183                 var td = new Date().clearTime(),
29184                     disable = (td < min || td > max ||
29185                     (ddMatch && format && ddMatch.test(td.dateFormat(format))) ||
29186                     (ddays && ddays.indexOf(td.getDay()) != -1));
29187
29188                 if(!this.disabled){
29189                     this.todayBtn.setDisabled(disable);
29190                     this.todayKeyListener[disable ? 'disable' : 'enable']();
29191                 }
29192             }
29193
29194             var setCellClass = function(cal, cell){
29195                 cell.title = '';
29196                 var t = d.clearTime(true).getTime();
29197                 cell.firstChild.dateValue = t;
29198                 if(t == today){
29199                     cell.className += ' x-date-today';
29200                     cell.title = cal.todayText;
29201                 }
29202                 if(t == sel){
29203                     cell.className += ' x-date-selected';
29204                     if(vis){
29205                         Ext.fly(cell.firstChild).focus(50);
29206                     }
29207                 }
29208                 // disabling
29209                 if(t < min) {
29210                     cell.className = ' x-date-disabled';
29211                     cell.title = cal.minText;
29212                     return;
29213                 }
29214                 if(t > max) {
29215                     cell.className = ' x-date-disabled';
29216                     cell.title = cal.maxText;
29217                     return;
29218                 }
29219                 if(ddays){
29220                     if(ddays.indexOf(d.getDay()) != -1){
29221                         cell.title = ddaysText;
29222                         cell.className = ' x-date-disabled';
29223                     }
29224                 }
29225                 if(ddMatch && format){
29226                     var fvalue = d.dateFormat(format);
29227                     if(ddMatch.test(fvalue)){
29228                         cell.title = ddText.replace('%0', fvalue);
29229                         cell.className = ' x-date-disabled';
29230                     }
29231                 }
29232             };
29233
29234             var i = 0;
29235             for(; i < startingPos; i++) {
29236                 textEls[i].innerHTML = (++prevStart);
29237                 d.setDate(d.getDate()+1);
29238                 cells[i].className = 'x-date-prevday';
29239                 setCellClass(this, cells[i]);
29240             }
29241             for(; i < days; i++){
29242                 var intDay = i - startingPos + 1;
29243                 textEls[i].innerHTML = (intDay);
29244                 d.setDate(d.getDate()+1);
29245                 cells[i].className = 'x-date-active';
29246                 setCellClass(this, cells[i]);
29247             }
29248             var extraDays = 0;
29249             for(; i < 42; i++) {
29250                  textEls[i].innerHTML = (++extraDays);
29251                  d.setDate(d.getDate()+1);
29252                  cells[i].className = 'x-date-nextday';
29253                  setCellClass(this, cells[i]);
29254             }
29255
29256             this.mbtn.setText(this.monthNames[date.getMonth()] + ' ' + date.getFullYear());
29257
29258             if(!this.internalRender){
29259                 var main = this.el.dom.firstChild,
29260                     w = main.offsetWidth;
29261                 this.el.setWidth(w + this.el.getBorderWidth('lr'));
29262                 Ext.fly(main).setWidth(w);
29263                 this.internalRender = true;
29264                 // opera does not respect the auto grow header center column
29265                 // then, after it gets a width opera refuses to recalculate
29266                 // without a second pass
29267                 if(Ext.isOpera && !this.secondPass){
29268                     main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + 'px';
29269                     this.secondPass = true;
29270                     this.update.defer(10, this, [date]);
29271                 }
29272             }
29273         }
29274     },
29275
29276     // private
29277     beforeDestroy : function() {
29278         if(this.rendered){
29279             Ext.destroy(
29280                 this.keyNav,
29281                 this.monthPicker,
29282                 this.eventEl,
29283                 this.mbtn,
29284                 this.nextRepeater,
29285                 this.prevRepeater,
29286                 this.cells.el,
29287                 this.todayBtn
29288             );
29289             delete this.textNodes;
29290             delete this.cells.elements;
29291         }
29292     }
29293
29294     /**
29295      * @cfg {String} autoEl @hide
29296      */
29297 });
29298
29299 Ext.reg('datepicker', Ext.DatePicker);
29300 /**
29301  * @class Ext.LoadMask
29302  * A simple utility class for generically masking elements while loading data.  If the {@link #store}
29303  * config option is specified, the masking will be automatically synchronized with the store's loading
29304  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
29305  * element's Updater load indicator and will be destroyed after the initial load.
29306  * <p>Example usage:</p>
29307  *<pre><code>
29308 // Basic mask:
29309 var myMask = new Ext.LoadMask(Ext.getBody(), {msg:"Please wait..."});
29310 myMask.show();
29311 </code></pre>
29312  * @constructor
29313  * Create a new LoadMask
29314  * @param {Mixed} el The element or DOM node, or its id
29315  * @param {Object} config The config object
29316  */
29317 Ext.LoadMask = function(el, config){
29318     this.el = Ext.get(el);
29319     Ext.apply(this, config);
29320     if(this.store){
29321         this.store.on({
29322             scope: this,
29323             beforeload: this.onBeforeLoad,
29324             load: this.onLoad,
29325             exception: this.onLoad
29326         });
29327         this.removeMask = Ext.value(this.removeMask, false);
29328     }else{
29329         var um = this.el.getUpdater();
29330         um.showLoadIndicator = false; // disable the default indicator
29331         um.on({
29332             scope: this,
29333             beforeupdate: this.onBeforeLoad,
29334             update: this.onLoad,
29335             failure: this.onLoad
29336         });
29337         this.removeMask = Ext.value(this.removeMask, true);
29338     }
29339 };
29340
29341 Ext.LoadMask.prototype = {
29342     /**
29343      * @cfg {Ext.data.Store} store
29344      * Optional Store to which the mask is bound. The mask is displayed when a load request is issued, and
29345      * hidden on either load sucess, or load fail.
29346      */
29347     /**
29348      * @cfg {Boolean} removeMask
29349      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
29350      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
29351      */
29352     /**
29353      * @cfg {String} msg
29354      * The text to display in a centered loading message box (defaults to 'Loading...')
29355      */
29356     msg : 'Loading...',
29357     /**
29358      * @cfg {String} msgCls
29359      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
29360      */
29361     msgCls : 'x-mask-loading',
29362
29363     /**
29364      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
29365      * @type Boolean
29366      */
29367     disabled: false,
29368
29369     /**
29370      * Disables the mask to prevent it from being displayed
29371      */
29372     disable : function(){
29373        this.disabled = true;
29374     },
29375
29376     /**
29377      * Enables the mask so that it can be displayed
29378      */
29379     enable : function(){
29380         this.disabled = false;
29381     },
29382
29383     // private
29384     onLoad : function(){
29385         this.el.unmask(this.removeMask);
29386     },
29387
29388     // private
29389     onBeforeLoad : function(){
29390         if(!this.disabled){
29391             this.el.mask(this.msg, this.msgCls);
29392         }
29393     },
29394
29395     /**
29396      * Show this LoadMask over the configured Element.
29397      */
29398     show: function(){
29399         this.onBeforeLoad();
29400     },
29401
29402     /**
29403      * Hide this LoadMask.
29404      */
29405     hide: function(){
29406         this.onLoad();
29407     },
29408
29409     // private
29410     destroy : function(){
29411         if(this.store){
29412             this.store.un('beforeload', this.onBeforeLoad, this);
29413             this.store.un('load', this.onLoad, this);
29414             this.store.un('exception', this.onLoad, this);
29415         }else{
29416             var um = this.el.getUpdater();
29417             um.un('beforeupdate', this.onBeforeLoad, this);
29418             um.un('update', this.onLoad, this);
29419             um.un('failure', this.onLoad, this);
29420         }
29421     }
29422 };Ext.ns('Ext.slider');
29423
29424 /**
29425  * @class Ext.slider.Thumb
29426  * @extends Object
29427  * Represents a single thumb element on a Slider. This would not usually be created manually and would instead
29428  * be created internally by an {@link Ext.slider.MultiSlider Ext.Slider}.
29429  */
29430 Ext.slider.Thumb = Ext.extend(Object, {
29431     
29432     /**
29433      * True while the thumb is in a drag operation
29434      * @type Boolean
29435      */
29436     dragging: false,
29437
29438     /**
29439      * @constructor
29440      * @cfg {Ext.slider.MultiSlider} slider The Slider to render to (required)
29441      */
29442     constructor: function(config) {
29443         /**
29444          * @property slider
29445          * @type Ext.slider.MultiSlider
29446          * The slider this thumb is contained within
29447          */
29448         Ext.apply(this, config || {}, {
29449             cls: 'x-slider-thumb',
29450
29451             /**
29452              * @cfg {Boolean} constrain True to constrain the thumb so that it cannot overlap its siblings
29453              */
29454             constrain: false
29455         });
29456
29457         Ext.slider.Thumb.superclass.constructor.call(this, config);
29458
29459         if (this.slider.vertical) {
29460             Ext.apply(this, Ext.slider.Thumb.Vertical);
29461         }
29462     },
29463
29464     /**
29465      * Renders the thumb into a slider
29466      */
29467     render: function() {
29468         this.el = this.slider.innerEl.insertFirst({cls: this.cls});
29469
29470         this.initEvents();
29471     },
29472
29473     /**
29474      * Enables the thumb if it is currently disabled
29475      */
29476     enable: function() {
29477         this.disabled = false;
29478         this.el.removeClass(this.slider.disabledClass);
29479     },
29480
29481     /**
29482      * Disables the thumb if it is currently enabled
29483      */
29484     disable: function() {
29485         this.disabled = true;
29486         this.el.addClass(this.slider.disabledClass);
29487     },
29488
29489     /**
29490      * Sets up an Ext.dd.DragTracker for this thumb
29491      */
29492     initEvents: function() {
29493         var el = this.el;
29494
29495         el.addClassOnOver('x-slider-thumb-over');
29496
29497         this.tracker = new Ext.dd.DragTracker({
29498             onBeforeStart: this.onBeforeDragStart.createDelegate(this),
29499             onStart      : this.onDragStart.createDelegate(this),
29500             onDrag       : this.onDrag.createDelegate(this),
29501             onEnd        : this.onDragEnd.createDelegate(this),
29502             tolerance    : 3,
29503             autoStart    : 300
29504         });
29505
29506         this.tracker.initEl(el);
29507     },
29508
29509     /**
29510      * @private
29511      * This is tied into the internal Ext.dd.DragTracker. If the slider is currently disabled,
29512      * this returns false to disable the DragTracker too.
29513      * @return {Boolean} False if the slider is currently disabled
29514      */
29515     onBeforeDragStart : function(e) {
29516         if (this.disabled) {
29517             return false;
29518         } else {
29519             this.slider.promoteThumb(this);
29520             return true;
29521         }
29522     },
29523
29524     /**
29525      * @private
29526      * This is tied into the internal Ext.dd.DragTracker's onStart template method. Adds the drag CSS class
29527      * to the thumb and fires the 'dragstart' event
29528      */
29529     onDragStart: function(e){
29530         this.el.addClass('x-slider-thumb-drag');
29531         this.dragging = true;
29532         this.dragStartValue = this.value;
29533
29534         this.slider.fireEvent('dragstart', this.slider, e, this);
29535     },
29536
29537     /**
29538      * @private
29539      * This is tied into the internal Ext.dd.DragTracker's onDrag template method. This is called every time
29540      * the DragTracker detects a drag movement. It updates the Slider's value using the position of the drag
29541      */
29542     onDrag: function(e) {
29543         var slider   = this.slider,
29544             index    = this.index,
29545             newValue = this.getNewValue();
29546
29547         if (this.constrain) {
29548             var above = slider.thumbs[index + 1],
29549                 below = slider.thumbs[index - 1];
29550
29551             if (below != undefined && newValue <= below.value) newValue = below.value;
29552             if (above != undefined && newValue >= above.value) newValue = above.value;
29553         }
29554
29555         slider.setValue(index, newValue, false);
29556         slider.fireEvent('drag', slider, e, this);
29557     },
29558
29559     getNewValue: function() {
29560         var slider   = this.slider,
29561             pos      = slider.innerEl.translatePoints(this.tracker.getXY());
29562
29563         return Ext.util.Format.round(slider.reverseValue(pos.left), slider.decimalPrecision);
29564     },
29565
29566     /**
29567      * @private
29568      * This is tied to the internal Ext.dd.DragTracker's onEnd template method. Removes the drag CSS class and
29569      * fires the 'changecomplete' event with the new value
29570      */
29571     onDragEnd: function(e) {
29572         var slider = this.slider,
29573             value  = this.value;
29574
29575         this.el.removeClass('x-slider-thumb-drag');
29576
29577         this.dragging = false;
29578         slider.fireEvent('dragend', slider, e);
29579
29580         if (this.dragStartValue != value) {
29581             slider.fireEvent('changecomplete', slider, value, this);
29582         }
29583     },
29584     
29585     /**
29586      * @private
29587      * Destroys the thumb
29588      */
29589     destroy: function(){
29590         Ext.destroyMembers(this, 'tracker', 'el');
29591     }
29592 });
29593
29594 /**
29595  * @class Ext.slider.MultiSlider
29596  * @extends Ext.BoxComponent
29597  * Slider which supports vertical or horizontal orientation, keyboard adjustments, configurable snapping, axis clicking and animation. Can be added as an item to any container. Example usage:
29598 <pre>
29599 new Ext.Slider({
29600     renderTo: Ext.getBody(),
29601     width: 200,
29602     value: 50,
29603     increment: 10,
29604     minValue: 0,
29605     maxValue: 100
29606 });
29607 </pre>
29608  * Sliders can be created with more than one thumb handle by passing an array of values instead of a single one:
29609 <pre>
29610 new Ext.Slider({
29611     renderTo: Ext.getBody(),
29612     width: 200,
29613     values: [25, 50, 75],
29614     minValue: 0,
29615     maxValue: 100,
29616
29617     //this defaults to true, setting to false allows the thumbs to pass each other
29618     {@link #constrainThumbs}: false
29619 });
29620 </pre>
29621  */
29622 Ext.slider.MultiSlider = Ext.extend(Ext.BoxComponent, {
29623     /**
29624      * @cfg {Number} value The value to initialize the slider with. Defaults to minValue.
29625      */
29626     /**
29627      * @cfg {Boolean} vertical Orient the Slider vertically rather than horizontally, defaults to false.
29628      */
29629     vertical: false,
29630     /**
29631      * @cfg {Number} minValue The minimum value for the Slider. Defaults to 0.
29632      */
29633     minValue: 0,
29634     /**
29635      * @cfg {Number} maxValue The maximum value for the Slider. Defaults to 100.
29636      */
29637     maxValue: 100,
29638     /**
29639      * @cfg {Number/Boolean} decimalPrecision.
29640      * <p>The number of decimal places to which to round the Slider's value. Defaults to 0.</p>
29641      * <p>To disable rounding, configure as <tt><b>false</b></tt>.</p>
29642      */
29643     decimalPrecision: 0,
29644     /**
29645      * @cfg {Number} keyIncrement How many units to change the Slider when adjusting with keyboard navigation. Defaults to 1. If the increment config is larger, it will be used instead.
29646      */
29647     keyIncrement: 1,
29648     /**
29649      * @cfg {Number} increment How many units to change the slider when adjusting by drag and drop. Use this option to enable 'snapping'.
29650      */
29651     increment: 0,
29652
29653     /**
29654      * @private
29655      * @property clickRange
29656      * @type Array
29657      * Determines whether or not a click to the slider component is considered to be a user request to change the value. Specified as an array of [top, bottom],
29658      * the click event's 'top' property is compared to these numbers and the click only considered a change request if it falls within them. e.g. if the 'top'
29659      * value of the click event is 4 or 16, the click is not considered a change request as it falls outside of the [5, 15] range
29660      */
29661     clickRange: [5,15],
29662
29663     /**
29664      * @cfg {Boolean} clickToChange Determines whether or not clicking on the Slider axis will change the slider. Defaults to true
29665      */
29666     clickToChange : true,
29667     /**
29668      * @cfg {Boolean} animate Turn on or off animation. Defaults to true
29669      */
29670     animate: true,
29671     /**
29672      * @cfg {Boolean} constrainThumbs True to disallow thumbs from overlapping one another. Defaults to true
29673      */
29674     constrainThumbs: true,
29675
29676     /**
29677      * @private
29678      * @property topThumbZIndex
29679      * @type Number
29680      * The number used internally to set the z index of the top thumb (see promoteThumb for details)
29681      */
29682     topThumbZIndex: 10000,
29683
29684     // private override
29685     initComponent : function(){
29686         if(!Ext.isDefined(this.value)){
29687             this.value = this.minValue;
29688         }
29689
29690         /**
29691          * @property thumbs
29692          * @type Array
29693          * Array containing references to each thumb
29694          */
29695         this.thumbs = [];
29696
29697         Ext.slider.MultiSlider.superclass.initComponent.call(this);
29698
29699         this.keyIncrement = Math.max(this.increment, this.keyIncrement);
29700         this.addEvents(
29701             /**
29702              * @event beforechange
29703              * Fires before the slider value is changed. By returning false from an event handler,
29704              * you can cancel the event and prevent the slider from changing.
29705              * @param {Ext.slider.MultiSlider} slider The slider
29706              * @param {Number} newValue The new value which the slider is being changed to.
29707              * @param {Number} oldValue The old value which the slider was previously.
29708              */
29709             'beforechange',
29710
29711             /**
29712              * @event change
29713              * Fires when the slider value is changed.
29714              * @param {Ext.slider.MultiSlider} slider The slider
29715              * @param {Number} newValue The new value which the slider has been changed to.
29716              * @param {Ext.slider.Thumb} thumb The thumb that was changed
29717              */
29718             'change',
29719
29720             /**
29721              * @event changecomplete
29722              * Fires when the slider value is changed by the user and any drag operations have completed.
29723              * @param {Ext.slider.MultiSlider} slider The slider
29724              * @param {Number} newValue The new value which the slider has been changed to.
29725              * @param {Ext.slider.Thumb} thumb The thumb that was changed
29726              */
29727             'changecomplete',
29728
29729             /**
29730              * @event dragstart
29731              * Fires after a drag operation has started.
29732              * @param {Ext.slider.MultiSlider} slider The slider
29733              * @param {Ext.EventObject} e The event fired from Ext.dd.DragTracker
29734              */
29735             'dragstart',
29736
29737             /**
29738              * @event drag
29739              * Fires continuously during the drag operation while the mouse is moving.
29740              * @param {Ext.slider.MultiSlider} slider The slider
29741              * @param {Ext.EventObject} e The event fired from Ext.dd.DragTracker
29742              */
29743             'drag',
29744
29745             /**
29746              * @event dragend
29747              * Fires after the drag operation has completed.
29748              * @param {Ext.slider.MultiSlider} slider The slider
29749              * @param {Ext.EventObject} e The event fired from Ext.dd.DragTracker
29750              */
29751             'dragend'
29752         );
29753
29754         /**
29755          * @property values
29756          * @type Array
29757          * Array of values to initalize the thumbs with
29758          */
29759         if (this.values == undefined || Ext.isEmpty(this.values)) this.values = [0];
29760
29761         var values = this.values;
29762
29763         for (var i=0; i < values.length; i++) {
29764             this.addThumb(values[i]);
29765         }
29766
29767         if(this.vertical){
29768             Ext.apply(this, Ext.slider.Vertical);
29769         }
29770     },
29771
29772     /**
29773      * Creates a new thumb and adds it to the slider
29774      * @param {Number} value The initial value to set on the thumb. Defaults to 0
29775      */
29776     addThumb: function(value) {
29777         var thumb = new Ext.slider.Thumb({
29778             value    : value,
29779             slider   : this,
29780             index    : this.thumbs.length,
29781             constrain: this.constrainThumbs
29782         });
29783         this.thumbs.push(thumb);
29784
29785         //render the thumb now if needed
29786         if (this.rendered) thumb.render();
29787     },
29788
29789     /**
29790      * @private
29791      * Moves the given thumb above all other by increasing its z-index. This is called when as drag
29792      * any thumb, so that the thumb that was just dragged is always at the highest z-index. This is
29793      * required when the thumbs are stacked on top of each other at one of the ends of the slider's
29794      * range, which can result in the user not being able to move any of them.
29795      * @param {Ext.slider.Thumb} topThumb The thumb to move to the top
29796      */
29797     promoteThumb: function(topThumb) {
29798         var thumbs = this.thumbs,
29799             zIndex, thumb;
29800
29801         for (var i = 0, j = thumbs.length; i < j; i++) {
29802             thumb = thumbs[i];
29803
29804             if (thumb == topThumb) {
29805                 zIndex = this.topThumbZIndex;
29806             } else {
29807                 zIndex = '';
29808             }
29809
29810             thumb.el.setStyle('zIndex', zIndex);
29811         }
29812     },
29813
29814     // private override
29815     onRender : function() {
29816         this.autoEl = {
29817             cls: 'x-slider ' + (this.vertical ? 'x-slider-vert' : 'x-slider-horz'),
29818             cn : {
29819                 cls: 'x-slider-end',
29820                 cn : {
29821                     cls:'x-slider-inner',
29822                     cn : [{tag:'a', cls:'x-slider-focus', href:"#", tabIndex: '-1', hidefocus:'on'}]
29823                 }
29824             }
29825         };
29826
29827         Ext.slider.MultiSlider.superclass.onRender.apply(this, arguments);
29828
29829         this.endEl   = this.el.first();
29830         this.innerEl = this.endEl.first();
29831         this.focusEl = this.innerEl.child('.x-slider-focus');
29832
29833         //render each thumb
29834         for (var i=0; i < this.thumbs.length; i++) {
29835             this.thumbs[i].render();
29836         }
29837
29838         //calculate the size of half a thumb
29839         var thumb      = this.innerEl.child('.x-slider-thumb');
29840         this.halfThumb = (this.vertical ? thumb.getHeight() : thumb.getWidth()) / 2;
29841
29842         this.initEvents();
29843     },
29844
29845     /**
29846      * @private
29847      * Adds keyboard and mouse listeners on this.el. Ignores click events on the internal focus element.
29848      * Creates a new DragTracker which is used to control what happens when the user drags the thumb around.
29849      */
29850     initEvents : function(){
29851         this.mon(this.el, {
29852             scope    : this,
29853             mousedown: this.onMouseDown,
29854             keydown  : this.onKeyDown
29855         });
29856
29857         this.focusEl.swallowEvent("click", true);
29858     },
29859
29860     /**
29861      * @private
29862      * Mousedown handler for the slider. If the clickToChange is enabled and the click was not on the draggable 'thumb',
29863      * this calculates the new value of the slider and tells the implementation (Horizontal or Vertical) to move the thumb
29864      * @param {Ext.EventObject} e The click event
29865      */
29866     onMouseDown : function(e){
29867         if(this.disabled){
29868             return;
29869         }
29870
29871         //see if the click was on any of the thumbs
29872         var thumbClicked = false;
29873         for (var i=0; i < this.thumbs.length; i++) {
29874             thumbClicked = thumbClicked || e.target == this.thumbs[i].el.dom;
29875         }
29876
29877         if (this.clickToChange && !thumbClicked) {
29878             var local = this.innerEl.translatePoints(e.getXY());
29879             this.onClickChange(local);
29880         }
29881         this.focus();
29882     },
29883
29884     /**
29885      * @private
29886      * Moves the thumb to the indicated position. Note that a Vertical implementation is provided in Ext.slider.Vertical.
29887      * Only changes the value if the click was within this.clickRange.
29888      * @param {Object} local Object containing top and left values for the click event.
29889      */
29890     onClickChange : function(local) {
29891         if (local.top > this.clickRange[0] && local.top < this.clickRange[1]) {
29892             //find the nearest thumb to the click event
29893             var thumb = this.getNearest(local, 'left'),
29894                 index = thumb.index;
29895
29896             this.setValue(index, Ext.util.Format.round(this.reverseValue(local.left), this.decimalPrecision), undefined, true);
29897         }
29898     },
29899
29900     /**
29901      * @private
29902      * Returns the nearest thumb to a click event, along with its distance
29903      * @param {Object} local Object containing top and left values from a click event
29904      * @param {String} prop The property of local to compare on. Use 'left' for horizontal sliders, 'top' for vertical ones
29905      * @return {Object} The closest thumb object and its distance from the click event
29906      */
29907     getNearest: function(local, prop) {
29908         var localValue = prop == 'top' ? this.innerEl.getHeight() - local[prop] : local[prop],
29909             clickValue = this.reverseValue(localValue),
29910             nearestDistance = (this.maxValue - this.minValue) + 5, //add a small fudge for the end of the slider 
29911             index = 0,
29912             nearest = null;
29913
29914         for (var i=0; i < this.thumbs.length; i++) {
29915             var thumb = this.thumbs[i],
29916                 value = thumb.value,
29917                 dist  = Math.abs(value - clickValue);
29918
29919             if (Math.abs(dist <= nearestDistance)) {
29920                 nearest = thumb;
29921                 index = i;
29922                 nearestDistance = dist;
29923             }
29924         }
29925         return nearest;
29926     },
29927
29928     /**
29929      * @private
29930      * Handler for any keypresses captured by the slider. If the key is UP or RIGHT, the thumb is moved along to the right
29931      * by this.keyIncrement. If DOWN or LEFT it is moved left. Pressing CTRL moves the slider to the end in either direction
29932      * @param {Ext.EventObject} e The Event object
29933      */
29934     onKeyDown : function(e){
29935         /*
29936          * The behaviour for keyboard handling with multiple thumbs is currently undefined.
29937          * There's no real sane default for it, so leave it like this until we come up
29938          * with a better way of doing it.
29939          */
29940         if(this.disabled || this.thumbs.length !== 1){
29941             e.preventDefault();
29942             return;
29943         }
29944         var k = e.getKey(),
29945             val;
29946         switch(k){
29947             case e.UP:
29948             case e.RIGHT:
29949                 e.stopEvent();
29950                 val = e.ctrlKey ? this.maxValue : this.getValue(0) + this.keyIncrement;
29951                 this.setValue(0, val, undefined, true);
29952             break;
29953             case e.DOWN:
29954             case e.LEFT:
29955                 e.stopEvent();
29956                 val = e.ctrlKey ? this.minValue : this.getValue(0) - this.keyIncrement;
29957                 this.setValue(0, val, undefined, true);
29958             break;
29959             default:
29960                 e.preventDefault();
29961         }
29962     },
29963
29964     /**
29965      * @private
29966      * If using snapping, this takes a desired new value and returns the closest snapped
29967      * value to it
29968      * @param {Number} value The unsnapped value
29969      * @return {Number} The value of the nearest snap target
29970      */
29971     doSnap : function(value){
29972         if (!(this.increment && value)) {
29973             return value;
29974         }
29975         var newValue = value,
29976             inc = this.increment,
29977             m = value % inc;
29978         if (m != 0) {
29979             newValue -= m;
29980             if (m * 2 >= inc) {
29981                 newValue += inc;
29982             } else if (m * 2 < -inc) {
29983                 newValue -= inc;
29984             }
29985         }
29986         return newValue.constrain(this.minValue,  this.maxValue);
29987     },
29988
29989     // private
29990     afterRender : function(){
29991         Ext.slider.MultiSlider.superclass.afterRender.apply(this, arguments);
29992
29993         for (var i=0; i < this.thumbs.length; i++) {
29994             var thumb = this.thumbs[i];
29995
29996             if (thumb.value !== undefined) {
29997                 var v = this.normalizeValue(thumb.value);
29998
29999                 if (v !== thumb.value) {
30000                     // delete this.value;
30001                     this.setValue(i, v, false);
30002                 } else {
30003                     this.moveThumb(i, this.translateValue(v), false);
30004                 }
30005             }
30006         };
30007     },
30008
30009     /**
30010      * @private
30011      * Returns the ratio of pixels to mapped values. e.g. if the slider is 200px wide and maxValue - minValue is 100,
30012      * the ratio is 2
30013      * @return {Number} The ratio of pixels to mapped values
30014      */
30015     getRatio : function(){
30016         var w = this.innerEl.getWidth(),
30017             v = this.maxValue - this.minValue;
30018         return v == 0 ? w : (w/v);
30019     },
30020
30021     /**
30022      * @private
30023      * Returns a snapped, constrained value when given a desired value
30024      * @param {Number} value Raw number value
30025      * @return {Number} The raw value rounded to the correct d.p. and constrained within the set max and min values
30026      */
30027     normalizeValue : function(v){
30028         v = this.doSnap(v);
30029         v = Ext.util.Format.round(v, this.decimalPrecision);
30030         v = v.constrain(this.minValue, this.maxValue);
30031         return v;
30032     },
30033
30034     /**
30035      * Sets the minimum value for the slider instance. If the current value is less than the
30036      * minimum value, the current value will be changed.
30037      * @param {Number} val The new minimum value
30038      */
30039     setMinValue : function(val){
30040         this.minValue = val;
30041         var i = 0,
30042             thumbs = this.thumbs,
30043             len = thumbs.length,
30044             t;
30045             
30046         for(; i < len; ++i){
30047             t = thumbs[i];
30048             t.value = t.value < val ? val : t.value;
30049         }
30050         this.syncThumb();
30051     },
30052
30053     /**
30054      * Sets the maximum value for the slider instance. If the current value is more than the
30055      * maximum value, the current value will be changed.
30056      * @param {Number} val The new maximum value
30057      */
30058     setMaxValue : function(val){
30059         this.maxValue = val;
30060         var i = 0,
30061             thumbs = this.thumbs,
30062             len = thumbs.length,
30063             t;
30064             
30065         for(; i < len; ++i){
30066             t = thumbs[i];
30067             t.value = t.value > val ? val : t.value;
30068         }
30069         this.syncThumb();
30070     },
30071
30072     /**
30073      * Programmatically sets the value of the Slider. Ensures that the value is constrained within
30074      * the minValue and maxValue.
30075      * @param {Number} index Index of the thumb to move
30076      * @param {Number} value The value to set the slider to. (This will be constrained within minValue and maxValue)
30077      * @param {Boolean} animate Turn on or off animation, defaults to true
30078      */
30079     setValue : function(index, v, animate, changeComplete) {
30080         var thumb = this.thumbs[index],
30081             el    = thumb.el;
30082
30083         v = this.normalizeValue(v);
30084
30085         if (v !== thumb.value && this.fireEvent('beforechange', this, v, thumb.value, thumb) !== false) {
30086             thumb.value = v;
30087             if(this.rendered){
30088                 this.moveThumb(index, this.translateValue(v), animate !== false);
30089                 this.fireEvent('change', this, v, thumb);
30090                 if(changeComplete){
30091                     this.fireEvent('changecomplete', this, v, thumb);
30092                 }
30093             }
30094         }
30095     },
30096
30097     /**
30098      * @private
30099      */
30100     translateValue : function(v) {
30101         var ratio = this.getRatio();
30102         return (v * ratio) - (this.minValue * ratio) - this.halfThumb;
30103     },
30104
30105     /**
30106      * @private
30107      * Given a pixel location along the slider, returns the mapped slider value for that pixel.
30108      * E.g. if we have a slider 200px wide with minValue = 100 and maxValue = 500, reverseValue(50)
30109      * returns 200
30110      * @param {Number} pos The position along the slider to return a mapped value for
30111      * @return {Number} The mapped value for the given position
30112      */
30113     reverseValue : function(pos){
30114         var ratio = this.getRatio();
30115         return (pos + (this.minValue * ratio)) / ratio;
30116     },
30117
30118     /**
30119      * @private
30120      * @param {Number} index Index of the thumb to move
30121      */
30122     moveThumb: function(index, v, animate){
30123         var thumb = this.thumbs[index].el;
30124
30125         if(!animate || this.animate === false){
30126             thumb.setLeft(v);
30127         }else{
30128             thumb.shift({left: v, stopFx: true, duration:.35});
30129         }
30130     },
30131
30132     // private
30133     focus : function(){
30134         this.focusEl.focus(10);
30135     },
30136
30137     // private
30138     onResize : function(w, h){
30139         var thumbs = this.thumbs,
30140             len = thumbs.length,
30141             i = 0;
30142             
30143         /*
30144          * If we happen to be animating during a resize, the position of the thumb will likely be off
30145          * when the animation stops. As such, just stop any animations before syncing the thumbs.
30146          */
30147         for(; i < len; ++i){
30148             thumbs[i].el.stopFx();    
30149         }
30150         // check to see if we're using an auto width
30151         if(Ext.isNumber(w)){
30152             this.innerEl.setWidth(w - (this.el.getPadding('l') + this.endEl.getPadding('r')));
30153         }
30154         this.syncThumb();
30155         Ext.slider.MultiSlider.superclass.onResize.apply(this, arguments);
30156     },
30157
30158     //private
30159     onDisable: function(){
30160         Ext.slider.MultiSlider.superclass.onDisable.call(this);
30161
30162         for (var i=0; i < this.thumbs.length; i++) {
30163             var thumb = this.thumbs[i],
30164                 el    = thumb.el;
30165
30166             thumb.disable();
30167
30168             if(Ext.isIE){
30169                 //IE breaks when using overflow visible and opacity other than 1.
30170                 //Create a place holder for the thumb and display it.
30171                 var xy = el.getXY();
30172                 el.hide();
30173
30174                 this.innerEl.addClass(this.disabledClass).dom.disabled = true;
30175
30176                 if (!this.thumbHolder) {
30177                     this.thumbHolder = this.endEl.createChild({cls: 'x-slider-thumb ' + this.disabledClass});
30178                 }
30179
30180                 this.thumbHolder.show().setXY(xy);
30181             }
30182         }
30183     },
30184
30185     //private
30186     onEnable: function(){
30187         Ext.slider.MultiSlider.superclass.onEnable.call(this);
30188
30189         for (var i=0; i < this.thumbs.length; i++) {
30190             var thumb = this.thumbs[i],
30191                 el    = thumb.el;
30192
30193             thumb.enable();
30194
30195             if (Ext.isIE) {
30196                 this.innerEl.removeClass(this.disabledClass).dom.disabled = false;
30197
30198                 if (this.thumbHolder) this.thumbHolder.hide();
30199
30200                 el.show();
30201                 this.syncThumb();
30202             }
30203         }
30204     },
30205
30206     /**
30207      * Synchronizes the thumb position to the proper proportion of the total component width based
30208      * on the current slider {@link #value}.  This will be called automatically when the Slider
30209      * is resized by a layout, but if it is rendered auto width, this method can be called from
30210      * another resize handler to sync the Slider if necessary.
30211      */
30212     syncThumb : function() {
30213         if (this.rendered) {
30214             for (var i=0; i < this.thumbs.length; i++) {
30215                 this.moveThumb(i, this.translateValue(this.thumbs[i].value));
30216             }
30217         }
30218     },
30219
30220     /**
30221      * Returns the current value of the slider
30222      * @param {Number} index The index of the thumb to return a value for
30223      * @return {Number} The current value of the slider
30224      */
30225     getValue : function(index) {
30226         return this.thumbs[index].value;
30227     },
30228
30229     /**
30230      * Returns an array of values - one for the location of each thumb
30231      * @return {Array} The set of thumb values
30232      */
30233     getValues: function() {
30234         var values = [];
30235
30236         for (var i=0; i < this.thumbs.length; i++) {
30237             values.push(this.thumbs[i].value);
30238         }
30239
30240         return values;
30241     },
30242
30243     // private
30244     beforeDestroy : function(){
30245         var thumbs = this.thumbs;
30246         for(var i = 0, len = thumbs.length; i < len; ++i){
30247             thumbs[i].destroy();
30248             thumbs[i] = null;
30249         }
30250         Ext.destroyMembers(this, 'endEl', 'innerEl', 'focusEl', 'thumbHolder');
30251         Ext.slider.MultiSlider.superclass.beforeDestroy.call(this);
30252     }
30253 });
30254
30255 Ext.reg('multislider', Ext.slider.MultiSlider);
30256
30257 /**
30258  * @class Ext.slider.SingleSlider
30259  * @extends Ext.slider.MultiSlider
30260  * Slider which supports vertical or horizontal orientation, keyboard adjustments,
30261  * configurable snapping, axis clicking and animation. Can be added as an item to
30262  * any container. Example usage:
30263 <pre><code>
30264 new Ext.slider.SingleSlider({
30265     renderTo: Ext.getBody(),
30266     width: 200,
30267     value: 50,
30268     increment: 10,
30269     minValue: 0,
30270     maxValue: 100
30271 });
30272 </code></pre>
30273  * The class Ext.slider.SingleSlider is aliased to Ext.Slider for backwards compatibility.
30274  */
30275 Ext.slider.SingleSlider = Ext.extend(Ext.slider.MultiSlider, {
30276     constructor: function(config) {
30277       config = config || {};
30278
30279       Ext.applyIf(config, {
30280           values: [config.value || 0]
30281       });
30282
30283       Ext.slider.SingleSlider.superclass.constructor.call(this, config);
30284     },
30285
30286     /**
30287      * Returns the current value of the slider
30288      * @return {Number} The current value of the slider
30289      */
30290     getValue: function() {
30291         //just returns the value of the first thumb, which should be the only one in a single slider
30292         return Ext.slider.SingleSlider.superclass.getValue.call(this, 0);
30293     },
30294
30295     /**
30296      * Programmatically sets the value of the Slider. Ensures that the value is constrained within
30297      * the minValue and maxValue.
30298      * @param {Number} value The value to set the slider to. (This will be constrained within minValue and maxValue)
30299      * @param {Boolean} animate Turn on or off animation, defaults to true
30300      */
30301     setValue: function(value, animate) {
30302         var args = Ext.toArray(arguments),
30303             len  = args.length;
30304
30305         //this is to maintain backwards compatiblity for sliders with only one thunb. Usually you must pass the thumb
30306         //index to setValue, but if we only have one thumb we inject the index here first if given the multi-slider
30307         //signature without the required index. The index will always be 0 for a single slider
30308         if (len == 1 || (len <= 3 && typeof arguments[1] != 'number')) {
30309             args.unshift(0);
30310         }
30311
30312         return Ext.slider.SingleSlider.superclass.setValue.apply(this, args);
30313     },
30314
30315     /**
30316      * Synchronizes the thumb position to the proper proportion of the total component width based
30317      * on the current slider {@link #value}.  This will be called automatically when the Slider
30318      * is resized by a layout, but if it is rendered auto width, this method can be called from
30319      * another resize handler to sync the Slider if necessary.
30320      */
30321     syncThumb : function() {
30322         return Ext.slider.SingleSlider.superclass.syncThumb.apply(this, [0].concat(arguments));
30323     },
30324     
30325     // private
30326     getNearest : function(){
30327         // Since there's only 1 thumb, it's always the nearest
30328         return this.thumbs[0];    
30329     }
30330 });
30331
30332 //backwards compatibility
30333 Ext.Slider = Ext.slider.SingleSlider;
30334
30335 Ext.reg('slider', Ext.slider.SingleSlider);
30336
30337 // private class to support vertical sliders
30338 Ext.slider.Vertical = {
30339     onResize : function(w, h){
30340         this.innerEl.setHeight(h - (this.el.getPadding('t') + this.endEl.getPadding('b')));
30341         this.syncThumb();
30342     },
30343
30344     getRatio : function(){
30345         var h = this.innerEl.getHeight(),
30346             v = this.maxValue - this.minValue;
30347         return h/v;
30348     },
30349
30350     moveThumb: function(index, v, animate) {
30351         var thumb = this.thumbs[index],
30352             el    = thumb.el;
30353
30354         if (!animate || this.animate === false) {
30355             el.setBottom(v);
30356         } else {
30357             el.shift({bottom: v, stopFx: true, duration:.35});
30358         }
30359     },
30360
30361     onClickChange : function(local) {
30362         if (local.left > this.clickRange[0] && local.left < this.clickRange[1]) {
30363             var thumb = this.getNearest(local, 'top'),
30364                 index = thumb.index,
30365                 value = this.minValue + this.reverseValue(this.innerEl.getHeight() - local.top);
30366
30367             this.setValue(index, Ext.util.Format.round(value, this.decimalPrecision), undefined, true);
30368         }
30369     }
30370 };
30371
30372 //private class to support vertical dragging of thumbs within a slider
30373 Ext.slider.Thumb.Vertical = {
30374     getNewValue: function() {
30375         var slider   = this.slider,
30376             innerEl  = slider.innerEl,
30377             pos      = innerEl.translatePoints(this.tracker.getXY()),
30378             bottom   = innerEl.getHeight() - pos.top;
30379
30380         return slider.minValue + Ext.util.Format.round(bottom / slider.getRatio(), slider.decimalPrecision);
30381     }
30382 };
30383 /**
30384  * @class Ext.ProgressBar
30385  * @extends Ext.BoxComponent
30386  * <p>An updateable progress bar component.  The progress bar supports two different modes: manual and automatic.</p>
30387  * <p>In manual mode, you are responsible for showing, updating (via {@link #updateProgress}) and clearing the
30388  * progress bar as needed from your own code.  This method is most appropriate when you want to show progress
30389  * throughout an operation that has predictable points of interest at which you can update the control.</p>
30390  * <p>In automatic mode, you simply call {@link #wait} and let the progress bar run indefinitely, only clearing it
30391  * once the operation is complete.  You can optionally have the progress bar wait for a specific amount of time
30392  * and then clear itself.  Automatic mode is most appropriate for timed operations or asynchronous operations in
30393  * which you have no need for indicating intermediate progress.</p>
30394  * @cfg {Float} value A floating point value between 0 and 1 (e.g., .5, defaults to 0)
30395  * @cfg {String} text The progress bar text (defaults to '')
30396  * @cfg {Mixed} textEl The element to render the progress text to (defaults to the progress
30397  * bar's internal text element)
30398  * @cfg {String} id The progress bar element's id (defaults to an auto-generated id)
30399  * @xtype progress
30400  */
30401 Ext.ProgressBar = Ext.extend(Ext.BoxComponent, {
30402    /**
30403     * @cfg {String} baseCls
30404     * The base CSS class to apply to the progress bar's wrapper element (defaults to 'x-progress')
30405     */
30406     baseCls : 'x-progress',
30407     
30408     /**
30409     * @cfg {Boolean} animate
30410     * True to animate the progress bar during transitions (defaults to false)
30411     */
30412     animate : false,
30413
30414     // private
30415     waitTimer : null,
30416
30417     // private
30418     initComponent : function(){
30419         Ext.ProgressBar.superclass.initComponent.call(this);
30420         this.addEvents(
30421             /**
30422              * @event update
30423              * Fires after each update interval
30424              * @param {Ext.ProgressBar} this
30425              * @param {Number} The current progress value
30426              * @param {String} The current progress text
30427              */
30428             "update"
30429         );
30430     },
30431
30432     // private
30433     onRender : function(ct, position){
30434         var tpl = new Ext.Template(
30435             '<div class="{cls}-wrap">',
30436                 '<div class="{cls}-inner">',
30437                     '<div class="{cls}-bar">',
30438                         '<div class="{cls}-text">',
30439                             '<div>&#160;</div>',
30440                         '</div>',
30441                     '</div>',
30442                     '<div class="{cls}-text {cls}-text-back">',
30443                         '<div>&#160;</div>',
30444                     '</div>',
30445                 '</div>',
30446             '</div>'
30447         );
30448
30449         this.el = position ? tpl.insertBefore(position, {cls: this.baseCls}, true)
30450             : tpl.append(ct, {cls: this.baseCls}, true);
30451                 
30452         if(this.id){
30453             this.el.dom.id = this.id;
30454         }
30455         var inner = this.el.dom.firstChild;
30456         this.progressBar = Ext.get(inner.firstChild);
30457
30458         if(this.textEl){
30459             //use an external text el
30460             this.textEl = Ext.get(this.textEl);
30461             delete this.textTopEl;
30462         }else{
30463             //setup our internal layered text els
30464             this.textTopEl = Ext.get(this.progressBar.dom.firstChild);
30465             var textBackEl = Ext.get(inner.childNodes[1]);
30466             this.textTopEl.setStyle("z-index", 99).addClass('x-hidden');
30467             this.textEl = new Ext.CompositeElement([this.textTopEl.dom.firstChild, textBackEl.dom.firstChild]);
30468             this.textEl.setWidth(inner.offsetWidth);
30469         }
30470         this.progressBar.setHeight(inner.offsetHeight);
30471     },
30472     
30473     // private
30474     afterRender : function(){
30475         Ext.ProgressBar.superclass.afterRender.call(this);
30476         if(this.value){
30477             this.updateProgress(this.value, this.text);
30478         }else{
30479             this.updateText(this.text);
30480         }
30481     },
30482
30483     /**
30484      * Updates the progress bar value, and optionally its text.  If the text argument is not specified,
30485      * any existing text value will be unchanged.  To blank out existing text, pass ''.  Note that even
30486      * if the progress bar value exceeds 1, it will never automatically reset -- you are responsible for
30487      * determining when the progress is complete and calling {@link #reset} to clear and/or hide the control.
30488      * @param {Float} value (optional) A floating point value between 0 and 1 (e.g., .5, defaults to 0)
30489      * @param {String} text (optional) The string to display in the progress text element (defaults to '')
30490      * @param {Boolean} animate (optional) Whether to animate the transition of the progress bar. If this value is
30491      * not specified, the default for the class is used (default to false)
30492      * @return {Ext.ProgressBar} this
30493      */
30494     updateProgress : function(value, text, animate){
30495         this.value = value || 0;
30496         if(text){
30497             this.updateText(text);
30498         }
30499         if(this.rendered && !this.isDestroyed){
30500             var w = Math.floor(value*this.el.dom.firstChild.offsetWidth);
30501             this.progressBar.setWidth(w, animate === true || (animate !== false && this.animate));
30502             if(this.textTopEl){
30503                 //textTopEl should be the same width as the bar so overflow will clip as the bar moves
30504                 this.textTopEl.removeClass('x-hidden').setWidth(w);
30505             }
30506         }
30507         this.fireEvent('update', this, value, text);
30508         return this;
30509     },
30510
30511     /**
30512      * Initiates an auto-updating progress bar.  A duration can be specified, in which case the progress
30513      * bar will automatically reset after a fixed amount of time and optionally call a callback function
30514      * if specified.  If no duration is passed in, then the progress bar will run indefinitely and must
30515      * be manually cleared by calling {@link #reset}.  The wait method accepts a config object with
30516      * the following properties:
30517      * <pre>
30518 Property   Type          Description
30519 ---------- ------------  ----------------------------------------------------------------------
30520 duration   Number        The length of time in milliseconds that the progress bar should
30521                          run before resetting itself (defaults to undefined, in which case it
30522                          will run indefinitely until reset is called)
30523 interval   Number        The length of time in milliseconds between each progress update
30524                          (defaults to 1000 ms)
30525 animate    Boolean       Whether to animate the transition of the progress bar. If this value is
30526                          not specified, the default for the class is used.                                                   
30527 increment  Number        The number of progress update segments to display within the progress
30528                          bar (defaults to 10).  If the bar reaches the end and is still
30529                          updating, it will automatically wrap back to the beginning.
30530 text       String        Optional text to display in the progress bar element (defaults to '').
30531 fn         Function      A callback function to execute after the progress bar finishes auto-
30532                          updating.  The function will be called with no arguments.  This function
30533                          will be ignored if duration is not specified since in that case the
30534                          progress bar can only be stopped programmatically, so any required function
30535                          should be called by the same code after it resets the progress bar.
30536 scope      Object        The scope that is passed to the callback function (only applies when
30537                          duration and fn are both passed).
30538 </pre>
30539          *
30540          * Example usage:
30541          * <pre><code>
30542 var p = new Ext.ProgressBar({
30543    renderTo: 'my-el'
30544 });
30545
30546 //Wait for 5 seconds, then update the status el (progress bar will auto-reset)
30547 p.wait({
30548    interval: 100, //bar will move fast!
30549    duration: 5000,
30550    increment: 15,
30551    text: 'Updating...',
30552    scope: this,
30553    fn: function(){
30554       Ext.fly('status').update('Done!');
30555    }
30556 });
30557
30558 //Or update indefinitely until some async action completes, then reset manually
30559 p.wait();
30560 myAction.on('complete', function(){
30561     p.reset();
30562     Ext.fly('status').update('Done!');
30563 });
30564 </code></pre>
30565      * @param {Object} config (optional) Configuration options
30566      * @return {Ext.ProgressBar} this
30567      */
30568     wait : function(o){
30569         if(!this.waitTimer){
30570             var scope = this;
30571             o = o || {};
30572             this.updateText(o.text);
30573             this.waitTimer = Ext.TaskMgr.start({
30574                 run: function(i){
30575                     var inc = o.increment || 10;
30576                     i -= 1;
30577                     this.updateProgress(((((i+inc)%inc)+1)*(100/inc))*0.01, null, o.animate);
30578                 },
30579                 interval: o.interval || 1000,
30580                 duration: o.duration,
30581                 onStop: function(){
30582                     if(o.fn){
30583                         o.fn.apply(o.scope || this);
30584                     }
30585                     this.reset();
30586                 },
30587                 scope: scope
30588             });
30589         }
30590         return this;
30591     },
30592
30593     /**
30594      * Returns true if the progress bar is currently in a {@link #wait} operation
30595      * @return {Boolean} True if waiting, else false
30596      */
30597     isWaiting : function(){
30598         return this.waitTimer !== null;
30599     },
30600
30601     /**
30602      * Updates the progress bar text.  If specified, textEl will be updated, otherwise the progress
30603      * bar itself will display the updated text.
30604      * @param {String} text (optional) The string to display in the progress text element (defaults to '')
30605      * @return {Ext.ProgressBar} this
30606      */
30607     updateText : function(text){
30608         this.text = text || '&#160;';
30609         if(this.rendered){
30610             this.textEl.update(this.text);
30611         }
30612         return this;
30613     },
30614     
30615     /**
30616      * Synchronizes the inner bar width to the proper proportion of the total componet width based
30617      * on the current progress {@link #value}.  This will be called automatically when the ProgressBar
30618      * is resized by a layout, but if it is rendered auto width, this method can be called from
30619      * another resize handler to sync the ProgressBar if necessary.
30620      */
30621     syncProgressBar : function(){
30622         if(this.value){
30623             this.updateProgress(this.value, this.text);
30624         }
30625         return this;
30626     },
30627
30628     /**
30629      * Sets the size of the progress bar.
30630      * @param {Number} width The new width in pixels
30631      * @param {Number} height The new height in pixels
30632      * @return {Ext.ProgressBar} this
30633      */
30634     setSize : function(w, h){
30635         Ext.ProgressBar.superclass.setSize.call(this, w, h);
30636         if(this.textTopEl){
30637             var inner = this.el.dom.firstChild;
30638             this.textEl.setSize(inner.offsetWidth, inner.offsetHeight);
30639         }
30640         this.syncProgressBar();
30641         return this;
30642     },
30643
30644     /**
30645      * Resets the progress bar value to 0 and text to empty string.  If hide = true, the progress
30646      * bar will also be hidden (using the {@link #hideMode} property internally).
30647      * @param {Boolean} hide (optional) True to hide the progress bar (defaults to false)
30648      * @return {Ext.ProgressBar} this
30649      */
30650     reset : function(hide){
30651         this.updateProgress(0);
30652         if(this.textTopEl){
30653             this.textTopEl.addClass('x-hidden');
30654         }
30655         this.clearTimer();
30656         if(hide === true){
30657             this.hide();
30658         }
30659         return this;
30660     },
30661     
30662     // private
30663     clearTimer : function(){
30664         if(this.waitTimer){
30665             this.waitTimer.onStop = null; //prevent recursion
30666             Ext.TaskMgr.stop(this.waitTimer);
30667             this.waitTimer = null;
30668         }
30669     },
30670     
30671     onDestroy: function(){
30672         this.clearTimer();
30673         if(this.rendered){
30674             if(this.textEl.isComposite){
30675                 this.textEl.clear();
30676             }
30677             Ext.destroyMembers(this, 'textEl', 'progressBar', 'textTopEl');
30678         }
30679         Ext.ProgressBar.superclass.onDestroy.call(this);
30680     }
30681 });
30682 Ext.reg('progress', Ext.ProgressBar);/*
30683  * These classes are derivatives of the similarly named classes in the YUI Library.
30684  * The original license:
30685  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
30686  * Code licensed under the BSD License:
30687  * http://developer.yahoo.net/yui/license.txt
30688  */
30689
30690 (function() {
30691
30692 var Event=Ext.EventManager;
30693 var Dom=Ext.lib.Dom;
30694
30695 /**
30696  * @class Ext.dd.DragDrop
30697  * Defines the interface and base operation of items that that can be
30698  * dragged or can be drop targets.  It was designed to be extended, overriding
30699  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
30700  * Up to three html elements can be associated with a DragDrop instance:
30701  * <ul>
30702  * <li>linked element: the element that is passed into the constructor.
30703  * This is the element which defines the boundaries for interaction with
30704  * other DragDrop objects.</li>
30705  * <li>handle element(s): The drag operation only occurs if the element that
30706  * was clicked matches a handle element.  By default this is the linked
30707  * element, but there are times that you will want only a portion of the
30708  * linked element to initiate the drag operation, and the setHandleElId()
30709  * method provides a way to define this.</li>
30710  * <li>drag element: this represents the element that would be moved along
30711  * with the cursor during a drag operation.  By default, this is the linked
30712  * element itself as in {@link Ext.dd.DD}.  setDragElId() lets you define
30713  * a separate element that would be moved, as in {@link Ext.dd.DDProxy}.
30714  * </li>
30715  * </ul>
30716  * This class should not be instantiated until the onload event to ensure that
30717  * the associated elements are available.
30718  * The following would define a DragDrop obj that would interact with any
30719  * other DragDrop obj in the "group1" group:
30720  * <pre>
30721  *  dd = new Ext.dd.DragDrop("div1", "group1");
30722  * </pre>
30723  * Since none of the event handlers have been implemented, nothing would
30724  * actually happen if you were to run the code above.  Normally you would
30725  * override this class or one of the default implementations, but you can
30726  * also override the methods you want on an instance of the class...
30727  * <pre>
30728  *  dd.onDragDrop = function(e, id) {
30729  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
30730  *  }
30731  * </pre>
30732  * @constructor
30733  * @param {String} id of the element that is linked to this instance
30734  * @param {String} sGroup the group of related DragDrop objects
30735  * @param {object} config an object containing configurable attributes
30736  *                Valid properties for DragDrop:
30737  *                    padding, isTarget, maintainOffset, primaryButtonOnly
30738  */
30739 Ext.dd.DragDrop = function(id, sGroup, config) {
30740     if(id) {
30741         this.init(id, sGroup, config);
30742     }
30743 };
30744
30745 Ext.dd.DragDrop.prototype = {
30746
30747     /**
30748      * Set to false to enable a DragDrop object to fire drag events while dragging
30749      * over its own Element. Defaults to true - DragDrop objects do not by default
30750      * fire drag events to themselves.
30751      * @property ignoreSelf
30752      * @type Boolean
30753      */
30754
30755     /**
30756      * The id of the element associated with this object.  This is what we
30757      * refer to as the "linked element" because the size and position of
30758      * this element is used to determine when the drag and drop objects have
30759      * interacted.
30760      * @property id
30761      * @type String
30762      */
30763     id: null,
30764
30765     /**
30766      * Configuration attributes passed into the constructor
30767      * @property config
30768      * @type object
30769      */
30770     config: null,
30771
30772     /**
30773      * The id of the element that will be dragged.  By default this is same
30774      * as the linked element, but could be changed to another element. Ex:
30775      * Ext.dd.DDProxy
30776      * @property dragElId
30777      * @type String
30778      * @private
30779      */
30780     dragElId: null,
30781
30782     /**
30783      * The ID of the element that initiates the drag operation.  By default
30784      * this is the linked element, but could be changed to be a child of this
30785      * element.  This lets us do things like only starting the drag when the
30786      * header element within the linked html element is clicked.
30787      * @property handleElId
30788      * @type String
30789      * @private
30790      */
30791     handleElId: null,
30792
30793     /**
30794      * An object who's property names identify HTML tags to be considered invalid as drag handles.
30795      * A non-null property value identifies the tag as invalid. Defaults to the 
30796      * following value which prevents drag operations from being initiated by &lt;a> elements:<pre><code>
30797 {
30798     A: "A"
30799 }</code></pre>
30800      * @property invalidHandleTypes
30801      * @type Object
30802      */
30803     invalidHandleTypes: null,
30804
30805     /**
30806      * An object who's property names identify the IDs of elements to be considered invalid as drag handles.
30807      * A non-null property value identifies the ID as invalid. For example, to prevent
30808      * dragging from being initiated on element ID "foo", use:<pre><code>
30809 {
30810     foo: true
30811 }</code></pre>
30812      * @property invalidHandleIds
30813      * @type Object
30814      */
30815     invalidHandleIds: null,
30816
30817     /**
30818      * An Array of CSS class names for elements to be considered in valid as drag handles.
30819      * @property invalidHandleClasses
30820      * @type Array
30821      */
30822     invalidHandleClasses: null,
30823
30824     /**
30825      * The linked element's absolute X position at the time the drag was
30826      * started
30827      * @property startPageX
30828      * @type int
30829      * @private
30830      */
30831     startPageX: 0,
30832
30833     /**
30834      * The linked element's absolute X position at the time the drag was
30835      * started
30836      * @property startPageY
30837      * @type int
30838      * @private
30839      */
30840     startPageY: 0,
30841
30842     /**
30843      * The group defines a logical collection of DragDrop objects that are
30844      * related.  Instances only get events when interacting with other
30845      * DragDrop object in the same group.  This lets us define multiple
30846      * groups using a single DragDrop subclass if we want.
30847      * @property groups
30848      * @type object An object in the format {'group1':true, 'group2':true}
30849      */
30850     groups: null,
30851
30852     /**
30853      * Individual drag/drop instances can be locked.  This will prevent
30854      * onmousedown start drag.
30855      * @property locked
30856      * @type boolean
30857      * @private
30858      */
30859     locked: false,
30860
30861     /**
30862      * Lock this instance
30863      * @method lock
30864      */
30865     lock: function() {
30866         this.locked = true;
30867     },
30868
30869     /**
30870      * When set to true, other DD objects in cooperating DDGroups do not receive
30871      * notification events when this DD object is dragged over them. Defaults to false.
30872      * @property moveOnly
30873      * @type boolean
30874      */
30875     moveOnly: false,
30876
30877     /**
30878      * Unlock this instace
30879      * @method unlock
30880      */
30881     unlock: function() {
30882         this.locked = false;
30883     },
30884
30885     /**
30886      * By default, all instances can be a drop target.  This can be disabled by
30887      * setting isTarget to false.
30888      * @property isTarget
30889      * @type boolean
30890      */
30891     isTarget: true,
30892
30893     /**
30894      * The padding configured for this drag and drop object for calculating
30895      * the drop zone intersection with this object.
30896      * @property padding
30897      * @type int[] An array containing the 4 padding values: [top, right, bottom, left]
30898      */
30899     padding: null,
30900
30901     /**
30902      * Cached reference to the linked element
30903      * @property _domRef
30904      * @private
30905      */
30906     _domRef: null,
30907
30908     /**
30909      * Internal typeof flag
30910      * @property __ygDragDrop
30911      * @private
30912      */
30913     __ygDragDrop: true,
30914
30915     /**
30916      * Set to true when horizontal contraints are applied
30917      * @property constrainX
30918      * @type boolean
30919      * @private
30920      */
30921     constrainX: false,
30922
30923     /**
30924      * Set to true when vertical contraints are applied
30925      * @property constrainY
30926      * @type boolean
30927      * @private
30928      */
30929     constrainY: false,
30930
30931     /**
30932      * The left constraint
30933      * @property minX
30934      * @type int
30935      * @private
30936      */
30937     minX: 0,
30938
30939     /**
30940      * The right constraint
30941      * @property maxX
30942      * @type int
30943      * @private
30944      */
30945     maxX: 0,
30946
30947     /**
30948      * The up constraint
30949      * @property minY
30950      * @type int
30951      * @private
30952      */
30953     minY: 0,
30954
30955     /**
30956      * The down constraint
30957      * @property maxY
30958      * @type int
30959      * @private
30960      */
30961     maxY: 0,
30962
30963     /**
30964      * Maintain offsets when we resetconstraints.  Set to true when you want
30965      * the position of the element relative to its parent to stay the same
30966      * when the page changes
30967      *
30968      * @property maintainOffset
30969      * @type boolean
30970      */
30971     maintainOffset: false,
30972
30973     /**
30974      * Array of pixel locations the element will snap to if we specified a
30975      * horizontal graduation/interval.  This array is generated automatically
30976      * when you define a tick interval.
30977      * @property xTicks
30978      * @type int[]
30979      */
30980     xTicks: null,
30981
30982     /**
30983      * Array of pixel locations the element will snap to if we specified a
30984      * vertical graduation/interval.  This array is generated automatically
30985      * when you define a tick interval.
30986      * @property yTicks
30987      * @type int[]
30988      */
30989     yTicks: null,
30990
30991     /**
30992      * By default the drag and drop instance will only respond to the primary
30993      * button click (left button for a right-handed mouse).  Set to true to
30994      * allow drag and drop to start with any mouse click that is propogated
30995      * by the browser
30996      * @property primaryButtonOnly
30997      * @type boolean
30998      */
30999     primaryButtonOnly: true,
31000
31001     /**
31002      * The available property is false until the linked dom element is accessible.
31003      * @property available
31004      * @type boolean
31005      */
31006     available: false,
31007
31008     /**
31009      * By default, drags can only be initiated if the mousedown occurs in the
31010      * region the linked element is.  This is done in part to work around a
31011      * bug in some browsers that mis-report the mousedown if the previous
31012      * mouseup happened outside of the window.  This property is set to true
31013      * if outer handles are defined.
31014      *
31015      * @property hasOuterHandles
31016      * @type boolean
31017      * @default false
31018      */
31019     hasOuterHandles: false,
31020
31021     /**
31022      * Code that executes immediately before the startDrag event
31023      * @method b4StartDrag
31024      * @private
31025      */
31026     b4StartDrag: function(x, y) { },
31027
31028     /**
31029      * Abstract method called after a drag/drop object is clicked
31030      * and the drag or mousedown time thresholds have beeen met.
31031      * @method startDrag
31032      * @param {int} X click location
31033      * @param {int} Y click location
31034      */
31035     startDrag: function(x, y) { /* override this */ },
31036
31037     /**
31038      * Code that executes immediately before the onDrag event
31039      * @method b4Drag
31040      * @private
31041      */
31042     b4Drag: function(e) { },
31043
31044     /**
31045      * Abstract method called during the onMouseMove event while dragging an
31046      * object.
31047      * @method onDrag
31048      * @param {Event} e the mousemove event
31049      */
31050     onDrag: function(e) { /* override this */ },
31051
31052     /**
31053      * Abstract method called when this element fist begins hovering over
31054      * another DragDrop obj
31055      * @method onDragEnter
31056      * @param {Event} e the mousemove event
31057      * @param {String|DragDrop[]} id In POINT mode, the element
31058      * id this is hovering over.  In INTERSECT mode, an array of one or more
31059      * dragdrop items being hovered over.
31060      */
31061     onDragEnter: function(e, id) { /* override this */ },
31062
31063     /**
31064      * Code that executes immediately before the onDragOver event
31065      * @method b4DragOver
31066      * @private
31067      */
31068     b4DragOver: function(e) { },
31069
31070     /**
31071      * Abstract method called when this element is hovering over another
31072      * DragDrop obj
31073      * @method onDragOver
31074      * @param {Event} e the mousemove event
31075      * @param {String|DragDrop[]} id In POINT mode, the element
31076      * id this is hovering over.  In INTERSECT mode, an array of dd items
31077      * being hovered over.
31078      */
31079     onDragOver: function(e, id) { /* override this */ },
31080
31081     /**
31082      * Code that executes immediately before the onDragOut event
31083      * @method b4DragOut
31084      * @private
31085      */
31086     b4DragOut: function(e) { },
31087
31088     /**
31089      * Abstract method called when we are no longer hovering over an element
31090      * @method onDragOut
31091      * @param {Event} e the mousemove event
31092      * @param {String|DragDrop[]} id In POINT mode, the element
31093      * id this was hovering over.  In INTERSECT mode, an array of dd items
31094      * that the mouse is no longer over.
31095      */
31096     onDragOut: function(e, id) { /* override this */ },
31097
31098     /**
31099      * Code that executes immediately before the onDragDrop event
31100      * @method b4DragDrop
31101      * @private
31102      */
31103     b4DragDrop: function(e) { },
31104
31105     /**
31106      * Abstract method called when this item is dropped on another DragDrop
31107      * obj
31108      * @method onDragDrop
31109      * @param {Event} e the mouseup event
31110      * @param {String|DragDrop[]} id In POINT mode, the element
31111      * id this was dropped on.  In INTERSECT mode, an array of dd items this
31112      * was dropped on.
31113      */
31114     onDragDrop: function(e, id) { /* override this */ },
31115
31116     /**
31117      * Abstract method called when this item is dropped on an area with no
31118      * drop target
31119      * @method onInvalidDrop
31120      * @param {Event} e the mouseup event
31121      */
31122     onInvalidDrop: function(e) { /* override this */ },
31123
31124     /**
31125      * Code that executes immediately before the endDrag event
31126      * @method b4EndDrag
31127      * @private
31128      */
31129     b4EndDrag: function(e) { },
31130
31131     /**
31132      * Fired when we are done dragging the object
31133      * @method endDrag
31134      * @param {Event} e the mouseup event
31135      */
31136     endDrag: function(e) { /* override this */ },
31137
31138     /**
31139      * Code executed immediately before the onMouseDown event
31140      * @method b4MouseDown
31141      * @param {Event} e the mousedown event
31142      * @private
31143      */
31144     b4MouseDown: function(e) {  },
31145
31146     /**
31147      * Event handler that fires when a drag/drop obj gets a mousedown
31148      * @method onMouseDown
31149      * @param {Event} e the mousedown event
31150      */
31151     onMouseDown: function(e) { /* override this */ },
31152
31153     /**
31154      * Event handler that fires when a drag/drop obj gets a mouseup
31155      * @method onMouseUp
31156      * @param {Event} e the mouseup event
31157      */
31158     onMouseUp: function(e) { /* override this */ },
31159
31160     /**
31161      * Override the onAvailable method to do what is needed after the initial
31162      * position was determined.
31163      * @method onAvailable
31164      */
31165     onAvailable: function () {
31166     },
31167
31168     /**
31169      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
31170      * @type Object
31171      */
31172     defaultPadding : {left:0, right:0, top:0, bottom:0},
31173
31174     /**
31175      * Initializes the drag drop object's constraints to restrict movement to a certain element.
31176  *
31177  * Usage:
31178  <pre><code>
31179  var dd = new Ext.dd.DDProxy("dragDiv1", "proxytest",
31180                 { dragElId: "existingProxyDiv" });
31181  dd.startDrag = function(){
31182      this.constrainTo("parent-id");
31183  };
31184  </code></pre>
31185  * Or you can initalize it using the {@link Ext.Element} object:
31186  <pre><code>
31187  Ext.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
31188      startDrag : function(){
31189          this.constrainTo("parent-id");
31190      }
31191  });
31192  </code></pre>
31193      * @param {Mixed} constrainTo The element to constrain to.
31194      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
31195      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
31196      * an object containing the sides to pad. For example: {right:10, bottom:10}
31197      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
31198      */
31199     constrainTo : function(constrainTo, pad, inContent){
31200         if(Ext.isNumber(pad)){
31201             pad = {left: pad, right:pad, top:pad, bottom:pad};
31202         }
31203         pad = pad || this.defaultPadding;
31204         var b = Ext.get(this.getEl()).getBox(),
31205             ce = Ext.get(constrainTo),
31206             s = ce.getScroll(),
31207             c, 
31208             cd = ce.dom;
31209         if(cd == document.body){
31210             c = { x: s.left, y: s.top, width: Ext.lib.Dom.getViewWidth(), height: Ext.lib.Dom.getViewHeight()};
31211         }else{
31212             var xy = ce.getXY();
31213             c = {x : xy[0], y: xy[1], width: cd.clientWidth, height: cd.clientHeight};
31214         }
31215
31216
31217         var topSpace = b.y - c.y,
31218             leftSpace = b.x - c.x;
31219
31220         this.resetConstraints();
31221         this.setXConstraint(leftSpace - (pad.left||0), // left
31222                 c.width - leftSpace - b.width - (pad.right||0), //right
31223                                 this.xTickSize
31224         );
31225         this.setYConstraint(topSpace - (pad.top||0), //top
31226                 c.height - topSpace - b.height - (pad.bottom||0), //bottom
31227                                 this.yTickSize
31228         );
31229     },
31230
31231     /**
31232      * Returns a reference to the linked element
31233      * @method getEl
31234      * @return {HTMLElement} the html element
31235      */
31236     getEl: function() {
31237         if (!this._domRef) {
31238             this._domRef = Ext.getDom(this.id);
31239         }
31240
31241         return this._domRef;
31242     },
31243
31244     /**
31245      * Returns a reference to the actual element to drag.  By default this is
31246      * the same as the html element, but it can be assigned to another
31247      * element. An example of this can be found in Ext.dd.DDProxy
31248      * @method getDragEl
31249      * @return {HTMLElement} the html element
31250      */
31251     getDragEl: function() {
31252         return Ext.getDom(this.dragElId);
31253     },
31254
31255     /**
31256      * Sets up the DragDrop object.  Must be called in the constructor of any
31257      * Ext.dd.DragDrop subclass
31258      * @method init
31259      * @param id the id of the linked element
31260      * @param {String} sGroup the group of related items
31261      * @param {object} config configuration attributes
31262      */
31263     init: function(id, sGroup, config) {
31264         this.initTarget(id, sGroup, config);
31265         Event.on(this.id, "mousedown", this.handleMouseDown, this);
31266         // Event.on(this.id, "selectstart", Event.preventDefault);
31267     },
31268
31269     /**
31270      * Initializes Targeting functionality only... the object does not
31271      * get a mousedown handler.
31272      * @method initTarget
31273      * @param id the id of the linked element
31274      * @param {String} sGroup the group of related items
31275      * @param {object} config configuration attributes
31276      */
31277     initTarget: function(id, sGroup, config) {
31278
31279         // configuration attributes
31280         this.config = config || {};
31281
31282         // create a local reference to the drag and drop manager
31283         this.DDM = Ext.dd.DDM;
31284         // initialize the groups array
31285         this.groups = {};
31286
31287         // assume that we have an element reference instead of an id if the
31288         // parameter is not a string
31289         if (typeof id !== "string") {
31290             id = Ext.id(id);
31291         }
31292
31293         // set the id
31294         this.id = id;
31295
31296         // add to an interaction group
31297         this.addToGroup((sGroup) ? sGroup : "default");
31298
31299         // We don't want to register this as the handle with the manager
31300         // so we just set the id rather than calling the setter.
31301         this.handleElId = id;
31302
31303         // the linked element is the element that gets dragged by default
31304         this.setDragElId(id);
31305
31306         // by default, clicked anchors will not start drag operations.
31307         this.invalidHandleTypes = { A: "A" };
31308         this.invalidHandleIds = {};
31309         this.invalidHandleClasses = [];
31310
31311         this.applyConfig();
31312
31313         this.handleOnAvailable();
31314     },
31315
31316     /**
31317      * Applies the configuration parameters that were passed into the constructor.
31318      * This is supposed to happen at each level through the inheritance chain.  So
31319      * a DDProxy implentation will execute apply config on DDProxy, DD, and
31320      * DragDrop in order to get all of the parameters that are available in
31321      * each object.
31322      * @method applyConfig
31323      */
31324     applyConfig: function() {
31325
31326         // configurable properties:
31327         //    padding, isTarget, maintainOffset, primaryButtonOnly
31328         this.padding           = this.config.padding || [0, 0, 0, 0];
31329         this.isTarget          = (this.config.isTarget !== false);
31330         this.maintainOffset    = (this.config.maintainOffset);
31331         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
31332
31333     },
31334
31335     /**
31336      * Executed when the linked element is available
31337      * @method handleOnAvailable
31338      * @private
31339      */
31340     handleOnAvailable: function() {
31341         this.available = true;
31342         this.resetConstraints();
31343         this.onAvailable();
31344     },
31345
31346      /**
31347      * Configures the padding for the target zone in px.  Effectively expands
31348      * (or reduces) the virtual object size for targeting calculations.
31349      * Supports css-style shorthand; if only one parameter is passed, all sides
31350      * will have that padding, and if only two are passed, the top and bottom
31351      * will have the first param, the left and right the second.
31352      * @method setPadding
31353      * @param {int} iTop    Top pad
31354      * @param {int} iRight  Right pad
31355      * @param {int} iBot    Bot pad
31356      * @param {int} iLeft   Left pad
31357      */
31358     setPadding: function(iTop, iRight, iBot, iLeft) {
31359         // this.padding = [iLeft, iRight, iTop, iBot];
31360         if (!iRight && 0 !== iRight) {
31361             this.padding = [iTop, iTop, iTop, iTop];
31362         } else if (!iBot && 0 !== iBot) {
31363             this.padding = [iTop, iRight, iTop, iRight];
31364         } else {
31365             this.padding = [iTop, iRight, iBot, iLeft];
31366         }
31367     },
31368
31369     /**
31370      * Stores the initial placement of the linked element.
31371      * @method setInitPosition
31372      * @param {int} diffX   the X offset, default 0
31373      * @param {int} diffY   the Y offset, default 0
31374      */
31375     setInitPosition: function(diffX, diffY) {
31376         var el = this.getEl();
31377
31378         if (!this.DDM.verifyEl(el)) {
31379             return;
31380         }
31381
31382         var dx = diffX || 0;
31383         var dy = diffY || 0;
31384
31385         var p = Dom.getXY( el );
31386
31387         this.initPageX = p[0] - dx;
31388         this.initPageY = p[1] - dy;
31389
31390         this.lastPageX = p[0];
31391         this.lastPageY = p[1];
31392
31393         this.setStartPosition(p);
31394     },
31395
31396     /**
31397      * Sets the start position of the element.  This is set when the obj
31398      * is initialized, the reset when a drag is started.
31399      * @method setStartPosition
31400      * @param pos current position (from previous lookup)
31401      * @private
31402      */
31403     setStartPosition: function(pos) {
31404         var p = pos || Dom.getXY( this.getEl() );
31405         this.deltaSetXY = null;
31406
31407         this.startPageX = p[0];
31408         this.startPageY = p[1];
31409     },
31410
31411     /**
31412      * Add this instance to a group of related drag/drop objects.  All
31413      * instances belong to at least one group, and can belong to as many
31414      * groups as needed.
31415      * @method addToGroup
31416      * @param sGroup {string} the name of the group
31417      */
31418     addToGroup: function(sGroup) {
31419         this.groups[sGroup] = true;
31420         this.DDM.regDragDrop(this, sGroup);
31421     },
31422
31423     /**
31424      * Remove's this instance from the supplied interaction group
31425      * @method removeFromGroup
31426      * @param {string}  sGroup  The group to drop
31427      */
31428     removeFromGroup: function(sGroup) {
31429         if (this.groups[sGroup]) {
31430             delete this.groups[sGroup];
31431         }
31432
31433         this.DDM.removeDDFromGroup(this, sGroup);
31434     },
31435
31436     /**
31437      * Allows you to specify that an element other than the linked element
31438      * will be moved with the cursor during a drag
31439      * @method setDragElId
31440      * @param id {string} the id of the element that will be used to initiate the drag
31441      */
31442     setDragElId: function(id) {
31443         this.dragElId = id;
31444     },
31445
31446     /**
31447      * Allows you to specify a child of the linked element that should be
31448      * used to initiate the drag operation.  An example of this would be if
31449      * you have a content div with text and links.  Clicking anywhere in the
31450      * content area would normally start the drag operation.  Use this method
31451      * to specify that an element inside of the content div is the element
31452      * that starts the drag operation.
31453      * @method setHandleElId
31454      * @param id {string} the id of the element that will be used to
31455      * initiate the drag.
31456      */
31457     setHandleElId: function(id) {
31458         if (typeof id !== "string") {
31459             id = Ext.id(id);
31460         }
31461         this.handleElId = id;
31462         this.DDM.regHandle(this.id, id);
31463     },
31464
31465     /**
31466      * Allows you to set an element outside of the linked element as a drag
31467      * handle
31468      * @method setOuterHandleElId
31469      * @param id the id of the element that will be used to initiate the drag
31470      */
31471     setOuterHandleElId: function(id) {
31472         if (typeof id !== "string") {
31473             id = Ext.id(id);
31474         }
31475         Event.on(id, "mousedown",
31476                 this.handleMouseDown, this);
31477         this.setHandleElId(id);
31478
31479         this.hasOuterHandles = true;
31480     },
31481
31482     /**
31483      * Remove all drag and drop hooks for this element
31484      * @method unreg
31485      */
31486     unreg: function() {
31487         Event.un(this.id, "mousedown",
31488                 this.handleMouseDown);
31489         this._domRef = null;
31490         this.DDM._remove(this);
31491     },
31492
31493     destroy : function(){
31494         this.unreg();
31495     },
31496
31497     /**
31498      * Returns true if this instance is locked, or the drag drop mgr is locked
31499      * (meaning that all drag/drop is disabled on the page.)
31500      * @method isLocked
31501      * @return {boolean} true if this obj or all drag/drop is locked, else
31502      * false
31503      */
31504     isLocked: function() {
31505         return (this.DDM.isLocked() || this.locked);
31506     },
31507
31508     /**
31509      * Fired when this object is clicked
31510      * @method handleMouseDown
31511      * @param {Event} e
31512      * @param {Ext.dd.DragDrop} oDD the clicked dd object (this dd obj)
31513      * @private
31514      */
31515     handleMouseDown: function(e, oDD){
31516         if (this.primaryButtonOnly && e.button != 0) {
31517             return;
31518         }
31519
31520         if (this.isLocked()) {
31521             return;
31522         }
31523
31524         this.DDM.refreshCache(this.groups);
31525
31526         var pt = new Ext.lib.Point(Ext.lib.Event.getPageX(e), Ext.lib.Event.getPageY(e));
31527         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
31528         } else {
31529             if (this.clickValidator(e)) {
31530
31531                 // set the initial element position
31532                 this.setStartPosition();
31533
31534                 this.b4MouseDown(e);
31535                 this.onMouseDown(e);
31536
31537                 this.DDM.handleMouseDown(e, this);
31538
31539                 this.DDM.stopEvent(e);
31540             } else {
31541
31542
31543             }
31544         }
31545     },
31546
31547     clickValidator: function(e) {
31548         var target = e.getTarget();
31549         return ( this.isValidHandleChild(target) &&
31550                     (this.id == this.handleElId ||
31551                         this.DDM.handleWasClicked(target, this.id)) );
31552     },
31553
31554     /**
31555      * Allows you to specify a tag name that should not start a drag operation
31556      * when clicked.  This is designed to facilitate embedding links within a
31557      * drag handle that do something other than start the drag.
31558      * @method addInvalidHandleType
31559      * @param {string} tagName the type of element to exclude
31560      */
31561     addInvalidHandleType: function(tagName) {
31562         var type = tagName.toUpperCase();
31563         this.invalidHandleTypes[type] = type;
31564     },
31565
31566     /**
31567      * Lets you to specify an element id for a child of a drag handle
31568      * that should not initiate a drag
31569      * @method addInvalidHandleId
31570      * @param {string} id the element id of the element you wish to ignore
31571      */
31572     addInvalidHandleId: function(id) {
31573         if (typeof id !== "string") {
31574             id = Ext.id(id);
31575         }
31576         this.invalidHandleIds[id] = id;
31577     },
31578
31579     /**
31580      * Lets you specify a css class of elements that will not initiate a drag
31581      * @method addInvalidHandleClass
31582      * @param {string} cssClass the class of the elements you wish to ignore
31583      */
31584     addInvalidHandleClass: function(cssClass) {
31585         this.invalidHandleClasses.push(cssClass);
31586     },
31587
31588     /**
31589      * Unsets an excluded tag name set by addInvalidHandleType
31590      * @method removeInvalidHandleType
31591      * @param {string} tagName the type of element to unexclude
31592      */
31593     removeInvalidHandleType: function(tagName) {
31594         var type = tagName.toUpperCase();
31595         // this.invalidHandleTypes[type] = null;
31596         delete this.invalidHandleTypes[type];
31597     },
31598
31599     /**
31600      * Unsets an invalid handle id
31601      * @method removeInvalidHandleId
31602      * @param {string} id the id of the element to re-enable
31603      */
31604     removeInvalidHandleId: function(id) {
31605         if (typeof id !== "string") {
31606             id = Ext.id(id);
31607         }
31608         delete this.invalidHandleIds[id];
31609     },
31610
31611     /**
31612      * Unsets an invalid css class
31613      * @method removeInvalidHandleClass
31614      * @param {string} cssClass the class of the element(s) you wish to
31615      * re-enable
31616      */
31617     removeInvalidHandleClass: function(cssClass) {
31618         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
31619             if (this.invalidHandleClasses[i] == cssClass) {
31620                 delete this.invalidHandleClasses[i];
31621             }
31622         }
31623     },
31624
31625     /**
31626      * Checks the tag exclusion list to see if this click should be ignored
31627      * @method isValidHandleChild
31628      * @param {HTMLElement} node the HTMLElement to evaluate
31629      * @return {boolean} true if this is a valid tag type, false if not
31630      */
31631     isValidHandleChild: function(node) {
31632
31633         var valid = true;
31634         // var n = (node.nodeName == "#text") ? node.parentNode : node;
31635         var nodeName;
31636         try {
31637             nodeName = node.nodeName.toUpperCase();
31638         } catch(e) {
31639             nodeName = node.nodeName;
31640         }
31641         valid = valid && !this.invalidHandleTypes[nodeName];
31642         valid = valid && !this.invalidHandleIds[node.id];
31643
31644         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
31645             valid = !Ext.fly(node).hasClass(this.invalidHandleClasses[i]);
31646         }
31647
31648
31649         return valid;
31650
31651     },
31652
31653     /**
31654      * Create the array of horizontal tick marks if an interval was specified
31655      * in setXConstraint().
31656      * @method setXTicks
31657      * @private
31658      */
31659     setXTicks: function(iStartX, iTickSize) {
31660         this.xTicks = [];
31661         this.xTickSize = iTickSize;
31662
31663         var tickMap = {};
31664
31665         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
31666             if (!tickMap[i]) {
31667                 this.xTicks[this.xTicks.length] = i;
31668                 tickMap[i] = true;
31669             }
31670         }
31671
31672         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
31673             if (!tickMap[i]) {
31674                 this.xTicks[this.xTicks.length] = i;
31675                 tickMap[i] = true;
31676             }
31677         }
31678
31679         this.xTicks.sort(this.DDM.numericSort) ;
31680     },
31681
31682     /**
31683      * Create the array of vertical tick marks if an interval was specified in
31684      * setYConstraint().
31685      * @method setYTicks
31686      * @private
31687      */
31688     setYTicks: function(iStartY, iTickSize) {
31689         this.yTicks = [];
31690         this.yTickSize = iTickSize;
31691
31692         var tickMap = {};
31693
31694         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
31695             if (!tickMap[i]) {
31696                 this.yTicks[this.yTicks.length] = i;
31697                 tickMap[i] = true;
31698             }
31699         }
31700
31701         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
31702             if (!tickMap[i]) {
31703                 this.yTicks[this.yTicks.length] = i;
31704                 tickMap[i] = true;
31705             }
31706         }
31707
31708         this.yTicks.sort(this.DDM.numericSort) ;
31709     },
31710
31711     /**
31712      * By default, the element can be dragged any place on the screen.  Use
31713      * this method to limit the horizontal travel of the element.  Pass in
31714      * 0,0 for the parameters if you want to lock the drag to the y axis.
31715      * @method setXConstraint
31716      * @param {int} iLeft the number of pixels the element can move to the left
31717      * @param {int} iRight the number of pixels the element can move to the
31718      * right
31719      * @param {int} iTickSize optional parameter for specifying that the
31720      * element
31721      * should move iTickSize pixels at a time.
31722      */
31723     setXConstraint: function(iLeft, iRight, iTickSize) {
31724         this.leftConstraint = iLeft;
31725         this.rightConstraint = iRight;
31726
31727         this.minX = this.initPageX - iLeft;
31728         this.maxX = this.initPageX + iRight;
31729         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
31730
31731         this.constrainX = true;
31732     },
31733
31734     /**
31735      * Clears any constraints applied to this instance.  Also clears ticks
31736      * since they can't exist independent of a constraint at this time.
31737      * @method clearConstraints
31738      */
31739     clearConstraints: function() {
31740         this.constrainX = false;
31741         this.constrainY = false;
31742         this.clearTicks();
31743     },
31744
31745     /**
31746      * Clears any tick interval defined for this instance
31747      * @method clearTicks
31748      */
31749     clearTicks: function() {
31750         this.xTicks = null;
31751         this.yTicks = null;
31752         this.xTickSize = 0;
31753         this.yTickSize = 0;
31754     },
31755
31756     /**
31757      * By default, the element can be dragged any place on the screen.  Set
31758      * this to limit the vertical travel of the element.  Pass in 0,0 for the
31759      * parameters if you want to lock the drag to the x axis.
31760      * @method setYConstraint
31761      * @param {int} iUp the number of pixels the element can move up
31762      * @param {int} iDown the number of pixels the element can move down
31763      * @param {int} iTickSize optional parameter for specifying that the
31764      * element should move iTickSize pixels at a time.
31765      */
31766     setYConstraint: function(iUp, iDown, iTickSize) {
31767         this.topConstraint = iUp;
31768         this.bottomConstraint = iDown;
31769
31770         this.minY = this.initPageY - iUp;
31771         this.maxY = this.initPageY + iDown;
31772         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
31773
31774         this.constrainY = true;
31775
31776     },
31777
31778     /**
31779      * resetConstraints must be called if you manually reposition a dd element.
31780      * @method resetConstraints
31781      * @param {boolean} maintainOffset
31782      */
31783     resetConstraints: function() {
31784         // Maintain offsets if necessary
31785         if (this.initPageX || this.initPageX === 0) {
31786             // figure out how much this thing has moved
31787             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
31788             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
31789
31790             this.setInitPosition(dx, dy);
31791
31792         // This is the first time we have detected the element's position
31793         } else {
31794             this.setInitPosition();
31795         }
31796
31797         if (this.constrainX) {
31798             this.setXConstraint( this.leftConstraint,
31799                                  this.rightConstraint,
31800                                  this.xTickSize        );
31801         }
31802
31803         if (this.constrainY) {
31804             this.setYConstraint( this.topConstraint,
31805                                  this.bottomConstraint,
31806                                  this.yTickSize         );
31807         }
31808     },
31809
31810     /**
31811      * Normally the drag element is moved pixel by pixel, but we can specify
31812      * that it move a number of pixels at a time.  This method resolves the
31813      * location when we have it set up like this.
31814      * @method getTick
31815      * @param {int} val where we want to place the object
31816      * @param {int[]} tickArray sorted array of valid points
31817      * @return {int} the closest tick
31818      * @private
31819      */
31820     getTick: function(val, tickArray) {
31821         if (!tickArray) {
31822             // If tick interval is not defined, it is effectively 1 pixel,
31823             // so we return the value passed to us.
31824             return val;
31825         } else if (tickArray[0] >= val) {
31826             // The value is lower than the first tick, so we return the first
31827             // tick.
31828             return tickArray[0];
31829         } else {
31830             for (var i=0, len=tickArray.length; i<len; ++i) {
31831                 var next = i + 1;
31832                 if (tickArray[next] && tickArray[next] >= val) {
31833                     var diff1 = val - tickArray[i];
31834                     var diff2 = tickArray[next] - val;
31835                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
31836                 }
31837             }
31838
31839             // The value is larger than the last tick, so we return the last
31840             // tick.
31841             return tickArray[tickArray.length - 1];
31842         }
31843     },
31844
31845     /**
31846      * toString method
31847      * @method toString
31848      * @return {string} string representation of the dd obj
31849      */
31850     toString: function() {
31851         return ("DragDrop " + this.id);
31852     }
31853
31854 };
31855
31856 })();
31857 /*
31858  * The drag and drop utility provides a framework for building drag and drop
31859  * applications.  In addition to enabling drag and drop for specific elements,
31860  * the drag and drop elements are tracked by the manager class, and the
31861  * interactions between the various elements are tracked during the drag and
31862  * the implementing code is notified about these important moments.
31863  */
31864
31865 // Only load the library once.  Rewriting the manager class would orphan
31866 // existing drag and drop instances.
31867 if (!Ext.dd.DragDropMgr) {
31868
31869 /**
31870  * @class Ext.dd.DragDropMgr
31871  * DragDropMgr is a singleton that tracks the element interaction for
31872  * all DragDrop items in the window.  Generally, you will not call
31873  * this class directly, but it does have helper methods that could
31874  * be useful in your DragDrop implementations.
31875  * @singleton
31876  */
31877 Ext.dd.DragDropMgr = function() {
31878
31879     var Event = Ext.EventManager;
31880
31881     return {
31882
31883         /**
31884          * Two dimensional Array of registered DragDrop objects.  The first
31885          * dimension is the DragDrop item group, the second the DragDrop
31886          * object.
31887          * @property ids
31888          * @type String[]
31889          * @private
31890          * @static
31891          */
31892         ids: {},
31893
31894         /**
31895          * Array of element ids defined as drag handles.  Used to determine
31896          * if the element that generated the mousedown event is actually the
31897          * handle and not the html element itself.
31898          * @property handleIds
31899          * @type String[]
31900          * @private
31901          * @static
31902          */
31903         handleIds: {},
31904
31905         /**
31906          * the DragDrop object that is currently being dragged
31907          * @property dragCurrent
31908          * @type DragDrop
31909          * @private
31910          * @static
31911          **/
31912         dragCurrent: null,
31913
31914         /**
31915          * the DragDrop object(s) that are being hovered over
31916          * @property dragOvers
31917          * @type Array
31918          * @private
31919          * @static
31920          */
31921         dragOvers: {},
31922
31923         /**
31924          * the X distance between the cursor and the object being dragged
31925          * @property deltaX
31926          * @type int
31927          * @private
31928          * @static
31929          */
31930         deltaX: 0,
31931
31932         /**
31933          * the Y distance between the cursor and the object being dragged
31934          * @property deltaY
31935          * @type int
31936          * @private
31937          * @static
31938          */
31939         deltaY: 0,
31940
31941         /**
31942          * Flag to determine if we should prevent the default behavior of the
31943          * events we define. By default this is true, but this can be set to
31944          * false if you need the default behavior (not recommended)
31945          * @property preventDefault
31946          * @type boolean
31947          * @static
31948          */
31949         preventDefault: true,
31950
31951         /**
31952          * Flag to determine if we should stop the propagation of the events
31953          * we generate. This is true by default but you may want to set it to
31954          * false if the html element contains other features that require the
31955          * mouse click.
31956          * @property stopPropagation
31957          * @type boolean
31958          * @static
31959          */
31960         stopPropagation: true,
31961
31962         /**
31963          * Internal flag that is set to true when drag and drop has been
31964          * intialized
31965          * @property initialized
31966          * @private
31967          * @static
31968          */
31969         initialized: false,
31970
31971         /**
31972          * All drag and drop can be disabled.
31973          * @property locked
31974          * @private
31975          * @static
31976          */
31977         locked: false,
31978
31979         /**
31980          * Called the first time an element is registered.
31981          * @method init
31982          * @private
31983          * @static
31984          */
31985         init: function() {
31986             this.initialized = true;
31987         },
31988
31989         /**
31990          * In point mode, drag and drop interaction is defined by the
31991          * location of the cursor during the drag/drop
31992          * @property POINT
31993          * @type int
31994          * @static
31995          */
31996         POINT: 0,
31997
31998         /**
31999          * In intersect mode, drag and drop interaction is defined by the
32000          * overlap of two or more drag and drop objects.
32001          * @property INTERSECT
32002          * @type int
32003          * @static
32004          */
32005         INTERSECT: 1,
32006
32007         /**
32008          * The current drag and drop mode.  Default: POINT
32009          * @property mode
32010          * @type int
32011          * @static
32012          */
32013         mode: 0,
32014
32015         /**
32016          * Runs method on all drag and drop objects
32017          * @method _execOnAll
32018          * @private
32019          * @static
32020          */
32021         _execOnAll: function(sMethod, args) {
32022             for (var i in this.ids) {
32023                 for (var j in this.ids[i]) {
32024                     var oDD = this.ids[i][j];
32025                     if (! this.isTypeOfDD(oDD)) {
32026                         continue;
32027                     }
32028                     oDD[sMethod].apply(oDD, args);
32029                 }
32030             }
32031         },
32032
32033         /**
32034          * Drag and drop initialization.  Sets up the global event handlers
32035          * @method _onLoad
32036          * @private
32037          * @static
32038          */
32039         _onLoad: function() {
32040
32041             this.init();
32042
32043
32044             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
32045             Event.on(document, "mousemove", this.handleMouseMove, this, true);
32046             Event.on(window,   "unload",    this._onUnload, this, true);
32047             Event.on(window,   "resize",    this._onResize, this, true);
32048             // Event.on(window,   "mouseout",    this._test);
32049
32050         },
32051
32052         /**
32053          * Reset constraints on all drag and drop objs
32054          * @method _onResize
32055          * @private
32056          * @static
32057          */
32058         _onResize: function(e) {
32059             this._execOnAll("resetConstraints", []);
32060         },
32061
32062         /**
32063          * Lock all drag and drop functionality
32064          * @method lock
32065          * @static
32066          */
32067         lock: function() { this.locked = true; },
32068
32069         /**
32070          * Unlock all drag and drop functionality
32071          * @method unlock
32072          * @static
32073          */
32074         unlock: function() { this.locked = false; },
32075
32076         /**
32077          * Is drag and drop locked?
32078          * @method isLocked
32079          * @return {boolean} True if drag and drop is locked, false otherwise.
32080          * @static
32081          */
32082         isLocked: function() { return this.locked; },
32083
32084         /**
32085          * Location cache that is set for all drag drop objects when a drag is
32086          * initiated, cleared when the drag is finished.
32087          * @property locationCache
32088          * @private
32089          * @static
32090          */
32091         locationCache: {},
32092
32093         /**
32094          * Set useCache to false if you want to force object the lookup of each
32095          * drag and drop linked element constantly during a drag.
32096          * @property useCache
32097          * @type boolean
32098          * @static
32099          */
32100         useCache: true,
32101
32102         /**
32103          * The number of pixels that the mouse needs to move after the
32104          * mousedown before the drag is initiated.  Default=3;
32105          * @property clickPixelThresh
32106          * @type int
32107          * @static
32108          */
32109         clickPixelThresh: 3,
32110
32111         /**
32112          * The number of milliseconds after the mousedown event to initiate the
32113          * drag if we don't get a mouseup event. Default=350
32114          * @property clickTimeThresh
32115          * @type int
32116          * @static
32117          */
32118         clickTimeThresh: 350,
32119
32120         /**
32121          * Flag that indicates that either the drag pixel threshold or the
32122          * mousdown time threshold has been met
32123          * @property dragThreshMet
32124          * @type boolean
32125          * @private
32126          * @static
32127          */
32128         dragThreshMet: false,
32129
32130         /**
32131          * Timeout used for the click time threshold
32132          * @property clickTimeout
32133          * @type Object
32134          * @private
32135          * @static
32136          */
32137         clickTimeout: null,
32138
32139         /**
32140          * The X position of the mousedown event stored for later use when a
32141          * drag threshold is met.
32142          * @property startX
32143          * @type int
32144          * @private
32145          * @static
32146          */
32147         startX: 0,
32148
32149         /**
32150          * The Y position of the mousedown event stored for later use when a
32151          * drag threshold is met.
32152          * @property startY
32153          * @type int
32154          * @private
32155          * @static
32156          */
32157         startY: 0,
32158
32159         /**
32160          * Each DragDrop instance must be registered with the DragDropMgr.
32161          * This is executed in DragDrop.init()
32162          * @method regDragDrop
32163          * @param {DragDrop} oDD the DragDrop object to register
32164          * @param {String} sGroup the name of the group this element belongs to
32165          * @static
32166          */
32167         regDragDrop: function(oDD, sGroup) {
32168             if (!this.initialized) { this.init(); }
32169
32170             if (!this.ids[sGroup]) {
32171                 this.ids[sGroup] = {};
32172             }
32173             this.ids[sGroup][oDD.id] = oDD;
32174         },
32175
32176         /**
32177          * Removes the supplied dd instance from the supplied group. Executed
32178          * by DragDrop.removeFromGroup, so don't call this function directly.
32179          * @method removeDDFromGroup
32180          * @private
32181          * @static
32182          */
32183         removeDDFromGroup: function(oDD, sGroup) {
32184             if (!this.ids[sGroup]) {
32185                 this.ids[sGroup] = {};
32186             }
32187
32188             var obj = this.ids[sGroup];
32189             if (obj && obj[oDD.id]) {
32190                 delete obj[oDD.id];
32191             }
32192         },
32193
32194         /**
32195          * Unregisters a drag and drop item.  This is executed in
32196          * DragDrop.unreg, use that method instead of calling this directly.
32197          * @method _remove
32198          * @private
32199          * @static
32200          */
32201         _remove: function(oDD) {
32202             for (var g in oDD.groups) {
32203                 if (g && this.ids[g] && this.ids[g][oDD.id]) {
32204                     delete this.ids[g][oDD.id];
32205                 }
32206             }
32207             delete this.handleIds[oDD.id];
32208         },
32209
32210         /**
32211          * Each DragDrop handle element must be registered.  This is done
32212          * automatically when executing DragDrop.setHandleElId()
32213          * @method regHandle
32214          * @param {String} sDDId the DragDrop id this element is a handle for
32215          * @param {String} sHandleId the id of the element that is the drag
32216          * handle
32217          * @static
32218          */
32219         regHandle: function(sDDId, sHandleId) {
32220             if (!this.handleIds[sDDId]) {
32221                 this.handleIds[sDDId] = {};
32222             }
32223             this.handleIds[sDDId][sHandleId] = sHandleId;
32224         },
32225
32226         /**
32227          * Utility function to determine if a given element has been
32228          * registered as a drag drop item.
32229          * @method isDragDrop
32230          * @param {String} id the element id to check
32231          * @return {boolean} true if this element is a DragDrop item,
32232          * false otherwise
32233          * @static
32234          */
32235         isDragDrop: function(id) {
32236             return ( this.getDDById(id) ) ? true : false;
32237         },
32238
32239         /**
32240          * Returns the drag and drop instances that are in all groups the
32241          * passed in instance belongs to.
32242          * @method getRelated
32243          * @param {DragDrop} p_oDD the obj to get related data for
32244          * @param {boolean} bTargetsOnly if true, only return targetable objs
32245          * @return {DragDrop[]} the related instances
32246          * @static
32247          */
32248         getRelated: function(p_oDD, bTargetsOnly) {
32249             var oDDs = [];
32250             for (var i in p_oDD.groups) {
32251                 for (var j in this.ids[i]) {
32252                     var dd = this.ids[i][j];
32253                     if (! this.isTypeOfDD(dd)) {
32254                         continue;
32255                     }
32256                     if (!bTargetsOnly || dd.isTarget) {
32257                         oDDs[oDDs.length] = dd;
32258                     }
32259                 }
32260             }
32261
32262             return oDDs;
32263         },
32264
32265         /**
32266          * Returns true if the specified dd target is a legal target for
32267          * the specifice drag obj
32268          * @method isLegalTarget
32269          * @param {DragDrop} oDD the drag obj
32270          * @param {DragDrop} oTargetDD the target
32271          * @return {boolean} true if the target is a legal target for the
32272          * dd obj
32273          * @static
32274          */
32275         isLegalTarget: function (oDD, oTargetDD) {
32276             var targets = this.getRelated(oDD, true);
32277             for (var i=0, len=targets.length;i<len;++i) {
32278                 if (targets[i].id == oTargetDD.id) {
32279                     return true;
32280                 }
32281             }
32282
32283             return false;
32284         },
32285
32286         /**
32287          * My goal is to be able to transparently determine if an object is
32288          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
32289          * returns "object", oDD.constructor.toString() always returns
32290          * "DragDrop" and not the name of the subclass.  So for now it just
32291          * evaluates a well-known variable in DragDrop.
32292          * @method isTypeOfDD
32293          * @param {Object} the object to evaluate
32294          * @return {boolean} true if typeof oDD = DragDrop
32295          * @static
32296          */
32297         isTypeOfDD: function (oDD) {
32298             return (oDD && oDD.__ygDragDrop);
32299         },
32300
32301         /**
32302          * Utility function to determine if a given element has been
32303          * registered as a drag drop handle for the given Drag Drop object.
32304          * @method isHandle
32305          * @param {String} id the element id to check
32306          * @return {boolean} true if this element is a DragDrop handle, false
32307          * otherwise
32308          * @static
32309          */
32310         isHandle: function(sDDId, sHandleId) {
32311             return ( this.handleIds[sDDId] &&
32312                             this.handleIds[sDDId][sHandleId] );
32313         },
32314
32315         /**
32316          * Returns the DragDrop instance for a given id
32317          * @method getDDById
32318          * @param {String} id the id of the DragDrop object
32319          * @return {DragDrop} the drag drop object, null if it is not found
32320          * @static
32321          */
32322         getDDById: function(id) {
32323             for (var i in this.ids) {
32324                 if (this.ids[i][id]) {
32325                     return this.ids[i][id];
32326                 }
32327             }
32328             return null;
32329         },
32330
32331         /**
32332          * Fired after a registered DragDrop object gets the mousedown event.
32333          * Sets up the events required to track the object being dragged
32334          * @method handleMouseDown
32335          * @param {Event} e the event
32336          * @param oDD the DragDrop object being dragged
32337          * @private
32338          * @static
32339          */
32340         handleMouseDown: function(e, oDD) {
32341             if(Ext.QuickTips){
32342                 Ext.QuickTips.ddDisable();
32343             }
32344             if(this.dragCurrent){
32345                 // the original browser mouseup wasn't handled (e.g. outside FF browser window)
32346                 // so clean up first to avoid breaking the next drag
32347                 this.handleMouseUp(e);
32348             }
32349             
32350             this.currentTarget = e.getTarget();
32351             this.dragCurrent = oDD;
32352
32353             var el = oDD.getEl();
32354
32355             // track start position
32356             this.startX = e.getPageX();
32357             this.startY = e.getPageY();
32358
32359             this.deltaX = this.startX - el.offsetLeft;
32360             this.deltaY = this.startY - el.offsetTop;
32361
32362             this.dragThreshMet = false;
32363
32364             this.clickTimeout = setTimeout(
32365                     function() {
32366                         var DDM = Ext.dd.DDM;
32367                         DDM.startDrag(DDM.startX, DDM.startY);
32368                     },
32369                     this.clickTimeThresh );
32370         },
32371
32372         /**
32373          * Fired when either the drag pixel threshol or the mousedown hold
32374          * time threshold has been met.
32375          * @method startDrag
32376          * @param x {int} the X position of the original mousedown
32377          * @param y {int} the Y position of the original mousedown
32378          * @static
32379          */
32380         startDrag: function(x, y) {
32381             clearTimeout(this.clickTimeout);
32382             if (this.dragCurrent) {
32383                 this.dragCurrent.b4StartDrag(x, y);
32384                 this.dragCurrent.startDrag(x, y);
32385             }
32386             this.dragThreshMet = true;
32387         },
32388
32389         /**
32390          * Internal function to handle the mouseup event.  Will be invoked
32391          * from the context of the document.
32392          * @method handleMouseUp
32393          * @param {Event} e the event
32394          * @private
32395          * @static
32396          */
32397         handleMouseUp: function(e) {
32398
32399             if(Ext.QuickTips){
32400                 Ext.QuickTips.ddEnable();
32401             }
32402             if (! this.dragCurrent) {
32403                 return;
32404             }
32405
32406             clearTimeout(this.clickTimeout);
32407
32408             if (this.dragThreshMet) {
32409                 this.fireEvents(e, true);
32410             } else {
32411             }
32412
32413             this.stopDrag(e);
32414
32415             this.stopEvent(e);
32416         },
32417
32418         /**
32419          * Utility to stop event propagation and event default, if these
32420          * features are turned on.
32421          * @method stopEvent
32422          * @param {Event} e the event as returned by this.getEvent()
32423          * @static
32424          */
32425         stopEvent: function(e){
32426             if(this.stopPropagation) {
32427                 e.stopPropagation();
32428             }
32429
32430             if (this.preventDefault) {
32431                 e.preventDefault();
32432             }
32433         },
32434
32435         /**
32436          * Internal function to clean up event handlers after the drag
32437          * operation is complete
32438          * @method stopDrag
32439          * @param {Event} e the event
32440          * @private
32441          * @static
32442          */
32443         stopDrag: function(e) {
32444             // Fire the drag end event for the item that was dragged
32445             if (this.dragCurrent) {
32446                 if (this.dragThreshMet) {
32447                     this.dragCurrent.b4EndDrag(e);
32448                     this.dragCurrent.endDrag(e);
32449                 }
32450
32451                 this.dragCurrent.onMouseUp(e);
32452             }
32453
32454             this.dragCurrent = null;
32455             this.dragOvers = {};
32456         },
32457
32458         /**
32459          * Internal function to handle the mousemove event.  Will be invoked
32460          * from the context of the html element.
32461          *
32462          * @TODO figure out what we can do about mouse events lost when the
32463          * user drags objects beyond the window boundary.  Currently we can
32464          * detect this in internet explorer by verifying that the mouse is
32465          * down during the mousemove event.  Firefox doesn't give us the
32466          * button state on the mousemove event.
32467          * @method handleMouseMove
32468          * @param {Event} e the event
32469          * @private
32470          * @static
32471          */
32472         handleMouseMove: function(e) {
32473             if (! this.dragCurrent) {
32474                 return true;
32475             }
32476             // var button = e.which || e.button;
32477
32478             // check for IE mouseup outside of page boundary
32479             if (Ext.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
32480                 this.stopEvent(e);
32481                 return this.handleMouseUp(e);
32482             }
32483
32484             if (!this.dragThreshMet) {
32485                 var diffX = Math.abs(this.startX - e.getPageX());
32486                 var diffY = Math.abs(this.startY - e.getPageY());
32487                 if (diffX > this.clickPixelThresh ||
32488                             diffY > this.clickPixelThresh) {
32489                     this.startDrag(this.startX, this.startY);
32490                 }
32491             }
32492
32493             if (this.dragThreshMet) {
32494                 this.dragCurrent.b4Drag(e);
32495                 this.dragCurrent.onDrag(e);
32496                 if(!this.dragCurrent.moveOnly){
32497                     this.fireEvents(e, false);
32498                 }
32499             }
32500
32501             this.stopEvent(e);
32502
32503             return true;
32504         },
32505
32506         /**
32507          * Iterates over all of the DragDrop elements to find ones we are
32508          * hovering over or dropping on
32509          * @method fireEvents
32510          * @param {Event} e the event
32511          * @param {boolean} isDrop is this a drop op or a mouseover op?
32512          * @private
32513          * @static
32514          */
32515         fireEvents: function(e, isDrop) {
32516             var dc = this.dragCurrent;
32517
32518             // If the user did the mouse up outside of the window, we could
32519             // get here even though we have ended the drag.
32520             if (!dc || dc.isLocked()) {
32521                 return;
32522             }
32523
32524             var pt = e.getPoint();
32525
32526             // cache the previous dragOver array
32527             var oldOvers = [];
32528
32529             var outEvts   = [];
32530             var overEvts  = [];
32531             var dropEvts  = [];
32532             var enterEvts = [];
32533
32534             // Check to see if the object(s) we were hovering over is no longer
32535             // being hovered over so we can fire the onDragOut event
32536             for (var i in this.dragOvers) {
32537
32538                 var ddo = this.dragOvers[i];
32539
32540                 if (! this.isTypeOfDD(ddo)) {
32541                     continue;
32542                 }
32543
32544                 if (! this.isOverTarget(pt, ddo, this.mode)) {
32545                     outEvts.push( ddo );
32546                 }
32547
32548                 oldOvers[i] = true;
32549                 delete this.dragOvers[i];
32550             }
32551
32552             for (var sGroup in dc.groups) {
32553
32554                 if ("string" != typeof sGroup) {
32555                     continue;
32556                 }
32557
32558                 for (i in this.ids[sGroup]) {
32559                     var oDD = this.ids[sGroup][i];
32560                     if (! this.isTypeOfDD(oDD)) {
32561                         continue;
32562                     }
32563
32564                     if (oDD.isTarget && !oDD.isLocked() && ((oDD != dc) || (dc.ignoreSelf === false))) {
32565                         if (this.isOverTarget(pt, oDD, this.mode)) {
32566                             // look for drop interactions
32567                             if (isDrop) {
32568                                 dropEvts.push( oDD );
32569                             // look for drag enter and drag over interactions
32570                             } else {
32571
32572                                 // initial drag over: dragEnter fires
32573                                 if (!oldOvers[oDD.id]) {
32574                                     enterEvts.push( oDD );
32575                                 // subsequent drag overs: dragOver fires
32576                                 } else {
32577                                     overEvts.push( oDD );
32578                                 }
32579
32580                                 this.dragOvers[oDD.id] = oDD;
32581                             }
32582                         }
32583                     }
32584                 }
32585             }
32586
32587             if (this.mode) {
32588                 if (outEvts.length) {
32589                     dc.b4DragOut(e, outEvts);
32590                     dc.onDragOut(e, outEvts);
32591                 }
32592
32593                 if (enterEvts.length) {
32594                     dc.onDragEnter(e, enterEvts);
32595                 }
32596
32597                 if (overEvts.length) {
32598                     dc.b4DragOver(e, overEvts);
32599                     dc.onDragOver(e, overEvts);
32600                 }
32601
32602                 if (dropEvts.length) {
32603                     dc.b4DragDrop(e, dropEvts);
32604                     dc.onDragDrop(e, dropEvts);
32605                 }
32606
32607             } else {
32608                 // fire dragout events
32609                 var len = 0;
32610                 for (i=0, len=outEvts.length; i<len; ++i) {
32611                     dc.b4DragOut(e, outEvts[i].id);
32612                     dc.onDragOut(e, outEvts[i].id);
32613                 }
32614
32615                 // fire enter events
32616                 for (i=0,len=enterEvts.length; i<len; ++i) {
32617                     // dc.b4DragEnter(e, oDD.id);
32618                     dc.onDragEnter(e, enterEvts[i].id);
32619                 }
32620
32621                 // fire over events
32622                 for (i=0,len=overEvts.length; i<len; ++i) {
32623                     dc.b4DragOver(e, overEvts[i].id);
32624                     dc.onDragOver(e, overEvts[i].id);
32625                 }
32626
32627                 // fire drop events
32628                 for (i=0, len=dropEvts.length; i<len; ++i) {
32629                     dc.b4DragDrop(e, dropEvts[i].id);
32630                     dc.onDragDrop(e, dropEvts[i].id);
32631                 }
32632
32633             }
32634
32635             // notify about a drop that did not find a target
32636             if (isDrop && !dropEvts.length) {
32637                 dc.onInvalidDrop(e);
32638             }
32639
32640         },
32641
32642         /**
32643          * Helper function for getting the best match from the list of drag
32644          * and drop objects returned by the drag and drop events when we are
32645          * in INTERSECT mode.  It returns either the first object that the
32646          * cursor is over, or the object that has the greatest overlap with
32647          * the dragged element.
32648          * @method getBestMatch
32649          * @param  {DragDrop[]} dds The array of drag and drop objects
32650          * targeted
32651          * @return {DragDrop}       The best single match
32652          * @static
32653          */
32654         getBestMatch: function(dds) {
32655             var winner = null;
32656             // Return null if the input is not what we expect
32657             //if (!dds || !dds.length || dds.length == 0) {
32658                // winner = null;
32659             // If there is only one item, it wins
32660             //} else if (dds.length == 1) {
32661
32662             var len = dds.length;
32663
32664             if (len == 1) {
32665                 winner = dds[0];
32666             } else {
32667                 // Loop through the targeted items
32668                 for (var i=0; i<len; ++i) {
32669                     var dd = dds[i];
32670                     // If the cursor is over the object, it wins.  If the
32671                     // cursor is over multiple matches, the first one we come
32672                     // to wins.
32673                     if (dd.cursorIsOver) {
32674                         winner = dd;
32675                         break;
32676                     // Otherwise the object with the most overlap wins
32677                     } else {
32678                         if (!winner ||
32679                             winner.overlap.getArea() < dd.overlap.getArea()) {
32680                             winner = dd;
32681                         }
32682                     }
32683                 }
32684             }
32685
32686             return winner;
32687         },
32688
32689         /**
32690          * Refreshes the cache of the top-left and bottom-right points of the
32691          * drag and drop objects in the specified group(s).  This is in the
32692          * format that is stored in the drag and drop instance, so typical
32693          * usage is:
32694          * <code>
32695          * Ext.dd.DragDropMgr.refreshCache(ddinstance.groups);
32696          * </code>
32697          * Alternatively:
32698          * <code>
32699          * Ext.dd.DragDropMgr.refreshCache({group1:true, group2:true});
32700          * </code>
32701          * @TODO this really should be an indexed array.  Alternatively this
32702          * method could accept both.
32703          * @method refreshCache
32704          * @param {Object} groups an associative array of groups to refresh
32705          * @static
32706          */
32707         refreshCache: function(groups) {
32708             for (var sGroup in groups) {
32709                 if ("string" != typeof sGroup) {
32710                     continue;
32711                 }
32712                 for (var i in this.ids[sGroup]) {
32713                     var oDD = this.ids[sGroup][i];
32714
32715                     if (this.isTypeOfDD(oDD)) {
32716                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
32717                         var loc = this.getLocation(oDD);
32718                         if (loc) {
32719                             this.locationCache[oDD.id] = loc;
32720                         } else {
32721                             delete this.locationCache[oDD.id];
32722                             // this will unregister the drag and drop object if
32723                             // the element is not in a usable state
32724                             // oDD.unreg();
32725                         }
32726                     }
32727                 }
32728             }
32729         },
32730
32731         /**
32732          * This checks to make sure an element exists and is in the DOM.  The
32733          * main purpose is to handle cases where innerHTML is used to remove
32734          * drag and drop objects from the DOM.  IE provides an 'unspecified
32735          * error' when trying to access the offsetParent of such an element
32736          * @method verifyEl
32737          * @param {HTMLElement} el the element to check
32738          * @return {boolean} true if the element looks usable
32739          * @static
32740          */
32741         verifyEl: function(el) {
32742             if (el) {
32743                 var parent;
32744                 if(Ext.isIE){
32745                     try{
32746                         parent = el.offsetParent;
32747                     }catch(e){}
32748                 }else{
32749                     parent = el.offsetParent;
32750                 }
32751                 if (parent) {
32752                     return true;
32753                 }
32754             }
32755
32756             return false;
32757         },
32758
32759         /**
32760          * Returns a Region object containing the drag and drop element's position
32761          * and size, including the padding configured for it
32762          * @method getLocation
32763          * @param {DragDrop} oDD the drag and drop object to get the
32764          *                       location for
32765          * @return {Ext.lib.Region} a Region object representing the total area
32766          *                             the element occupies, including any padding
32767          *                             the instance is configured for.
32768          * @static
32769          */
32770         getLocation: function(oDD) {
32771             if (! this.isTypeOfDD(oDD)) {
32772                 return null;
32773             }
32774
32775             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
32776
32777             try {
32778                 pos= Ext.lib.Dom.getXY(el);
32779             } catch (e) { }
32780
32781             if (!pos) {
32782                 return null;
32783             }
32784
32785             x1 = pos[0];
32786             x2 = x1 + el.offsetWidth;
32787             y1 = pos[1];
32788             y2 = y1 + el.offsetHeight;
32789
32790             t = y1 - oDD.padding[0];
32791             r = x2 + oDD.padding[1];
32792             b = y2 + oDD.padding[2];
32793             l = x1 - oDD.padding[3];
32794
32795             return new Ext.lib.Region( t, r, b, l );
32796         },
32797
32798         /**
32799          * Checks the cursor location to see if it over the target
32800          * @method isOverTarget
32801          * @param {Ext.lib.Point} pt The point to evaluate
32802          * @param {DragDrop} oTarget the DragDrop object we are inspecting
32803          * @return {boolean} true if the mouse is over the target
32804          * @private
32805          * @static
32806          */
32807         isOverTarget: function(pt, oTarget, intersect) {
32808             // use cache if available
32809             var loc = this.locationCache[oTarget.id];
32810             if (!loc || !this.useCache) {
32811                 loc = this.getLocation(oTarget);
32812                 this.locationCache[oTarget.id] = loc;
32813
32814             }
32815
32816             if (!loc) {
32817                 return false;
32818             }
32819
32820             oTarget.cursorIsOver = loc.contains( pt );
32821
32822             // DragDrop is using this as a sanity check for the initial mousedown
32823             // in this case we are done.  In POINT mode, if the drag obj has no
32824             // contraints, we are also done. Otherwise we need to evaluate the
32825             // location of the target as related to the actual location of the
32826             // dragged element.
32827             var dc = this.dragCurrent;
32828             if (!dc || !dc.getTargetCoord ||
32829                     (!intersect && !dc.constrainX && !dc.constrainY)) {
32830                 return oTarget.cursorIsOver;
32831             }
32832
32833             oTarget.overlap = null;
32834
32835             // Get the current location of the drag element, this is the
32836             // location of the mouse event less the delta that represents
32837             // where the original mousedown happened on the element.  We
32838             // need to consider constraints and ticks as well.
32839             var pos = dc.getTargetCoord(pt.x, pt.y);
32840
32841             var el = dc.getDragEl();
32842             var curRegion = new Ext.lib.Region( pos.y,
32843                                                    pos.x + el.offsetWidth,
32844                                                    pos.y + el.offsetHeight,
32845                                                    pos.x );
32846
32847             var overlap = curRegion.intersect(loc);
32848
32849             if (overlap) {
32850                 oTarget.overlap = overlap;
32851                 return (intersect) ? true : oTarget.cursorIsOver;
32852             } else {
32853                 return false;
32854             }
32855         },
32856
32857         /**
32858          * unload event handler
32859          * @method _onUnload
32860          * @private
32861          * @static
32862          */
32863         _onUnload: function(e, me) {
32864             Ext.dd.DragDropMgr.unregAll();
32865         },
32866
32867         /**
32868          * Cleans up the drag and drop events and objects.
32869          * @method unregAll
32870          * @private
32871          * @static
32872          */
32873         unregAll: function() {
32874
32875             if (this.dragCurrent) {
32876                 this.stopDrag();
32877                 this.dragCurrent = null;
32878             }
32879
32880             this._execOnAll("unreg", []);
32881
32882             for (var i in this.elementCache) {
32883                 delete this.elementCache[i];
32884             }
32885
32886             this.elementCache = {};
32887             this.ids = {};
32888         },
32889
32890         /**
32891          * A cache of DOM elements
32892          * @property elementCache
32893          * @private
32894          * @static
32895          */
32896         elementCache: {},
32897
32898         /**
32899          * Get the wrapper for the DOM element specified
32900          * @method getElWrapper
32901          * @param {String} id the id of the element to get
32902          * @return {Ext.dd.DDM.ElementWrapper} the wrapped element
32903          * @private
32904          * @deprecated This wrapper isn't that useful
32905          * @static
32906          */
32907         getElWrapper: function(id) {
32908             var oWrapper = this.elementCache[id];
32909             if (!oWrapper || !oWrapper.el) {
32910                 oWrapper = this.elementCache[id] =
32911                     new this.ElementWrapper(Ext.getDom(id));
32912             }
32913             return oWrapper;
32914         },
32915
32916         /**
32917          * Returns the actual DOM element
32918          * @method getElement
32919          * @param {String} id the id of the elment to get
32920          * @return {Object} The element
32921          * @deprecated use Ext.lib.Ext.getDom instead
32922          * @static
32923          */
32924         getElement: function(id) {
32925             return Ext.getDom(id);
32926         },
32927
32928         /**
32929          * Returns the style property for the DOM element (i.e.,
32930          * document.getElById(id).style)
32931          * @method getCss
32932          * @param {String} id the id of the elment to get
32933          * @return {Object} The style property of the element
32934          * @deprecated use Ext.lib.Dom instead
32935          * @static
32936          */
32937         getCss: function(id) {
32938             var el = Ext.getDom(id);
32939             return (el) ? el.style : null;
32940         },
32941
32942         /**
32943          * Inner class for cached elements
32944          * @class Ext.dd.DragDropMgr.ElementWrapper
32945          * @for DragDropMgr
32946          * @private
32947          * @deprecated
32948          */
32949         ElementWrapper: function(el) {
32950                 /**
32951                  * The element
32952                  * @property el
32953                  */
32954                 this.el = el || null;
32955                 /**
32956                  * The element id
32957                  * @property id
32958                  */
32959                 this.id = this.el && el.id;
32960                 /**
32961                  * A reference to the style property
32962                  * @property css
32963                  */
32964                 this.css = this.el && el.style;
32965             },
32966
32967         /**
32968          * Returns the X position of an html element
32969          * @method getPosX
32970          * @param el the element for which to get the position
32971          * @return {int} the X coordinate
32972          * @for DragDropMgr
32973          * @deprecated use Ext.lib.Dom.getX instead
32974          * @static
32975          */
32976         getPosX: function(el) {
32977             return Ext.lib.Dom.getX(el);
32978         },
32979
32980         /**
32981          * Returns the Y position of an html element
32982          * @method getPosY
32983          * @param el the element for which to get the position
32984          * @return {int} the Y coordinate
32985          * @deprecated use Ext.lib.Dom.getY instead
32986          * @static
32987          */
32988         getPosY: function(el) {
32989             return Ext.lib.Dom.getY(el);
32990         },
32991
32992         /**
32993          * Swap two nodes.  In IE, we use the native method, for others we
32994          * emulate the IE behavior
32995          * @method swapNode
32996          * @param n1 the first node to swap
32997          * @param n2 the other node to swap
32998          * @static
32999          */
33000         swapNode: function(n1, n2) {
33001             if (n1.swapNode) {
33002                 n1.swapNode(n2);
33003             } else {
33004                 var p = n2.parentNode;
33005                 var s = n2.nextSibling;
33006
33007                 if (s == n1) {
33008                     p.insertBefore(n1, n2);
33009                 } else if (n2 == n1.nextSibling) {
33010                     p.insertBefore(n2, n1);
33011                 } else {
33012                     n1.parentNode.replaceChild(n2, n1);
33013                     p.insertBefore(n1, s);
33014                 }
33015             }
33016         },
33017
33018         /**
33019          * Returns the current scroll position
33020          * @method getScroll
33021          * @private
33022          * @static
33023          */
33024         getScroll: function () {
33025             var t, l, dde=document.documentElement, db=document.body;
33026             if (dde && (dde.scrollTop || dde.scrollLeft)) {
33027                 t = dde.scrollTop;
33028                 l = dde.scrollLeft;
33029             } else if (db) {
33030                 t = db.scrollTop;
33031                 l = db.scrollLeft;
33032             } else {
33033
33034             }
33035             return { top: t, left: l };
33036         },
33037
33038         /**
33039          * Returns the specified element style property
33040          * @method getStyle
33041          * @param {HTMLElement} el          the element
33042          * @param {string}      styleProp   the style property
33043          * @return {string} The value of the style property
33044          * @deprecated use Ext.lib.Dom.getStyle
33045          * @static
33046          */
33047         getStyle: function(el, styleProp) {
33048             return Ext.fly(el).getStyle(styleProp);
33049         },
33050
33051         /**
33052          * Gets the scrollTop
33053          * @method getScrollTop
33054          * @return {int} the document's scrollTop
33055          * @static
33056          */
33057         getScrollTop: function () {
33058             return this.getScroll().top;
33059         },
33060
33061         /**
33062          * Gets the scrollLeft
33063          * @method getScrollLeft
33064          * @return {int} the document's scrollTop
33065          * @static
33066          */
33067         getScrollLeft: function () {
33068             return this.getScroll().left;
33069         },
33070
33071         /**
33072          * Sets the x/y position of an element to the location of the
33073          * target element.
33074          * @method moveToEl
33075          * @param {HTMLElement} moveEl      The element to move
33076          * @param {HTMLElement} targetEl    The position reference element
33077          * @static
33078          */
33079         moveToEl: function (moveEl, targetEl) {
33080             var aCoord = Ext.lib.Dom.getXY(targetEl);
33081             Ext.lib.Dom.setXY(moveEl, aCoord);
33082         },
33083
33084         /**
33085          * Numeric array sort function
33086          * @method numericSort
33087          * @static
33088          */
33089         numericSort: function(a, b) {
33090             return (a - b);
33091         },
33092
33093         /**
33094          * Internal counter
33095          * @property _timeoutCount
33096          * @private
33097          * @static
33098          */
33099         _timeoutCount: 0,
33100
33101         /**
33102          * Trying to make the load order less important.  Without this we get
33103          * an error if this file is loaded before the Event Utility.
33104          * @method _addListeners
33105          * @private
33106          * @static
33107          */
33108         _addListeners: function() {
33109             var DDM = Ext.dd.DDM;
33110             if ( Ext.lib.Event && document ) {
33111                 DDM._onLoad();
33112             } else {
33113                 if (DDM._timeoutCount > 2000) {
33114                 } else {
33115                     setTimeout(DDM._addListeners, 10);
33116                     if (document && document.body) {
33117                         DDM._timeoutCount += 1;
33118                     }
33119                 }
33120             }
33121         },
33122
33123         /**
33124          * Recursively searches the immediate parent and all child nodes for
33125          * the handle element in order to determine wheter or not it was
33126          * clicked.
33127          * @method handleWasClicked
33128          * @param node the html element to inspect
33129          * @static
33130          */
33131         handleWasClicked: function(node, id) {
33132             if (this.isHandle(id, node.id)) {
33133                 return true;
33134             } else {
33135                 // check to see if this is a text node child of the one we want
33136                 var p = node.parentNode;
33137
33138                 while (p) {
33139                     if (this.isHandle(id, p.id)) {
33140                         return true;
33141                     } else {
33142                         p = p.parentNode;
33143                     }
33144                 }
33145             }
33146
33147             return false;
33148         }
33149
33150     };
33151
33152 }();
33153
33154 // shorter alias, save a few bytes
33155 Ext.dd.DDM = Ext.dd.DragDropMgr;
33156 Ext.dd.DDM._addListeners();
33157
33158 }
33159
33160 /**
33161  * @class Ext.dd.DD
33162  * A DragDrop implementation where the linked element follows the
33163  * mouse cursor during a drag.
33164  * @extends Ext.dd.DragDrop
33165  * @constructor
33166  * @param {String} id the id of the linked element
33167  * @param {String} sGroup the group of related DragDrop items
33168  * @param {object} config an object containing configurable attributes
33169  *                Valid properties for DD:
33170  *                    scroll
33171  */
33172 Ext.dd.DD = function(id, sGroup, config) {
33173     if (id) {
33174         this.init(id, sGroup, config);
33175     }
33176 };
33177
33178 Ext.extend(Ext.dd.DD, Ext.dd.DragDrop, {
33179
33180     /**
33181      * When set to true, the utility automatically tries to scroll the browser
33182      * window when a drag and drop element is dragged near the viewport boundary.
33183      * Defaults to true.
33184      * @property scroll
33185      * @type boolean
33186      */
33187     scroll: true,
33188
33189     /**
33190      * Sets the pointer offset to the distance between the linked element's top
33191      * left corner and the location the element was clicked
33192      * @method autoOffset
33193      * @param {int} iPageX the X coordinate of the click
33194      * @param {int} iPageY the Y coordinate of the click
33195      */
33196     autoOffset: function(iPageX, iPageY) {
33197         var x = iPageX - this.startPageX;
33198         var y = iPageY - this.startPageY;
33199         this.setDelta(x, y);
33200     },
33201
33202     /**
33203      * Sets the pointer offset.  You can call this directly to force the
33204      * offset to be in a particular location (e.g., pass in 0,0 to set it
33205      * to the center of the object)
33206      * @method setDelta
33207      * @param {int} iDeltaX the distance from the left
33208      * @param {int} iDeltaY the distance from the top
33209      */
33210     setDelta: function(iDeltaX, iDeltaY) {
33211         this.deltaX = iDeltaX;
33212         this.deltaY = iDeltaY;
33213     },
33214
33215     /**
33216      * Sets the drag element to the location of the mousedown or click event,
33217      * maintaining the cursor location relative to the location on the element
33218      * that was clicked.  Override this if you want to place the element in a
33219      * location other than where the cursor is.
33220      * @method setDragElPos
33221      * @param {int} iPageX the X coordinate of the mousedown or drag event
33222      * @param {int} iPageY the Y coordinate of the mousedown or drag event
33223      */
33224     setDragElPos: function(iPageX, iPageY) {
33225         // the first time we do this, we are going to check to make sure
33226         // the element has css positioning
33227
33228         var el = this.getDragEl();
33229         this.alignElWithMouse(el, iPageX, iPageY);
33230     },
33231
33232     /**
33233      * Sets the element to the location of the mousedown or click event,
33234      * maintaining the cursor location relative to the location on the element
33235      * that was clicked.  Override this if you want to place the element in a
33236      * location other than where the cursor is.
33237      * @method alignElWithMouse
33238      * @param {HTMLElement} el the element to move
33239      * @param {int} iPageX the X coordinate of the mousedown or drag event
33240      * @param {int} iPageY the Y coordinate of the mousedown or drag event
33241      */
33242     alignElWithMouse: function(el, iPageX, iPageY) {
33243         var oCoord = this.getTargetCoord(iPageX, iPageY);
33244         var fly = el.dom ? el : Ext.fly(el, '_dd');
33245         if (!this.deltaSetXY) {
33246             var aCoord = [oCoord.x, oCoord.y];
33247             fly.setXY(aCoord);
33248             var newLeft = fly.getLeft(true);
33249             var newTop  = fly.getTop(true);
33250             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
33251         } else {
33252             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
33253         }
33254
33255         this.cachePosition(oCoord.x, oCoord.y);
33256         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
33257         return oCoord;
33258     },
33259
33260     /**
33261      * Saves the most recent position so that we can reset the constraints and
33262      * tick marks on-demand.  We need to know this so that we can calculate the
33263      * number of pixels the element is offset from its original position.
33264      * @method cachePosition
33265      * @param iPageX the current x position (optional, this just makes it so we
33266      * don't have to look it up again)
33267      * @param iPageY the current y position (optional, this just makes it so we
33268      * don't have to look it up again)
33269      */
33270     cachePosition: function(iPageX, iPageY) {
33271         if (iPageX) {
33272             this.lastPageX = iPageX;
33273             this.lastPageY = iPageY;
33274         } else {
33275             var aCoord = Ext.lib.Dom.getXY(this.getEl());
33276             this.lastPageX = aCoord[0];
33277             this.lastPageY = aCoord[1];
33278         }
33279     },
33280
33281     /**
33282      * Auto-scroll the window if the dragged object has been moved beyond the
33283      * visible window boundary.
33284      * @method autoScroll
33285      * @param {int} x the drag element's x position
33286      * @param {int} y the drag element's y position
33287      * @param {int} h the height of the drag element
33288      * @param {int} w the width of the drag element
33289      * @private
33290      */
33291     autoScroll: function(x, y, h, w) {
33292
33293         if (this.scroll) {
33294             // The client height
33295             var clientH = Ext.lib.Dom.getViewHeight();
33296
33297             // The client width
33298             var clientW = Ext.lib.Dom.getViewWidth();
33299
33300             // The amt scrolled down
33301             var st = this.DDM.getScrollTop();
33302
33303             // The amt scrolled right
33304             var sl = this.DDM.getScrollLeft();
33305
33306             // Location of the bottom of the element
33307             var bot = h + y;
33308
33309             // Location of the right of the element
33310             var right = w + x;
33311
33312             // The distance from the cursor to the bottom of the visible area,
33313             // adjusted so that we don't scroll if the cursor is beyond the
33314             // element drag constraints
33315             var toBot = (clientH + st - y - this.deltaY);
33316
33317             // The distance from the cursor to the right of the visible area
33318             var toRight = (clientW + sl - x - this.deltaX);
33319
33320
33321             // How close to the edge the cursor must be before we scroll
33322             // var thresh = (document.all) ? 100 : 40;
33323             var thresh = 40;
33324
33325             // How many pixels to scroll per autoscroll op.  This helps to reduce
33326             // clunky scrolling. IE is more sensitive about this ... it needs this
33327             // value to be higher.
33328             var scrAmt = (document.all) ? 80 : 30;
33329
33330             // Scroll down if we are near the bottom of the visible page and the
33331             // obj extends below the crease
33332             if ( bot > clientH && toBot < thresh ) {
33333                 window.scrollTo(sl, st + scrAmt);
33334             }
33335
33336             // Scroll up if the window is scrolled down and the top of the object
33337             // goes above the top border
33338             if ( y < st && st > 0 && y - st < thresh ) {
33339                 window.scrollTo(sl, st - scrAmt);
33340             }
33341
33342             // Scroll right if the obj is beyond the right border and the cursor is
33343             // near the border.
33344             if ( right > clientW && toRight < thresh ) {
33345                 window.scrollTo(sl + scrAmt, st);
33346             }
33347
33348             // Scroll left if the window has been scrolled to the right and the obj
33349             // extends past the left border
33350             if ( x < sl && sl > 0 && x - sl < thresh ) {
33351                 window.scrollTo(sl - scrAmt, st);
33352             }
33353         }
33354     },
33355
33356     /**
33357      * Finds the location the element should be placed if we want to move
33358      * it to where the mouse location less the click offset would place us.
33359      * @method getTargetCoord
33360      * @param {int} iPageX the X coordinate of the click
33361      * @param {int} iPageY the Y coordinate of the click
33362      * @return an object that contains the coordinates (Object.x and Object.y)
33363      * @private
33364      */
33365     getTargetCoord: function(iPageX, iPageY) {
33366         var x = iPageX - this.deltaX;
33367         var y = iPageY - this.deltaY;
33368
33369         if (this.constrainX) {
33370             if (x < this.minX) { x = this.minX; }
33371             if (x > this.maxX) { x = this.maxX; }
33372         }
33373
33374         if (this.constrainY) {
33375             if (y < this.minY) { y = this.minY; }
33376             if (y > this.maxY) { y = this.maxY; }
33377         }
33378
33379         x = this.getTick(x, this.xTicks);
33380         y = this.getTick(y, this.yTicks);
33381
33382
33383         return {x:x, y:y};
33384     },
33385
33386     /**
33387      * Sets up config options specific to this class. Overrides
33388      * Ext.dd.DragDrop, but all versions of this method through the
33389      * inheritance chain are called
33390      */
33391     applyConfig: function() {
33392         Ext.dd.DD.superclass.applyConfig.call(this);
33393         this.scroll = (this.config.scroll !== false);
33394     },
33395
33396     /**
33397      * Event that fires prior to the onMouseDown event.  Overrides
33398      * Ext.dd.DragDrop.
33399      */
33400     b4MouseDown: function(e) {
33401         // this.resetConstraints();
33402         this.autoOffset(e.getPageX(),
33403                             e.getPageY());
33404     },
33405
33406     /**
33407      * Event that fires prior to the onDrag event.  Overrides
33408      * Ext.dd.DragDrop.
33409      */
33410     b4Drag: function(e) {
33411         this.setDragElPos(e.getPageX(),
33412                             e.getPageY());
33413     },
33414
33415     toString: function() {
33416         return ("DD " + this.id);
33417     }
33418
33419     //////////////////////////////////////////////////////////////////////////
33420     // Debugging ygDragDrop events that can be overridden
33421     //////////////////////////////////////////////////////////////////////////
33422     /*
33423     startDrag: function(x, y) {
33424     },
33425
33426     onDrag: function(e) {
33427     },
33428
33429     onDragEnter: function(e, id) {
33430     },
33431
33432     onDragOver: function(e, id) {
33433     },
33434
33435     onDragOut: function(e, id) {
33436     },
33437
33438     onDragDrop: function(e, id) {
33439     },
33440
33441     endDrag: function(e) {
33442     }
33443
33444     */
33445
33446 });
33447 /**
33448  * @class Ext.dd.DDProxy
33449  * A DragDrop implementation that inserts an empty, bordered div into
33450  * the document that follows the cursor during drag operations.  At the time of
33451  * the click, the frame div is resized to the dimensions of the linked html
33452  * element, and moved to the exact location of the linked element.
33453  *
33454  * References to the "frame" element refer to the single proxy element that
33455  * was created to be dragged in place of all DDProxy elements on the
33456  * page.
33457  *
33458  * @extends Ext.dd.DD
33459  * @constructor
33460  * @param {String} id the id of the linked html element
33461  * @param {String} sGroup the group of related DragDrop objects
33462  * @param {object} config an object containing configurable attributes
33463  *                Valid properties for DDProxy in addition to those in DragDrop:
33464  *                   resizeFrame, centerFrame, dragElId
33465  */
33466 Ext.dd.DDProxy = function(id, sGroup, config) {
33467     if (id) {
33468         this.init(id, sGroup, config);
33469         this.initFrame();
33470     }
33471 };
33472
33473 /**
33474  * The default drag frame div id
33475  * @property Ext.dd.DDProxy.dragElId
33476  * @type String
33477  * @static
33478  */
33479 Ext.dd.DDProxy.dragElId = "ygddfdiv";
33480
33481 Ext.extend(Ext.dd.DDProxy, Ext.dd.DD, {
33482
33483     /**
33484      * By default we resize the drag frame to be the same size as the element
33485      * we want to drag (this is to get the frame effect).  We can turn it off
33486      * if we want a different behavior.
33487      * @property resizeFrame
33488      * @type boolean
33489      */
33490     resizeFrame: true,
33491
33492     /**
33493      * By default the frame is positioned exactly where the drag element is, so
33494      * we use the cursor offset provided by Ext.dd.DD.  Another option that works only if
33495      * you do not have constraints on the obj is to have the drag frame centered
33496      * around the cursor.  Set centerFrame to true for this effect.
33497      * @property centerFrame
33498      * @type boolean
33499      */
33500     centerFrame: false,
33501
33502     /**
33503      * Creates the proxy element if it does not yet exist
33504      * @method createFrame
33505      */
33506     createFrame: function() {
33507         var self = this;
33508         var body = document.body;
33509
33510         if (!body || !body.firstChild) {
33511             setTimeout( function() { self.createFrame(); }, 50 );
33512             return;
33513         }
33514
33515         var div = this.getDragEl();
33516
33517         if (!div) {
33518             div    = document.createElement("div");
33519             div.id = this.dragElId;
33520             var s  = div.style;
33521
33522             s.position   = "absolute";
33523             s.visibility = "hidden";
33524             s.cursor     = "move";
33525             s.border     = "2px solid #aaa";
33526             s.zIndex     = 999;
33527
33528             // appendChild can blow up IE if invoked prior to the window load event
33529             // while rendering a table.  It is possible there are other scenarios
33530             // that would cause this to happen as well.
33531             body.insertBefore(div, body.firstChild);
33532         }
33533     },
33534
33535     /**
33536      * Initialization for the drag frame element.  Must be called in the
33537      * constructor of all subclasses
33538      * @method initFrame
33539      */
33540     initFrame: function() {
33541         this.createFrame();
33542     },
33543
33544     applyConfig: function() {
33545         Ext.dd.DDProxy.superclass.applyConfig.call(this);
33546
33547         this.resizeFrame = (this.config.resizeFrame !== false);
33548         this.centerFrame = (this.config.centerFrame);
33549         this.setDragElId(this.config.dragElId || Ext.dd.DDProxy.dragElId);
33550     },
33551
33552     /**
33553      * Resizes the drag frame to the dimensions of the clicked object, positions
33554      * it over the object, and finally displays it
33555      * @method showFrame
33556      * @param {int} iPageX X click position
33557      * @param {int} iPageY Y click position
33558      * @private
33559      */
33560     showFrame: function(iPageX, iPageY) {
33561         var el = this.getEl();
33562         var dragEl = this.getDragEl();
33563         var s = dragEl.style;
33564
33565         this._resizeProxy();
33566
33567         if (this.centerFrame) {
33568             this.setDelta( Math.round(parseInt(s.width,  10)/2),
33569                            Math.round(parseInt(s.height, 10)/2) );
33570         }
33571
33572         this.setDragElPos(iPageX, iPageY);
33573
33574         Ext.fly(dragEl).show();
33575     },
33576
33577     /**
33578      * The proxy is automatically resized to the dimensions of the linked
33579      * element when a drag is initiated, unless resizeFrame is set to false
33580      * @method _resizeProxy
33581      * @private
33582      */
33583     _resizeProxy: function() {
33584         if (this.resizeFrame) {
33585             var el = this.getEl();
33586             Ext.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
33587         }
33588     },
33589
33590     // overrides Ext.dd.DragDrop
33591     b4MouseDown: function(e) {
33592         var x = e.getPageX();
33593         var y = e.getPageY();
33594         this.autoOffset(x, y);
33595         this.setDragElPos(x, y);
33596     },
33597
33598     // overrides Ext.dd.DragDrop
33599     b4StartDrag: function(x, y) {
33600         // show the drag frame
33601         this.showFrame(x, y);
33602     },
33603
33604     // overrides Ext.dd.DragDrop
33605     b4EndDrag: function(e) {
33606         Ext.fly(this.getDragEl()).hide();
33607     },
33608
33609     // overrides Ext.dd.DragDrop
33610     // By default we try to move the element to the last location of the frame.
33611     // This is so that the default behavior mirrors that of Ext.dd.DD.
33612     endDrag: function(e) {
33613
33614         var lel = this.getEl();
33615         var del = this.getDragEl();
33616
33617         // Show the drag frame briefly so we can get its position
33618         del.style.visibility = "";
33619
33620         this.beforeMove();
33621         // Hide the linked element before the move to get around a Safari
33622         // rendering bug.
33623         lel.style.visibility = "hidden";
33624         Ext.dd.DDM.moveToEl(lel, del);
33625         del.style.visibility = "hidden";
33626         lel.style.visibility = "";
33627
33628         this.afterDrag();
33629     },
33630
33631     beforeMove : function(){
33632
33633     },
33634
33635     afterDrag : function(){
33636
33637     },
33638
33639     toString: function() {
33640         return ("DDProxy " + this.id);
33641     }
33642
33643 });
33644 /**
33645  * @class Ext.dd.DDTarget
33646  * A DragDrop implementation that does not move, but can be a drop
33647  * target.  You would get the same result by simply omitting implementation
33648  * for the event callbacks, but this way we reduce the processing cost of the
33649  * event listener and the callbacks.
33650  * @extends Ext.dd.DragDrop
33651  * @constructor
33652  * @param {String} id the id of the element that is a drop target
33653  * @param {String} sGroup the group of related DragDrop objects
33654  * @param {object} config an object containing configurable attributes
33655  *                 Valid properties for DDTarget in addition to those in
33656  *                 DragDrop:
33657  *                    none
33658  */
33659 Ext.dd.DDTarget = function(id, sGroup, config) {
33660     if (id) {
33661         this.initTarget(id, sGroup, config);
33662     }
33663 };
33664
33665 // Ext.dd.DDTarget.prototype = new Ext.dd.DragDrop();
33666 Ext.extend(Ext.dd.DDTarget, Ext.dd.DragDrop, {
33667     /**
33668      * @hide
33669      * Overridden and disabled. A DDTarget does not support being dragged.
33670      * @method
33671      */
33672     getDragEl: Ext.emptyFn,
33673     /**
33674      * @hide
33675      * Overridden and disabled. A DDTarget does not support being dragged.
33676      * @method
33677      */
33678     isValidHandleChild: Ext.emptyFn,
33679     /**
33680      * @hide
33681      * Overridden and disabled. A DDTarget does not support being dragged.
33682      * @method
33683      */
33684     startDrag: Ext.emptyFn,
33685     /**
33686      * @hide
33687      * Overridden and disabled. A DDTarget does not support being dragged.
33688      * @method
33689      */
33690     endDrag: Ext.emptyFn,
33691     /**
33692      * @hide
33693      * Overridden and disabled. A DDTarget does not support being dragged.
33694      * @method
33695      */
33696     onDrag: Ext.emptyFn,
33697     /**
33698      * @hide
33699      * Overridden and disabled. A DDTarget does not support being dragged.
33700      * @method
33701      */
33702     onDragDrop: Ext.emptyFn,
33703     /**
33704      * @hide
33705      * Overridden and disabled. A DDTarget does not support being dragged.
33706      * @method
33707      */
33708     onDragEnter: Ext.emptyFn,
33709     /**
33710      * @hide
33711      * Overridden and disabled. A DDTarget does not support being dragged.
33712      * @method
33713      */
33714     onDragOut: Ext.emptyFn,
33715     /**
33716      * @hide
33717      * Overridden and disabled. A DDTarget does not support being dragged.
33718      * @method
33719      */
33720     onDragOver: Ext.emptyFn,
33721     /**
33722      * @hide
33723      * Overridden and disabled. A DDTarget does not support being dragged.
33724      * @method
33725      */
33726     onInvalidDrop: Ext.emptyFn,
33727     /**
33728      * @hide
33729      * Overridden and disabled. A DDTarget does not support being dragged.
33730      * @method
33731      */
33732     onMouseDown: Ext.emptyFn,
33733     /**
33734      * @hide
33735      * Overridden and disabled. A DDTarget does not support being dragged.
33736      * @method
33737      */
33738     onMouseUp: Ext.emptyFn,
33739     /**
33740      * @hide
33741      * Overridden and disabled. A DDTarget does not support being dragged.
33742      * @method
33743      */
33744     setXConstraint: Ext.emptyFn,
33745     /**
33746      * @hide
33747      * Overridden and disabled. A DDTarget does not support being dragged.
33748      * @method
33749      */
33750     setYConstraint: Ext.emptyFn,
33751     /**
33752      * @hide
33753      * Overridden and disabled. A DDTarget does not support being dragged.
33754      * @method
33755      */
33756     resetConstraints: Ext.emptyFn,
33757     /**
33758      * @hide
33759      * Overridden and disabled. A DDTarget does not support being dragged.
33760      * @method
33761      */
33762     clearConstraints: Ext.emptyFn,
33763     /**
33764      * @hide
33765      * Overridden and disabled. A DDTarget does not support being dragged.
33766      * @method
33767      */
33768     clearTicks: Ext.emptyFn,
33769     /**
33770      * @hide
33771      * Overridden and disabled. A DDTarget does not support being dragged.
33772      * @method
33773      */
33774     setInitPosition: Ext.emptyFn,
33775     /**
33776      * @hide
33777      * Overridden and disabled. A DDTarget does not support being dragged.
33778      * @method
33779      */
33780     setDragElId: Ext.emptyFn,
33781     /**
33782      * @hide
33783      * Overridden and disabled. A DDTarget does not support being dragged.
33784      * @method
33785      */
33786     setHandleElId: Ext.emptyFn,
33787     /**
33788      * @hide
33789      * Overridden and disabled. A DDTarget does not support being dragged.
33790      * @method
33791      */
33792     setOuterHandleElId: Ext.emptyFn,
33793     /**
33794      * @hide
33795      * Overridden and disabled. A DDTarget does not support being dragged.
33796      * @method
33797      */
33798     addInvalidHandleClass: Ext.emptyFn,
33799     /**
33800      * @hide
33801      * Overridden and disabled. A DDTarget does not support being dragged.
33802      * @method
33803      */
33804     addInvalidHandleId: Ext.emptyFn,
33805     /**
33806      * @hide
33807      * Overridden and disabled. A DDTarget does not support being dragged.
33808      * @method
33809      */
33810     addInvalidHandleType: Ext.emptyFn,
33811     /**
33812      * @hide
33813      * Overridden and disabled. A DDTarget does not support being dragged.
33814      * @method
33815      */
33816     removeInvalidHandleClass: Ext.emptyFn,
33817     /**
33818      * @hide
33819      * Overridden and disabled. A DDTarget does not support being dragged.
33820      * @method
33821      */
33822     removeInvalidHandleId: Ext.emptyFn,
33823     /**
33824      * @hide
33825      * Overridden and disabled. A DDTarget does not support being dragged.
33826      * @method
33827      */
33828     removeInvalidHandleType: Ext.emptyFn,
33829
33830     toString: function() {
33831         return ("DDTarget " + this.id);
33832     }
33833 });/**
33834  * @class Ext.dd.DragTracker
33835  * @extends Ext.util.Observable
33836  * A DragTracker listens for drag events on an Element and fires events at the start and end of the drag,
33837  * as well as during the drag. This is useful for components such as {@link Ext.slider.MultiSlider}, where there is
33838  * an element that can be dragged around to change the Slider's value.
33839  * DragTracker provides a series of template methods that should be overridden to provide functionality
33840  * in response to detected drag operations. These are onBeforeStart, onStart, onDrag and onEnd.
33841  * See {@link Ext.slider.MultiSlider}'s initEvents function for an example implementation.
33842  */
33843 Ext.dd.DragTracker = Ext.extend(Ext.util.Observable,  {    
33844     /**
33845      * @cfg {Boolean} active
33846          * Defaults to <tt>false</tt>.
33847          */     
33848     active: false,
33849     /**
33850      * @cfg {Number} tolerance
33851          * Number of pixels the drag target must be moved before dragging is considered to have started. Defaults to <tt>5</tt>.
33852          */     
33853     tolerance: 5,
33854     /**
33855      * @cfg {Boolean/Number} autoStart
33856          * Defaults to <tt>false</tt>. Specify <tt>true</tt> to defer trigger start by 1000 ms.
33857          * Specify a Number for the number of milliseconds to defer trigger start.
33858          */     
33859     autoStart: false,
33860     
33861     constructor : function(config){
33862         Ext.apply(this, config);
33863             this.addEvents(
33864                 /**
33865                  * @event mousedown
33866                  * @param {Object} this
33867                  * @param {Object} e event object
33868                  */
33869                 'mousedown',
33870                 /**
33871                  * @event mouseup
33872                  * @param {Object} this
33873                  * @param {Object} e event object
33874                  */
33875                 'mouseup',
33876                 /**
33877                  * @event mousemove
33878                  * @param {Object} this
33879                  * @param {Object} e event object
33880                  */
33881                 'mousemove',
33882                 /**
33883                  * @event dragstart
33884                  * @param {Object} this
33885                  * @param {Object} e event object
33886                  */
33887                 'dragstart',
33888                 /**
33889                  * @event dragend
33890                  * @param {Object} this
33891                  * @param {Object} e event object
33892                  */
33893                 'dragend',
33894                 /**
33895                  * @event drag
33896                  * @param {Object} this
33897                  * @param {Object} e event object
33898                  */
33899                 'drag'
33900             );
33901         
33902             this.dragRegion = new Ext.lib.Region(0,0,0,0);
33903         
33904             if(this.el){
33905                 this.initEl(this.el);
33906             }
33907         Ext.dd.DragTracker.superclass.constructor.call(this, config);
33908     },
33909
33910     initEl: function(el){
33911         this.el = Ext.get(el);
33912         el.on('mousedown', this.onMouseDown, this,
33913                 this.delegate ? {delegate: this.delegate} : undefined);
33914     },
33915
33916     destroy : function(){
33917         this.el.un('mousedown', this.onMouseDown, this);
33918         delete this.el;
33919     },
33920
33921     onMouseDown: function(e, target){
33922         if(this.fireEvent('mousedown', this, e) !== false && this.onBeforeStart(e) !== false){
33923             this.startXY = this.lastXY = e.getXY();
33924             this.dragTarget = this.delegate ? target : this.el.dom;
33925             if(this.preventDefault !== false){
33926                 e.preventDefault();
33927             }
33928             Ext.getDoc().on({
33929                 scope: this,
33930                 mouseup: this.onMouseUp,
33931                 mousemove: this.onMouseMove,
33932                 selectstart: this.stopSelect
33933             });
33934             if(this.autoStart){
33935                 this.timer = this.triggerStart.defer(this.autoStart === true ? 1000 : this.autoStart, this, [e]);
33936             }
33937         }
33938     },
33939
33940     onMouseMove: function(e, target){
33941         // HACK: IE hack to see if button was released outside of window. */
33942         if(this.active && Ext.isIE && !e.browserEvent.button){
33943             e.preventDefault();
33944             this.onMouseUp(e);
33945             return;
33946         }
33947
33948         e.preventDefault();
33949         var xy = e.getXY(), s = this.startXY;
33950         this.lastXY = xy;
33951         if(!this.active){
33952             if(Math.abs(s[0]-xy[0]) > this.tolerance || Math.abs(s[1]-xy[1]) > this.tolerance){
33953                 this.triggerStart(e);
33954             }else{
33955                 return;
33956             }
33957         }
33958         this.fireEvent('mousemove', this, e);
33959         this.onDrag(e);
33960         this.fireEvent('drag', this, e);
33961     },
33962
33963     onMouseUp: function(e) {
33964         var doc = Ext.getDoc(),
33965             wasActive = this.active;
33966             
33967         doc.un('mousemove', this.onMouseMove, this);
33968         doc.un('mouseup', this.onMouseUp, this);
33969         doc.un('selectstart', this.stopSelect, this);
33970         e.preventDefault();
33971         this.clearStart();
33972         this.active = false;
33973         delete this.elRegion;
33974         this.fireEvent('mouseup', this, e);
33975         if(wasActive){
33976             this.onEnd(e);
33977             this.fireEvent('dragend', this, e);
33978         }
33979     },
33980
33981     triggerStart: function(e) {
33982         this.clearStart();
33983         this.active = true;
33984         this.onStart(e);
33985         this.fireEvent('dragstart', this, e);
33986     },
33987
33988     clearStart : function() {
33989         if(this.timer){
33990             clearTimeout(this.timer);
33991             delete this.timer;
33992         }
33993     },
33994
33995     stopSelect : function(e) {
33996         e.stopEvent();
33997         return false;
33998     },
33999     
34000     /**
34001      * Template method which should be overridden by each DragTracker instance. Called when the user first clicks and
34002      * holds the mouse button down. Return false to disallow the drag
34003      * @param {Ext.EventObject} e The event object
34004      */
34005     onBeforeStart : function(e) {
34006
34007     },
34008
34009     /**
34010      * Template method which should be overridden by each DragTracker instance. Called when a drag operation starts
34011      * (e.g. the user has moved the tracked element beyond the specified tolerance)
34012      * @param {Ext.EventObject} e The event object
34013      */
34014     onStart : function(xy) {
34015
34016     },
34017
34018     /**
34019      * Template method which should be overridden by each DragTracker instance. Called whenever a drag has been detected.
34020      * @param {Ext.EventObject} e The event object
34021      */
34022     onDrag : function(e) {
34023
34024     },
34025
34026     /**
34027      * Template method which should be overridden by each DragTracker instance. Called when a drag operation has been completed
34028      * (e.g. the user clicked and held the mouse down, dragged the element and then released the mouse button)
34029      * @param {Ext.EventObject} e The event object
34030      */
34031     onEnd : function(e) {
34032
34033     },
34034
34035     /**
34036      * Returns the drag target
34037      * @return {Ext.Element} The element currently being tracked
34038      */
34039     getDragTarget : function(){
34040         return this.dragTarget;
34041     },
34042
34043     getDragCt : function(){
34044         return this.el;
34045     },
34046
34047     getXY : function(constrain){
34048         return constrain ?
34049                this.constrainModes[constrain].call(this, this.lastXY) : this.lastXY;
34050     },
34051
34052     getOffset : function(constrain){
34053         var xy = this.getXY(constrain),
34054             s = this.startXY;
34055         return [s[0]-xy[0], s[1]-xy[1]];
34056     },
34057
34058     constrainModes: {
34059         'point' : function(xy){
34060
34061             if(!this.elRegion){
34062                 this.elRegion = this.getDragCt().getRegion();
34063             }
34064
34065             var dr = this.dragRegion;
34066
34067             dr.left = xy[0];
34068             dr.top = xy[1];
34069             dr.right = xy[0];
34070             dr.bottom = xy[1];
34071
34072             dr.constrainTo(this.elRegion);
34073
34074             return [dr.left, dr.top];
34075         }
34076     }
34077 });/**
34078  * @class Ext.dd.ScrollManager
34079  * <p>Provides automatic scrolling of overflow regions in the page during drag operations.</p>
34080  * <p>The ScrollManager configs will be used as the defaults for any scroll container registered with it,
34081  * but you can also override most of the configs per scroll container by adding a 
34082  * <tt>ddScrollConfig</tt> object to the target element that contains these properties: {@link #hthresh},
34083  * {@link #vthresh}, {@link #increment} and {@link #frequency}.  Example usage:
34084  * <pre><code>
34085 var el = Ext.get('scroll-ct');
34086 el.ddScrollConfig = {
34087     vthresh: 50,
34088     hthresh: -1,
34089     frequency: 100,
34090     increment: 200
34091 };
34092 Ext.dd.ScrollManager.register(el);
34093 </code></pre>
34094  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
34095  * @singleton
34096  */
34097 Ext.dd.ScrollManager = function(){
34098     var ddm = Ext.dd.DragDropMgr;
34099     var els = {};
34100     var dragEl = null;
34101     var proc = {};
34102     
34103     var onStop = function(e){
34104         dragEl = null;
34105         clearProc();
34106     };
34107     
34108     var triggerRefresh = function(){
34109         if(ddm.dragCurrent){
34110              ddm.refreshCache(ddm.dragCurrent.groups);
34111         }
34112     };
34113     
34114     var doScroll = function(){
34115         if(ddm.dragCurrent){
34116             var dds = Ext.dd.ScrollManager;
34117             var inc = proc.el.ddScrollConfig ?
34118                       proc.el.ddScrollConfig.increment : dds.increment;
34119             if(!dds.animate){
34120                 if(proc.el.scroll(proc.dir, inc)){
34121                     triggerRefresh();
34122                 }
34123             }else{
34124                 proc.el.scroll(proc.dir, inc, true, dds.animDuration, triggerRefresh);
34125             }
34126         }
34127     };
34128     
34129     var clearProc = function(){
34130         if(proc.id){
34131             clearInterval(proc.id);
34132         }
34133         proc.id = 0;
34134         proc.el = null;
34135         proc.dir = "";
34136     };
34137
34138     var startProc = function(el, dir){
34139         clearProc();
34140         proc.el = el;
34141         proc.dir = dir;
34142         var group = el.ddScrollConfig ? el.ddScrollConfig.ddGroup : undefined,
34143             freq  = (el.ddScrollConfig && el.ddScrollConfig.frequency)
34144                   ? el.ddScrollConfig.frequency
34145                   : Ext.dd.ScrollManager.frequency;
34146
34147         if (group === undefined || ddm.dragCurrent.ddGroup == group) {
34148             proc.id = setInterval(doScroll, freq);
34149         }
34150     };
34151     
34152     var onFire = function(e, isDrop){
34153         if(isDrop || !ddm.dragCurrent){ return; }
34154         var dds = Ext.dd.ScrollManager;
34155         if(!dragEl || dragEl != ddm.dragCurrent){
34156             dragEl = ddm.dragCurrent;
34157             // refresh regions on drag start
34158             dds.refreshCache();
34159         }
34160         
34161         var xy = Ext.lib.Event.getXY(e);
34162         var pt = new Ext.lib.Point(xy[0], xy[1]);
34163         for(var id in els){
34164             var el = els[id], r = el._region;
34165             var c = el.ddScrollConfig ? el.ddScrollConfig : dds;
34166             if(r && r.contains(pt) && el.isScrollable()){
34167                 if(r.bottom - pt.y <= c.vthresh){
34168                     if(proc.el != el){
34169                         startProc(el, "down");
34170                     }
34171                     return;
34172                 }else if(r.right - pt.x <= c.hthresh){
34173                     if(proc.el != el){
34174                         startProc(el, "left");
34175                     }
34176                     return;
34177                 }else if(pt.y - r.top <= c.vthresh){
34178                     if(proc.el != el){
34179                         startProc(el, "up");
34180                     }
34181                     return;
34182                 }else if(pt.x - r.left <= c.hthresh){
34183                     if(proc.el != el){
34184                         startProc(el, "right");
34185                     }
34186                     return;
34187                 }
34188             }
34189         }
34190         clearProc();
34191     };
34192     
34193     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
34194     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
34195     
34196     return {
34197         /**
34198          * Registers new overflow element(s) to auto scroll
34199          * @param {Mixed/Array} el The id of or the element to be scrolled or an array of either
34200          */
34201         register : function(el){
34202             if(Ext.isArray(el)){
34203                 for(var i = 0, len = el.length; i < len; i++) {
34204                         this.register(el[i]);
34205                 }
34206             }else{
34207                 el = Ext.get(el);
34208                 els[el.id] = el;
34209             }
34210         },
34211         
34212         /**
34213          * Unregisters overflow element(s) so they are no longer scrolled
34214          * @param {Mixed/Array} el The id of or the element to be removed or an array of either
34215          */
34216         unregister : function(el){
34217             if(Ext.isArray(el)){
34218                 for(var i = 0, len = el.length; i < len; i++) {
34219                         this.unregister(el[i]);
34220                 }
34221             }else{
34222                 el = Ext.get(el);
34223                 delete els[el.id];
34224             }
34225         },
34226         
34227         /**
34228          * The number of pixels from the top or bottom edge of a container the pointer needs to be to
34229          * trigger scrolling (defaults to 25)
34230          * @type Number
34231          */
34232         vthresh : 25,
34233         /**
34234          * The number of pixels from the right or left edge of a container the pointer needs to be to
34235          * trigger scrolling (defaults to 25)
34236          * @type Number
34237          */
34238         hthresh : 25,
34239
34240         /**
34241          * The number of pixels to scroll in each scroll increment (defaults to 100)
34242          * @type Number
34243          */
34244         increment : 100,
34245         
34246         /**
34247          * The frequency of scrolls in milliseconds (defaults to 500)
34248          * @type Number
34249          */
34250         frequency : 500,
34251         
34252         /**
34253          * True to animate the scroll (defaults to true)
34254          * @type Boolean
34255          */
34256         animate: true,
34257         
34258         /**
34259          * The animation duration in seconds - 
34260          * MUST BE less than Ext.dd.ScrollManager.frequency! (defaults to .4)
34261          * @type Number
34262          */
34263         animDuration: .4,
34264         
34265         /**
34266          * The named drag drop {@link Ext.dd.DragSource#ddGroup group} to which this container belongs (defaults to undefined). 
34267          * If a ddGroup is specified, then container scrolling will only occur when a dragged object is in the same ddGroup.
34268          * @type String
34269          */
34270         ddGroup: undefined,
34271         
34272         /**
34273          * Manually trigger a cache refresh.
34274          */
34275         refreshCache : function(){
34276             for(var id in els){
34277                 if(typeof els[id] == 'object'){ // for people extending the object prototype
34278                     els[id]._region = els[id].getRegion();
34279                 }
34280             }
34281         }
34282     };
34283 }();/**
34284  * @class Ext.dd.Registry
34285  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
34286  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
34287  * @singleton
34288  */
34289 Ext.dd.Registry = function(){
34290     var elements = {}; 
34291     var handles = {}; 
34292     var autoIdSeed = 0;
34293
34294     var getId = function(el, autogen){
34295         if(typeof el == "string"){
34296             return el;
34297         }
34298         var id = el.id;
34299         if(!id && autogen !== false){
34300             id = "extdd-" + (++autoIdSeed);
34301             el.id = id;
34302         }
34303         return id;
34304     };
34305     
34306     return {
34307     /**
34308      * Resgister a drag drop element
34309      * @param {String/HTMLElement} element The id or DOM node to register
34310      * @param {Object} data (optional) An custom data object that will be passed between the elements that are involved
34311      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
34312      * knows how to interpret, plus there are some specific properties known to the Registry that should be
34313      * populated in the data object (if applicable):
34314      * <pre>
34315 Value      Description<br />
34316 ---------  ------------------------------------------<br />
34317 handles    Array of DOM nodes that trigger dragging<br />
34318            for the element being registered<br />
34319 isHandle   True if the element passed in triggers<br />
34320            dragging itself, else false
34321 </pre>
34322      */
34323         register : function(el, data){
34324             data = data || {};
34325             if(typeof el == "string"){
34326                 el = document.getElementById(el);
34327             }
34328             data.ddel = el;
34329             elements[getId(el)] = data;
34330             if(data.isHandle !== false){
34331                 handles[data.ddel.id] = data;
34332             }
34333             if(data.handles){
34334                 var hs = data.handles;
34335                 for(var i = 0, len = hs.length; i < len; i++){
34336                         handles[getId(hs[i])] = data;
34337                 }
34338             }
34339         },
34340
34341     /**
34342      * Unregister a drag drop element
34343      * @param {String/HTMLElement} element The id or DOM node to unregister
34344      */
34345         unregister : function(el){
34346             var id = getId(el, false);
34347             var data = elements[id];
34348             if(data){
34349                 delete elements[id];
34350                 if(data.handles){
34351                     var hs = data.handles;
34352                     for(var i = 0, len = hs.length; i < len; i++){
34353                         delete handles[getId(hs[i], false)];
34354                     }
34355                 }
34356             }
34357         },
34358
34359     /**
34360      * Returns the handle registered for a DOM Node by id
34361      * @param {String/HTMLElement} id The DOM node or id to look up
34362      * @return {Object} handle The custom handle data
34363      */
34364         getHandle : function(id){
34365             if(typeof id != "string"){ // must be element?
34366                 id = id.id;
34367             }
34368             return handles[id];
34369         },
34370
34371     /**
34372      * Returns the handle that is registered for the DOM node that is the target of the event
34373      * @param {Event} e The event
34374      * @return {Object} handle The custom handle data
34375      */
34376         getHandleFromEvent : function(e){
34377             var t = Ext.lib.Event.getTarget(e);
34378             return t ? handles[t.id] : null;
34379         },
34380
34381     /**
34382      * Returns a custom data object that is registered for a DOM node by id
34383      * @param {String/HTMLElement} id The DOM node or id to look up
34384      * @return {Object} data The custom data
34385      */
34386         getTarget : function(id){
34387             if(typeof id != "string"){ // must be element?
34388                 id = id.id;
34389             }
34390             return elements[id];
34391         },
34392
34393     /**
34394      * Returns a custom data object that is registered for the DOM node that is the target of the event
34395      * @param {Event} e The event
34396      * @return {Object} data The custom data
34397      */
34398         getTargetFromEvent : function(e){
34399             var t = Ext.lib.Event.getTarget(e);
34400             return t ? elements[t.id] || handles[t.id] : null;
34401         }
34402     };
34403 }();/**
34404  * @class Ext.dd.StatusProxy
34405  * A specialized drag proxy that supports a drop status icon, {@link Ext.Layer} styles and auto-repair.  This is the
34406  * default drag proxy used by all Ext.dd components.
34407  * @constructor
34408  * @param {Object} config
34409  */
34410 Ext.dd.StatusProxy = function(config){
34411     Ext.apply(this, config);
34412     this.id = this.id || Ext.id();
34413     this.el = new Ext.Layer({
34414         dh: {
34415             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
34416                 {tag: "div", cls: "x-dd-drop-icon"},
34417                 {tag: "div", cls: "x-dd-drag-ghost"}
34418             ]
34419         }, 
34420         shadow: !config || config.shadow !== false
34421     });
34422     this.ghost = Ext.get(this.el.dom.childNodes[1]);
34423     this.dropStatus = this.dropNotAllowed;
34424 };
34425
34426 Ext.dd.StatusProxy.prototype = {
34427     /**
34428      * @cfg {String} dropAllowed
34429      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
34430      */
34431     dropAllowed : "x-dd-drop-ok",
34432     /**
34433      * @cfg {String} dropNotAllowed
34434      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
34435      */
34436     dropNotAllowed : "x-dd-drop-nodrop",
34437
34438     /**
34439      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
34440      * over the current target element.
34441      * @param {String} cssClass The css class for the new drop status indicator image
34442      */
34443     setStatus : function(cssClass){
34444         cssClass = cssClass || this.dropNotAllowed;
34445         if(this.dropStatus != cssClass){
34446             this.el.replaceClass(this.dropStatus, cssClass);
34447             this.dropStatus = cssClass;
34448         }
34449     },
34450
34451     /**
34452      * Resets the status indicator to the default dropNotAllowed value
34453      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
34454      */
34455     reset : function(clearGhost){
34456         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
34457         this.dropStatus = this.dropNotAllowed;
34458         if(clearGhost){
34459             this.ghost.update("");
34460         }
34461     },
34462
34463     /**
34464      * Updates the contents of the ghost element
34465      * @param {String/HTMLElement} html The html that will replace the current innerHTML of the ghost element, or a
34466      * DOM node to append as the child of the ghost element (in which case the innerHTML will be cleared first).
34467      */
34468     update : function(html){
34469         if(typeof html == "string"){
34470             this.ghost.update(html);
34471         }else{
34472             this.ghost.update("");
34473             html.style.margin = "0";
34474             this.ghost.dom.appendChild(html);
34475         }
34476         var el = this.ghost.dom.firstChild; 
34477         if(el){
34478             Ext.fly(el).setStyle('float', 'none');
34479         }
34480     },
34481
34482     /**
34483      * Returns the underlying proxy {@link Ext.Layer}
34484      * @return {Ext.Layer} el
34485     */
34486     getEl : function(){
34487         return this.el;
34488     },
34489
34490     /**
34491      * Returns the ghost element
34492      * @return {Ext.Element} el
34493      */
34494     getGhost : function(){
34495         return this.ghost;
34496     },
34497
34498     /**
34499      * Hides the proxy
34500      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
34501      */
34502     hide : function(clear){
34503         this.el.hide();
34504         if(clear){
34505             this.reset(true);
34506         }
34507     },
34508
34509     /**
34510      * Stops the repair animation if it's currently running
34511      */
34512     stop : function(){
34513         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
34514             this.anim.stop();
34515         }
34516     },
34517
34518     /**
34519      * Displays this proxy
34520      */
34521     show : function(){
34522         this.el.show();
34523     },
34524
34525     /**
34526      * Force the Layer to sync its shadow and shim positions to the element
34527      */
34528     sync : function(){
34529         this.el.sync();
34530     },
34531
34532     /**
34533      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
34534      * invalid drop operation by the item being dragged.
34535      * @param {Array} xy The XY position of the element ([x, y])
34536      * @param {Function} callback The function to call after the repair is complete.
34537      * @param {Object} scope The scope (<code>this</code> reference) in which the callback function is executed. Defaults to the browser window.
34538      */
34539     repair : function(xy, callback, scope){
34540         this.callback = callback;
34541         this.scope = scope;
34542         if(xy && this.animRepair !== false){
34543             this.el.addClass("x-dd-drag-repair");
34544             this.el.hideUnders(true);
34545             this.anim = this.el.shift({
34546                 duration: this.repairDuration || .5,
34547                 easing: 'easeOut',
34548                 xy: xy,
34549                 stopFx: true,
34550                 callback: this.afterRepair,
34551                 scope: this
34552             });
34553         }else{
34554             this.afterRepair();
34555         }
34556     },
34557
34558     // private
34559     afterRepair : function(){
34560         this.hide(true);
34561         if(typeof this.callback == "function"){
34562             this.callback.call(this.scope || this);
34563         }
34564         this.callback = null;
34565         this.scope = null;
34566     },
34567     
34568     destroy: function(){
34569         Ext.destroy(this.ghost, this.el);    
34570     }
34571 };/**
34572  * @class Ext.dd.DragSource
34573  * @extends Ext.dd.DDProxy
34574  * A simple class that provides the basic implementation needed to make any element draggable.
34575  * @constructor
34576  * @param {Mixed} el The container element
34577  * @param {Object} config
34578  */
34579 Ext.dd.DragSource = function(el, config){
34580     this.el = Ext.get(el);
34581     if(!this.dragData){
34582         this.dragData = {};
34583     }
34584     
34585     Ext.apply(this, config);
34586     
34587     if(!this.proxy){
34588         this.proxy = new Ext.dd.StatusProxy();
34589     }
34590     Ext.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group, 
34591           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
34592     
34593     this.dragging = false;
34594 };
34595
34596 Ext.extend(Ext.dd.DragSource, Ext.dd.DDProxy, {
34597     /**
34598      * @cfg {String} ddGroup
34599      * A named drag drop group to which this object belongs.  If a group is specified, then this object will only
34600      * interact with other drag drop objects in the same group (defaults to undefined).
34601      */
34602     /**
34603      * @cfg {String} dropAllowed
34604      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
34605      */
34606     dropAllowed : "x-dd-drop-ok",
34607     /**
34608      * @cfg {String} dropNotAllowed
34609      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
34610      */
34611     dropNotAllowed : "x-dd-drop-nodrop",
34612
34613     /**
34614      * Returns the data object associated with this drag source
34615      * @return {Object} data An object containing arbitrary data
34616      */
34617     getDragData : function(e){
34618         return this.dragData;
34619     },
34620
34621     // private
34622     onDragEnter : function(e, id){
34623         var target = Ext.dd.DragDropMgr.getDDById(id);
34624         this.cachedTarget = target;
34625         if(this.beforeDragEnter(target, e, id) !== false){
34626             if(target.isNotifyTarget){
34627                 var status = target.notifyEnter(this, e, this.dragData);
34628                 this.proxy.setStatus(status);
34629             }else{
34630                 this.proxy.setStatus(this.dropAllowed);
34631             }
34632             
34633             if(this.afterDragEnter){
34634                 /**
34635                  * An empty function by default, but provided so that you can perform a custom action
34636                  * when the dragged item enters the drop target by providing an implementation.
34637                  * @param {Ext.dd.DragDrop} target The drop target
34638                  * @param {Event} e The event object
34639                  * @param {String} id The id of the dragged element
34640                  * @method afterDragEnter
34641                  */
34642                 this.afterDragEnter(target, e, id);
34643             }
34644         }
34645     },
34646
34647     /**
34648      * An empty function by default, but provided so that you can perform a custom action
34649      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
34650      * @param {Ext.dd.DragDrop} target The drop target
34651      * @param {Event} e The event object
34652      * @param {String} id The id of the dragged element
34653      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
34654      */
34655     beforeDragEnter : function(target, e, id){
34656         return true;
34657     },
34658
34659     // private
34660     alignElWithMouse: function() {
34661         Ext.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
34662         this.proxy.sync();
34663     },
34664
34665     // private
34666     onDragOver : function(e, id){
34667         var target = this.cachedTarget || Ext.dd.DragDropMgr.getDDById(id);
34668         if(this.beforeDragOver(target, e, id) !== false){
34669             if(target.isNotifyTarget){
34670                 var status = target.notifyOver(this, e, this.dragData);
34671                 this.proxy.setStatus(status);
34672             }
34673
34674             if(this.afterDragOver){
34675                 /**
34676                  * An empty function by default, but provided so that you can perform a custom action
34677                  * while the dragged item is over the drop target by providing an implementation.
34678                  * @param {Ext.dd.DragDrop} target The drop target
34679                  * @param {Event} e The event object
34680                  * @param {String} id The id of the dragged element
34681                  * @method afterDragOver
34682                  */
34683                 this.afterDragOver(target, e, id);
34684             }
34685         }
34686     },
34687
34688     /**
34689      * An empty function by default, but provided so that you can perform a custom action
34690      * while the dragged item is over the drop target and optionally cancel the onDragOver.
34691      * @param {Ext.dd.DragDrop} target The drop target
34692      * @param {Event} e The event object
34693      * @param {String} id The id of the dragged element
34694      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
34695      */
34696     beforeDragOver : function(target, e, id){
34697         return true;
34698     },
34699
34700     // private
34701     onDragOut : function(e, id){
34702         var target = this.cachedTarget || Ext.dd.DragDropMgr.getDDById(id);
34703         if(this.beforeDragOut(target, e, id) !== false){
34704             if(target.isNotifyTarget){
34705                 target.notifyOut(this, e, this.dragData);
34706             }
34707             this.proxy.reset();
34708             if(this.afterDragOut){
34709                 /**
34710                  * An empty function by default, but provided so that you can perform a custom action
34711                  * after the dragged item is dragged out of the target without dropping.
34712                  * @param {Ext.dd.DragDrop} target The drop target
34713                  * @param {Event} e The event object
34714                  * @param {String} id The id of the dragged element
34715                  * @method afterDragOut
34716                  */
34717                 this.afterDragOut(target, e, id);
34718             }
34719         }
34720         this.cachedTarget = null;
34721     },
34722
34723     /**
34724      * An empty function by default, but provided so that you can perform a custom action before the dragged
34725      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
34726      * @param {Ext.dd.DragDrop} target The drop target
34727      * @param {Event} e The event object
34728      * @param {String} id The id of the dragged element
34729      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
34730      */
34731     beforeDragOut : function(target, e, id){
34732         return true;
34733     },
34734     
34735     // private
34736     onDragDrop : function(e, id){
34737         var target = this.cachedTarget || Ext.dd.DragDropMgr.getDDById(id);
34738         if(this.beforeDragDrop(target, e, id) !== false){
34739             if(target.isNotifyTarget){
34740                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
34741                     this.onValidDrop(target, e, id);
34742                 }else{
34743                     this.onInvalidDrop(target, e, id);
34744                 }
34745             }else{
34746                 this.onValidDrop(target, e, id);
34747             }
34748             
34749             if(this.afterDragDrop){
34750                 /**
34751                  * An empty function by default, but provided so that you can perform a custom action
34752                  * after a valid drag drop has occurred by providing an implementation.
34753                  * @param {Ext.dd.DragDrop} target The drop target
34754                  * @param {Event} e The event object
34755                  * @param {String} id The id of the dropped element
34756                  * @method afterDragDrop
34757                  */
34758                 this.afterDragDrop(target, e, id);
34759             }
34760         }
34761         delete this.cachedTarget;
34762     },
34763
34764     /**
34765      * An empty function by default, but provided so that you can perform a custom action before the dragged
34766      * item is dropped onto the target and optionally cancel the onDragDrop.
34767      * @param {Ext.dd.DragDrop} target The drop target
34768      * @param {Event} e The event object
34769      * @param {String} id The id of the dragged element
34770      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
34771      */
34772     beforeDragDrop : function(target, e, id){
34773         return true;
34774     },
34775
34776     // private
34777     onValidDrop : function(target, e, id){
34778         this.hideProxy();
34779         if(this.afterValidDrop){
34780             /**
34781              * An empty function by default, but provided so that you can perform a custom action
34782              * after a valid drop has occurred by providing an implementation.
34783              * @param {Object} target The target DD 
34784              * @param {Event} e The event object
34785              * @param {String} id The id of the dropped element
34786              * @method afterInvalidDrop
34787              */
34788             this.afterValidDrop(target, e, id);
34789         }
34790     },
34791
34792     // private
34793     getRepairXY : function(e, data){
34794         return this.el.getXY();  
34795     },
34796
34797     // private
34798     onInvalidDrop : function(target, e, id){
34799         this.beforeInvalidDrop(target, e, id);
34800         if(this.cachedTarget){
34801             if(this.cachedTarget.isNotifyTarget){
34802                 this.cachedTarget.notifyOut(this, e, this.dragData);
34803             }
34804             this.cacheTarget = null;
34805         }
34806         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
34807
34808         if(this.afterInvalidDrop){
34809             /**
34810              * An empty function by default, but provided so that you can perform a custom action
34811              * after an invalid drop has occurred by providing an implementation.
34812              * @param {Event} e The event object
34813              * @param {String} id The id of the dropped element
34814              * @method afterInvalidDrop
34815              */
34816             this.afterInvalidDrop(e, id);
34817         }
34818     },
34819
34820     // private
34821     afterRepair : function(){
34822         if(Ext.enableFx){
34823             this.el.highlight(this.hlColor || "c3daf9");
34824         }
34825         this.dragging = false;
34826     },
34827
34828     /**
34829      * An empty function by default, but provided so that you can perform a custom action after an invalid
34830      * drop has occurred.
34831      * @param {Ext.dd.DragDrop} target The drop target
34832      * @param {Event} e The event object
34833      * @param {String} id The id of the dragged element
34834      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
34835      */
34836     beforeInvalidDrop : function(target, e, id){
34837         return true;
34838     },
34839
34840     // private
34841     handleMouseDown : function(e){
34842         if(this.dragging) {
34843             return;
34844         }
34845         var data = this.getDragData(e);
34846         if(data && this.onBeforeDrag(data, e) !== false){
34847             this.dragData = data;
34848             this.proxy.stop();
34849             Ext.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
34850         } 
34851     },
34852
34853     /**
34854      * An empty function by default, but provided so that you can perform a custom action before the initial
34855      * drag event begins and optionally cancel it.
34856      * @param {Object} data An object containing arbitrary data to be shared with drop targets
34857      * @param {Event} e The event object
34858      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
34859      */
34860     onBeforeDrag : function(data, e){
34861         return true;
34862     },
34863
34864     /**
34865      * An empty function by default, but provided so that you can perform a custom action once the initial
34866      * drag event has begun.  The drag cannot be canceled from this function.
34867      * @param {Number} x The x position of the click on the dragged object
34868      * @param {Number} y The y position of the click on the dragged object
34869      */
34870     onStartDrag : Ext.emptyFn,
34871
34872     // private override
34873     startDrag : function(x, y){
34874         this.proxy.reset();
34875         this.dragging = true;
34876         this.proxy.update("");
34877         this.onInitDrag(x, y);
34878         this.proxy.show();
34879     },
34880
34881     // private
34882     onInitDrag : function(x, y){
34883         var clone = this.el.dom.cloneNode(true);
34884         clone.id = Ext.id(); // prevent duplicate ids
34885         this.proxy.update(clone);
34886         this.onStartDrag(x, y);
34887         return true;
34888     },
34889
34890     /**
34891      * Returns the drag source's underlying {@link Ext.dd.StatusProxy}
34892      * @return {Ext.dd.StatusProxy} proxy The StatusProxy
34893      */
34894     getProxy : function(){
34895         return this.proxy;  
34896     },
34897
34898     /**
34899      * Hides the drag source's {@link Ext.dd.StatusProxy}
34900      */
34901     hideProxy : function(){
34902         this.proxy.hide();  
34903         this.proxy.reset(true);
34904         this.dragging = false;
34905     },
34906
34907     // private
34908     triggerCacheRefresh : function(){
34909         Ext.dd.DDM.refreshCache(this.groups);
34910     },
34911
34912     // private - override to prevent hiding
34913     b4EndDrag: function(e) {
34914     },
34915
34916     // private - override to prevent moving
34917     endDrag : function(e){
34918         this.onEndDrag(this.dragData, e);
34919     },
34920
34921     // private
34922     onEndDrag : function(data, e){
34923     },
34924     
34925     // private - pin to cursor
34926     autoOffset : function(x, y) {
34927         this.setDelta(-12, -20);
34928     },
34929     
34930     destroy: function(){
34931         Ext.dd.DragSource.superclass.destroy.call(this);
34932         Ext.destroy(this.proxy);
34933     }
34934 });/**
34935  * @class Ext.dd.DropTarget
34936  * @extends Ext.dd.DDTarget
34937  * A simple class that provides the basic implementation needed to make any element a drop target that can have
34938  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
34939  * @constructor
34940  * @param {Mixed} el The container element
34941  * @param {Object} config
34942  */
34943 Ext.dd.DropTarget = Ext.extend(Ext.dd.DDTarget, {
34944     
34945     constructor : function(el, config){
34946         this.el = Ext.get(el);
34947     
34948         Ext.apply(this, config);
34949     
34950         if(this.containerScroll){
34951             Ext.dd.ScrollManager.register(this.el);
34952         }
34953     
34954         Ext.dd.DropTarget.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group, 
34955               {isTarget: true});        
34956     },
34957     
34958     /**
34959      * @cfg {String} ddGroup
34960      * A named drag drop group to which this object belongs.  If a group is specified, then this object will only
34961      * interact with other drag drop objects in the same group (defaults to undefined).
34962      */
34963     /**
34964      * @cfg {String} overClass
34965      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
34966      */
34967     /**
34968      * @cfg {String} dropAllowed
34969      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
34970      */
34971     dropAllowed : "x-dd-drop-ok",
34972     /**
34973      * @cfg {String} dropNotAllowed
34974      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
34975      */
34976     dropNotAllowed : "x-dd-drop-nodrop",
34977
34978     // private
34979     isTarget : true,
34980
34981     // private
34982     isNotifyTarget : true,
34983
34984     /**
34985      * The function a {@link Ext.dd.DragSource} calls once to notify this drop target that the source is now over the
34986      * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
34987      * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
34988      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop target
34989      * @param {Event} e The event
34990      * @param {Object} data An object containing arbitrary data supplied by the drag source
34991      * @return {String} status The CSS class that communicates the drop status back to the source so that the
34992      * underlying {@link Ext.dd.StatusProxy} can be updated
34993      */
34994     notifyEnter : function(dd, e, data){
34995         if(this.overClass){
34996             this.el.addClass(this.overClass);
34997         }
34998         return this.dropAllowed;
34999     },
35000
35001     /**
35002      * The function a {@link Ext.dd.DragSource} calls continuously while it is being dragged over the target.
35003      * This method will be called on every mouse movement while the drag source is over the drop target.
35004      * This default implementation simply returns the dropAllowed config value.
35005      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop target
35006      * @param {Event} e The event
35007      * @param {Object} data An object containing arbitrary data supplied by the drag source
35008      * @return {String} status The CSS class that communicates the drop status back to the source so that the
35009      * underlying {@link Ext.dd.StatusProxy} can be updated
35010      */
35011     notifyOver : function(dd, e, data){
35012         return this.dropAllowed;
35013     },
35014
35015     /**
35016      * The function a {@link Ext.dd.DragSource} calls once to notify this drop target that the source has been dragged
35017      * out of the target without dropping.  This default implementation simply removes the CSS class specified by
35018      * overClass (if any) from the drop element.
35019      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop target
35020      * @param {Event} e The event
35021      * @param {Object} data An object containing arbitrary data supplied by the drag source
35022      */
35023     notifyOut : function(dd, e, data){
35024         if(this.overClass){
35025             this.el.removeClass(this.overClass);
35026         }
35027     },
35028
35029     /**
35030      * The function a {@link Ext.dd.DragSource} calls once to notify this drop target that the dragged item has
35031      * been dropped on it.  This method has no default implementation and returns false, so you must provide an
35032      * implementation that does something to process the drop event and returns true so that the drag source's
35033      * repair action does not run.
35034      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop target
35035      * @param {Event} e The event
35036      * @param {Object} data An object containing arbitrary data supplied by the drag source
35037      * @return {Boolean} True if the drop was valid, else false
35038      */
35039     notifyDrop : function(dd, e, data){
35040         return false;
35041     },
35042     
35043     destroy : function(){
35044         Ext.dd.DropTarget.superclass.destroy.call(this);
35045         if(this.containerScroll){
35046             Ext.dd.ScrollManager.unregister(this.el);
35047         }
35048     }
35049 });/**
35050  * @class Ext.dd.DragZone
35051  * @extends Ext.dd.DragSource
35052  * <p>This class provides a container DD instance that allows dragging of multiple child source nodes.</p>
35053  * <p>This class does not move the drag target nodes, but a proxy element which may contain
35054  * any DOM structure you wish. The DOM element to show in the proxy is provided by either a
35055  * provided implementation of {@link #getDragData}, or by registered draggables registered with {@link Ext.dd.Registry}</p>
35056  * <p>If you wish to provide draggability for an arbitrary number of DOM nodes, each of which represent some
35057  * application object (For example nodes in a {@link Ext.DataView DataView}) then use of this class
35058  * is the most efficient way to "activate" those nodes.</p>
35059  * <p>By default, this class requires that draggable child nodes are registered with {@link Ext.dd.Registry}.
35060  * However a simpler way to allow a DragZone to manage any number of draggable elements is to configure
35061  * the DragZone with  an implementation of the {@link #getDragData} method which interrogates the passed
35062  * mouse event to see if it has taken place within an element, or class of elements. This is easily done
35063  * by using the event's {@link Ext.EventObject#getTarget getTarget} method to identify a node based on a
35064  * {@link Ext.DomQuery} selector. For example, to make the nodes of a DataView draggable, use the following
35065  * technique. Knowledge of the use of the DataView is required:</p><pre><code>
35066 myDataView.on('render', function(v) {
35067     myDataView.dragZone = new Ext.dd.DragZone(v.getEl(), {
35068
35069 //      On receipt of a mousedown event, see if it is within a DataView node.
35070 //      Return a drag data object if so.
35071         getDragData: function(e) {
35072
35073 //          Use the DataView's own itemSelector (a mandatory property) to
35074 //          test if the mousedown is within one of the DataView's nodes.
35075             var sourceEl = e.getTarget(v.itemSelector, 10);
35076
35077 //          If the mousedown is within a DataView node, clone the node to produce
35078 //          a ddel element for use by the drag proxy. Also add application data
35079 //          to the returned data object.
35080             if (sourceEl) {
35081                 d = sourceEl.cloneNode(true);
35082                 d.id = Ext.id();
35083                 return {
35084                     ddel: d,
35085                     sourceEl: sourceEl,
35086                     repairXY: Ext.fly(sourceEl).getXY(),
35087                     sourceStore: v.store,
35088                     draggedRecord: v.{@link Ext.DataView#getRecord getRecord}(sourceEl)
35089                 }
35090             }
35091         },
35092
35093 //      Provide coordinates for the proxy to slide back to on failed drag.
35094 //      This is the original XY coordinates of the draggable element captured
35095 //      in the getDragData method.
35096         getRepairXY: function() {
35097             return this.dragData.repairXY;
35098         }
35099     });
35100 });</code></pre>
35101  * See the {@link Ext.dd.DropZone DropZone} documentation for details about building a DropZone which
35102  * cooperates with this DragZone.
35103  * @constructor
35104  * @param {Mixed} el The container element
35105  * @param {Object} config
35106  */
35107 Ext.dd.DragZone = Ext.extend(Ext.dd.DragSource, {
35108     
35109     constructor : function(el, config){
35110         Ext.dd.DragZone.superclass.constructor.call(this, el, config);
35111         if(this.containerScroll){
35112             Ext.dd.ScrollManager.register(this.el);
35113         }
35114     },
35115     
35116     /**
35117      * This property contains the data representing the dragged object. This data is set up by the implementation
35118      * of the {@link #getDragData} method. It must contain a <tt>ddel</tt> property, but can contain
35119      * any other data according to the application's needs.
35120      * @type Object
35121      * @property dragData
35122      */
35123     /**
35124      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
35125      * for auto scrolling during drag operations.
35126      */
35127     /**
35128      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
35129      * method after a failed drop (defaults to "c3daf9" - light blue)
35130      */
35131
35132     /**
35133      * Called when a mousedown occurs in this container. Looks in {@link Ext.dd.Registry}
35134      * for a valid target to drag based on the mouse down. Override this method
35135      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
35136      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
35137      * @param {EventObject} e The mouse down event
35138      * @return {Object} The dragData
35139      */
35140     getDragData : function(e){
35141         return Ext.dd.Registry.getHandleFromEvent(e);
35142     },
35143     
35144     /**
35145      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
35146      * this.dragData.ddel
35147      * @param {Number} x The x position of the click on the dragged object
35148      * @param {Number} y The y position of the click on the dragged object
35149      * @return {Boolean} true to continue the drag, false to cancel
35150      */
35151     onInitDrag : function(x, y){
35152         this.proxy.update(this.dragData.ddel.cloneNode(true));
35153         this.onStartDrag(x, y);
35154         return true;
35155     },
35156     
35157     /**
35158      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
35159      */
35160     afterRepair : function(){
35161         if(Ext.enableFx){
35162             Ext.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
35163         }
35164         this.dragging = false;
35165     },
35166
35167     /**
35168      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
35169      * the XY of this.dragData.ddel
35170      * @param {EventObject} e The mouse up event
35171      * @return {Array} The xy location (e.g. [100, 200])
35172      */
35173     getRepairXY : function(e){
35174         return Ext.Element.fly(this.dragData.ddel).getXY();  
35175     },
35176     
35177     destroy : function(){
35178         Ext.dd.DragZone.superclass.destroy.call(this);
35179         if(this.containerScroll){
35180             Ext.dd.ScrollManager.unregister(this.el);
35181         }
35182     }
35183 });/**
35184  * @class Ext.dd.DropZone
35185  * @extends Ext.dd.DropTarget
35186  * <p>This class provides a container DD instance that allows dropping on multiple child target nodes.</p>
35187  * <p>By default, this class requires that child nodes accepting drop are registered with {@link Ext.dd.Registry}.
35188  * However a simpler way to allow a DropZone to manage any number of target elements is to configure the
35189  * DropZone with an implementation of {@link #getTargetFromEvent} which interrogates the passed
35190  * mouse event to see if it has taken place within an element, or class of elements. This is easily done
35191  * by using the event's {@link Ext.EventObject#getTarget getTarget} method to identify a node based on a
35192  * {@link Ext.DomQuery} selector.</p>
35193  * <p>Once the DropZone has detected through calling getTargetFromEvent, that the mouse is over
35194  * a drop target, that target is passed as the first parameter to {@link #onNodeEnter}, {@link #onNodeOver},
35195  * {@link #onNodeOut}, {@link #onNodeDrop}. You may configure the instance of DropZone with implementations
35196  * of these methods to provide application-specific behaviour for these events to update both
35197  * application state, and UI state.</p>
35198  * <p>For example to make a GridPanel a cooperating target with the example illustrated in
35199  * {@link Ext.dd.DragZone DragZone}, the following technique might be used:</p><pre><code>
35200 myGridPanel.on('render', function() {
35201     myGridPanel.dropZone = new Ext.dd.DropZone(myGridPanel.getView().scroller, {
35202
35203 //      If the mouse is over a grid row, return that node. This is
35204 //      provided as the "target" parameter in all "onNodeXXXX" node event handling functions
35205         getTargetFromEvent: function(e) {
35206             return e.getTarget(myGridPanel.getView().rowSelector);
35207         },
35208
35209 //      On entry into a target node, highlight that node.
35210         onNodeEnter : function(target, dd, e, data){ 
35211             Ext.fly(target).addClass('my-row-highlight-class');
35212         },
35213
35214 //      On exit from a target node, unhighlight that node.
35215         onNodeOut : function(target, dd, e, data){ 
35216             Ext.fly(target).removeClass('my-row-highlight-class');
35217         },
35218
35219 //      While over a target node, return the default drop allowed class which
35220 //      places a "tick" icon into the drag proxy.
35221         onNodeOver : function(target, dd, e, data){ 
35222             return Ext.dd.DropZone.prototype.dropAllowed;
35223         },
35224
35225 //      On node drop we can interrogate the target to find the underlying
35226 //      application object that is the real target of the dragged data.
35227 //      In this case, it is a Record in the GridPanel's Store.
35228 //      We can use the data set up by the DragZone's getDragData method to read
35229 //      any data we decided to attach in the DragZone's getDragData method.
35230         onNodeDrop : function(target, dd, e, data){
35231             var rowIndex = myGridPanel.getView().findRowIndex(target);
35232             var r = myGridPanel.getStore().getAt(rowIndex);
35233             Ext.Msg.alert('Drop gesture', 'Dropped Record id ' + data.draggedRecord.id +
35234                 ' on Record id ' + r.id);
35235             return true;
35236         }
35237     });
35238 }
35239 </code></pre>
35240  * See the {@link Ext.dd.DragZone DragZone} documentation for details about building a DragZone which
35241  * cooperates with this DropZone.
35242  * @constructor
35243  * @param {Mixed} el The container element
35244  * @param {Object} config
35245  */
35246 Ext.dd.DropZone = function(el, config){
35247     Ext.dd.DropZone.superclass.constructor.call(this, el, config);
35248 };
35249
35250 Ext.extend(Ext.dd.DropZone, Ext.dd.DropTarget, {
35251     /**
35252      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
35253      * this looks up the event target in the {@link Ext.dd.Registry}, although you can override this method to
35254      * provide your own custom lookup.
35255      * @param {Event} e The event
35256      * @return {Object} data The custom data
35257      */
35258     getTargetFromEvent : function(e){
35259         return Ext.dd.Registry.getTargetFromEvent(e);
35260     },
35261
35262     /**
35263      * Called when the DropZone determines that a {@link Ext.dd.DragSource} has entered a drop node
35264      * that has either been registered or detected by a configured implementation of {@link #getTargetFromEvent}.
35265      * This method has no default implementation and should be overridden to provide
35266      * node-specific processing if necessary.
35267      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
35268      * {@link #getTargetFromEvent} for this node)
35269      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone
35270      * @param {Event} e The event
35271      * @param {Object} data An object containing arbitrary data supplied by the drag source
35272      */
35273     onNodeEnter : function(n, dd, e, data){
35274         
35275     },
35276
35277     /**
35278      * Called while the DropZone determines that a {@link Ext.dd.DragSource} is over a drop node
35279      * that has either been registered or detected by a configured implementation of {@link #getTargetFromEvent}.
35280      * The default implementation returns this.dropNotAllowed, so it should be
35281      * overridden to provide the proper feedback.
35282      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
35283      * {@link #getTargetFromEvent} for this node)
35284      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone
35285      * @param {Event} e The event
35286      * @param {Object} data An object containing arbitrary data supplied by the drag source
35287      * @return {String} status The CSS class that communicates the drop status back to the source so that the
35288      * underlying {@link Ext.dd.StatusProxy} can be updated
35289      */
35290     onNodeOver : function(n, dd, e, data){
35291         return this.dropAllowed;
35292     },
35293
35294     /**
35295      * Called when the DropZone determines that a {@link Ext.dd.DragSource} has been dragged out of
35296      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
35297      * node-specific processing if necessary.
35298      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
35299      * {@link #getTargetFromEvent} for this node)
35300      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone
35301      * @param {Event} e The event
35302      * @param {Object} data An object containing arbitrary data supplied by the drag source
35303      */
35304     onNodeOut : function(n, dd, e, data){
35305         
35306     },
35307
35308     /**
35309      * Called when the DropZone determines that a {@link Ext.dd.DragSource} has been dropped onto
35310      * the drop node.  The default implementation returns false, so it should be overridden to provide the
35311      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
35312      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
35313      * {@link #getTargetFromEvent} for this node)
35314      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone
35315      * @param {Event} e The event
35316      * @param {Object} data An object containing arbitrary data supplied by the drag source
35317      * @return {Boolean} True if the drop was valid, else false
35318      */
35319     onNodeDrop : function(n, dd, e, data){
35320         return false;
35321     },
35322
35323     /**
35324      * Called while the DropZone determines that a {@link Ext.dd.DragSource} is being dragged over it,
35325      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
35326      * it should be overridden to provide the proper feedback if necessary.
35327      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone
35328      * @param {Event} e The event
35329      * @param {Object} data An object containing arbitrary data supplied by the drag source
35330      * @return {String} status The CSS class that communicates the drop status back to the source so that the
35331      * underlying {@link Ext.dd.StatusProxy} can be updated
35332      */
35333     onContainerOver : function(dd, e, data){
35334         return this.dropNotAllowed;
35335     },
35336
35337     /**
35338      * Called when the DropZone determines that a {@link Ext.dd.DragSource} has been dropped on it,
35339      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
35340      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
35341      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
35342      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone
35343      * @param {Event} e The event
35344      * @param {Object} data An object containing arbitrary data supplied by the drag source
35345      * @return {Boolean} True if the drop was valid, else false
35346      */
35347     onContainerDrop : function(dd, e, data){
35348         return false;
35349     },
35350
35351     /**
35352      * The function a {@link Ext.dd.DragSource} calls once to notify this drop zone that the source is now over
35353      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
35354      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
35355      * you should override this method and provide a custom implementation.
35356      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone
35357      * @param {Event} e The event
35358      * @param {Object} data An object containing arbitrary data supplied by the drag source
35359      * @return {String} status The CSS class that communicates the drop status back to the source so that the
35360      * underlying {@link Ext.dd.StatusProxy} can be updated
35361      */
35362     notifyEnter : function(dd, e, data){
35363         return this.dropNotAllowed;
35364     },
35365
35366     /**
35367      * The function a {@link Ext.dd.DragSource} calls continuously while it is being dragged over the drop zone.
35368      * This method will be called on every mouse movement while the drag source is over the drop zone.
35369      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
35370      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
35371      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
35372      * registered node, it will call {@link #onContainerOver}.
35373      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone
35374      * @param {Event} e The event
35375      * @param {Object} data An object containing arbitrary data supplied by the drag source
35376      * @return {String} status The CSS class that communicates the drop status back to the source so that the
35377      * underlying {@link Ext.dd.StatusProxy} can be updated
35378      */
35379     notifyOver : function(dd, e, data){
35380         var n = this.getTargetFromEvent(e);
35381         if(!n){ // not over valid drop target
35382             if(this.lastOverNode){
35383                 this.onNodeOut(this.lastOverNode, dd, e, data);
35384                 this.lastOverNode = null;
35385             }
35386             return this.onContainerOver(dd, e, data);
35387         }
35388         if(this.lastOverNode != n){
35389             if(this.lastOverNode){
35390                 this.onNodeOut(this.lastOverNode, dd, e, data);
35391             }
35392             this.onNodeEnter(n, dd, e, data);
35393             this.lastOverNode = n;
35394         }
35395         return this.onNodeOver(n, dd, e, data);
35396     },
35397
35398     /**
35399      * The function a {@link Ext.dd.DragSource} calls once to notify this drop zone that the source has been dragged
35400      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
35401      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
35402      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop target
35403      * @param {Event} e The event
35404      * @param {Object} data An object containing arbitrary data supplied by the drag zone
35405      */
35406     notifyOut : function(dd, e, data){
35407         if(this.lastOverNode){
35408             this.onNodeOut(this.lastOverNode, dd, e, data);
35409             this.lastOverNode = null;
35410         }
35411     },
35412
35413     /**
35414      * The function a {@link Ext.dd.DragSource} calls once to notify this drop zone that the dragged item has
35415      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
35416      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
35417      * otherwise it will call {@link #onContainerDrop}.
35418      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone
35419      * @param {Event} e The event
35420      * @param {Object} data An object containing arbitrary data supplied by the drag source
35421      * @return {Boolean} True if the drop was valid, else false
35422      */
35423     notifyDrop : function(dd, e, data){
35424         if(this.lastOverNode){
35425             this.onNodeOut(this.lastOverNode, dd, e, data);
35426             this.lastOverNode = null;
35427         }
35428         var n = this.getTargetFromEvent(e);
35429         return n ?
35430             this.onNodeDrop(n, dd, e, data) :
35431             this.onContainerDrop(dd, e, data);
35432     },
35433
35434     // private
35435     triggerCacheRefresh : function(){
35436         Ext.dd.DDM.refreshCache(this.groups);
35437     }  
35438 });/**
35439  * @class Ext.Element
35440  */
35441 Ext.Element.addMethods({
35442     /**
35443      * Initializes a {@link Ext.dd.DD} drag drop object for this element.
35444      * @param {String} group The group the DD object is member of
35445      * @param {Object} config The DD config object
35446      * @param {Object} overrides An object containing methods to override/implement on the DD object
35447      * @return {Ext.dd.DD} The DD object
35448      */
35449     initDD : function(group, config, overrides){
35450         var dd = new Ext.dd.DD(Ext.id(this.dom), group, config);
35451         return Ext.apply(dd, overrides);
35452     },
35453
35454     /**
35455      * Initializes a {@link Ext.dd.DDProxy} object for this element.
35456      * @param {String} group The group the DDProxy object is member of
35457      * @param {Object} config The DDProxy config object
35458      * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
35459      * @return {Ext.dd.DDProxy} The DDProxy object
35460      */
35461     initDDProxy : function(group, config, overrides){
35462         var dd = new Ext.dd.DDProxy(Ext.id(this.dom), group, config);
35463         return Ext.apply(dd, overrides);
35464     },
35465
35466     /**
35467      * Initializes a {@link Ext.dd.DDTarget} object for this element.
35468      * @param {String} group The group the DDTarget object is member of
35469      * @param {Object} config The DDTarget config object
35470      * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
35471      * @return {Ext.dd.DDTarget} The DDTarget object
35472      */
35473     initDDTarget : function(group, config, overrides){
35474         var dd = new Ext.dd.DDTarget(Ext.id(this.dom), group, config);
35475         return Ext.apply(dd, overrides);
35476     }
35477 });
35478 /**
35479  * @class Ext.data.Api
35480  * @extends Object
35481  * Ext.data.Api is a singleton designed to manage the data API including methods
35482  * for validating a developer's DataProxy API.  Defines variables for CRUD actions
35483  * create, read, update and destroy in addition to a mapping of RESTful HTTP methods
35484  * GET, POST, PUT and DELETE to CRUD actions.
35485  * @singleton
35486  */
35487 Ext.data.Api = (function() {
35488
35489     // private validActions.  validActions is essentially an inverted hash of Ext.data.Api.actions, where value becomes the key.
35490     // Some methods in this singleton (e.g.: getActions, getVerb) will loop through actions with the code <code>for (var verb in this.actions)</code>
35491     // For efficiency, some methods will first check this hash for a match.  Those methods which do acces validActions will cache their result here.
35492     // We cannot pre-define this hash since the developer may over-ride the actions at runtime.
35493     var validActions = {};
35494
35495     return {
35496         /**
35497          * Defined actions corresponding to remote actions:
35498          * <pre><code>
35499 actions: {
35500     create  : 'create',  // Text representing the remote-action to create records on server.
35501     read    : 'read',    // Text representing the remote-action to read/load data from server.
35502     update  : 'update',  // Text representing the remote-action to update records on server.
35503     destroy : 'destroy'  // Text representing the remote-action to destroy records on server.
35504 }
35505          * </code></pre>
35506          * @property actions
35507          * @type Object
35508          */
35509         actions : {
35510             create  : 'create',
35511             read    : 'read',
35512             update  : 'update',
35513             destroy : 'destroy'
35514         },
35515
35516         /**
35517          * Defined {CRUD action}:{HTTP method} pairs to associate HTTP methods with the
35518          * corresponding actions for {@link Ext.data.DataProxy#restful RESTful proxies}.
35519          * Defaults to:
35520          * <pre><code>
35521 restActions : {
35522     create  : 'POST',
35523     read    : 'GET',
35524     update  : 'PUT',
35525     destroy : 'DELETE'
35526 },
35527          * </code></pre>
35528          */
35529         restActions : {
35530             create  : 'POST',
35531             read    : 'GET',
35532             update  : 'PUT',
35533             destroy : 'DELETE'
35534         },
35535
35536         /**
35537          * Returns true if supplied action-name is a valid API action defined in <code>{@link #actions}</code> constants
35538          * @param {String} action Action to test for availability.
35539          * @return {Boolean}
35540          */
35541         isAction : function(action) {
35542             return (Ext.data.Api.actions[action]) ? true : false;
35543         },
35544
35545         /**
35546          * Returns the actual CRUD action KEY "create", "read", "update" or "destroy" from the supplied action-name.  This method is used internally and shouldn't generally
35547          * need to be used directly.  The key/value pair of Ext.data.Api.actions will often be identical but this is not necessarily true.  A developer can override this naming
35548          * convention if desired.  However, the framework internally calls methods based upon the KEY so a way of retreiving the the words "create", "read", "update" and "destroy" is
35549          * required.  This method will cache discovered KEYS into the private validActions hash.
35550          * @param {String} name The runtime name of the action.
35551          * @return {String||null} returns the action-key, or verb of the user-action or null if invalid.
35552          * @nodoc
35553          */
35554         getVerb : function(name) {
35555             if (validActions[name]) {
35556                 return validActions[name];  // <-- found in cache.  return immediately.
35557             }
35558             for (var verb in this.actions) {
35559                 if (this.actions[verb] === name) {
35560                     validActions[name] = verb;
35561                     break;
35562                 }
35563             }
35564             return (validActions[name] !== undefined) ? validActions[name] : null;
35565         },
35566
35567         /**
35568          * Returns true if the supplied API is valid; that is, check that all keys match defined actions
35569          * otherwise returns an array of mistakes.
35570          * @return {String[]|true}
35571          */
35572         isValid : function(api){
35573             var invalid = [];
35574             var crud = this.actions; // <-- cache a copy of the actions.
35575             for (var action in api) {
35576                 if (!(action in crud)) {
35577                     invalid.push(action);
35578                 }
35579             }
35580             return (!invalid.length) ? true : invalid;
35581         },
35582
35583         /**
35584          * Returns true if the supplied verb upon the supplied proxy points to a unique url in that none of the other api-actions
35585          * point to the same url.  The question is important for deciding whether to insert the "xaction" HTTP parameter within an
35586          * Ajax request.  This method is used internally and shouldn't generally need to be called directly.
35587          * @param {Ext.data.DataProxy} proxy
35588          * @param {String} verb
35589          * @return {Boolean}
35590          */
35591         hasUniqueUrl : function(proxy, verb) {
35592             var url = (proxy.api[verb]) ? proxy.api[verb].url : null;
35593             var unique = true;
35594             for (var action in proxy.api) {
35595                 if ((unique = (action === verb) ? true : (proxy.api[action].url != url) ? true : false) === false) {
35596                     break;
35597                 }
35598             }
35599             return unique;
35600         },
35601
35602         /**
35603          * This method is used internally by <tt>{@link Ext.data.DataProxy DataProxy}</tt> and should not generally need to be used directly.
35604          * Each action of a DataProxy api can be initially defined as either a String or an Object.  When specified as an object,
35605          * one can explicitly define the HTTP method (GET|POST) to use for each CRUD action.  This method will prepare the supplied API, setting
35606          * each action to the Object form.  If your API-actions do not explicitly define the HTTP method, the "method" configuration-parameter will
35607          * be used.  If the method configuration parameter is not specified, POST will be used.
35608          <pre><code>
35609 new Ext.data.HttpProxy({
35610     method: "POST",     // <-- default HTTP method when not specified.
35611     api: {
35612         create: 'create.php',
35613         load: 'read.php',
35614         save: 'save.php',
35615         destroy: 'destroy.php'
35616     }
35617 });
35618
35619 // Alternatively, one can use the object-form to specify the API
35620 new Ext.data.HttpProxy({
35621     api: {
35622         load: {url: 'read.php', method: 'GET'},
35623         create: 'create.php',
35624         destroy: 'destroy.php',
35625         save: 'update.php'
35626     }
35627 });
35628         </code></pre>
35629          *
35630          * @param {Ext.data.DataProxy} proxy
35631          */
35632         prepare : function(proxy) {
35633             if (!proxy.api) {
35634                 proxy.api = {}; // <-- No api?  create a blank one.
35635             }
35636             for (var verb in this.actions) {
35637                 var action = this.actions[verb];
35638                 proxy.api[action] = proxy.api[action] || proxy.url || proxy.directFn;
35639                 if (typeof(proxy.api[action]) == 'string') {
35640                     proxy.api[action] = {
35641                         url: proxy.api[action],
35642                         method: (proxy.restful === true) ? Ext.data.Api.restActions[action] : undefined
35643                     };
35644                 }
35645             }
35646         },
35647
35648         /**
35649          * Prepares a supplied Proxy to be RESTful.  Sets the HTTP method for each api-action to be one of
35650          * GET, POST, PUT, DELETE according to the defined {@link #restActions}.
35651          * @param {Ext.data.DataProxy} proxy
35652          */
35653         restify : function(proxy) {
35654             proxy.restful = true;
35655             for (var verb in this.restActions) {
35656                 proxy.api[this.actions[verb]].method ||
35657                     (proxy.api[this.actions[verb]].method = this.restActions[verb]);
35658             }
35659             // TODO: perhaps move this interceptor elsewhere?  like into DataProxy, perhaps?  Placed here
35660             // to satisfy initial 3.0 final release of REST features.
35661             proxy.onWrite = proxy.onWrite.createInterceptor(function(action, o, response, rs) {
35662                 var reader = o.reader;
35663                 var res = new Ext.data.Response({
35664                     action: action,
35665                     raw: response
35666                 });
35667
35668                 switch (response.status) {
35669                     case 200:   // standard 200 response, send control back to HttpProxy#onWrite by returning true from this intercepted #onWrite
35670                         return true;
35671                         break;
35672                     case 201:   // entity created but no response returned
35673                         if (Ext.isEmpty(res.raw.responseText)) {
35674                           res.success = true;
35675                         } else {
35676                           //if the response contains data, treat it like a 200
35677                           return true;
35678                         }
35679                         break;
35680                     case 204:  // no-content.  Create a fake response.
35681                         res.success = true;
35682                         res.data = null;
35683                         break;
35684                     default:
35685                         return true;
35686                         break;
35687                 }
35688                 if (res.success === true) {
35689                     this.fireEvent("write", this, action, res.data, res, rs, o.request.arg);
35690                 } else {
35691                     this.fireEvent('exception', this, 'remote', action, o, res, rs);
35692                 }
35693                 o.request.callback.call(o.request.scope, res.data, res, res.success);
35694
35695                 return false;   // <-- false to prevent intercepted function from running.
35696             }, proxy);
35697         }
35698     };
35699 })();
35700
35701 /**
35702  * Ext.data.Response
35703  * Experimental.  Do not use directly.
35704  */
35705 Ext.data.Response = function(params, response) {
35706     Ext.apply(this, params, {
35707         raw: response
35708     });
35709 };
35710 Ext.data.Response.prototype = {
35711     message : null,
35712     success : false,
35713     status : null,
35714     root : null,
35715     raw : null,
35716
35717     getMessage : function() {
35718         return this.message;
35719     },
35720     getSuccess : function() {
35721         return this.success;
35722     },
35723     getStatus : function() {
35724         return this.status;
35725     },
35726     getRoot : function() {
35727         return this.root;
35728     },
35729     getRawResponse : function() {
35730         return this.raw;
35731     }
35732 };
35733
35734 /**
35735  * @class Ext.data.Api.Error
35736  * @extends Ext.Error
35737  * Error class for Ext.data.Api errors
35738  */
35739 Ext.data.Api.Error = Ext.extend(Ext.Error, {
35740     constructor : function(message, arg) {
35741         this.arg = arg;
35742         Ext.Error.call(this, message);
35743     },
35744     name: 'Ext.data.Api'
35745 });
35746 Ext.apply(Ext.data.Api.Error.prototype, {
35747     lang: {
35748         'action-url-undefined': 'No fallback url defined for this action.  When defining a DataProxy api, please be sure to define an url for each CRUD action in Ext.data.Api.actions or define a default url in addition to your api-configuration.',
35749         'invalid': 'received an invalid API-configuration.  Please ensure your proxy API-configuration contains only the actions defined in Ext.data.Api.actions',
35750         'invalid-url': 'Invalid url.  Please review your proxy configuration.',
35751         'execute': 'Attempted to execute an unknown action.  Valid API actions are defined in Ext.data.Api.actions"'
35752     }
35753 });
35754
35755
35756
35757 /**
35758  * @class Ext.data.SortTypes
35759  * @singleton
35760  * Defines the default sorting (casting?) comparison functions used when sorting data.
35761  */
35762 Ext.data.SortTypes = {
35763     /**
35764      * Default sort that does nothing
35765      * @param {Mixed} s The value being converted
35766      * @return {Mixed} The comparison value
35767      */
35768     none : function(s){
35769         return s;
35770     },
35771     
35772     /**
35773      * The regular expression used to strip tags
35774      * @type {RegExp}
35775      * @property
35776      */
35777     stripTagsRE : /<\/?[^>]+>/gi,
35778     
35779     /**
35780      * Strips all HTML tags to sort on text only
35781      * @param {Mixed} s The value being converted
35782      * @return {String} The comparison value
35783      */
35784     asText : function(s){
35785         return String(s).replace(this.stripTagsRE, "");
35786     },
35787     
35788     /**
35789      * Strips all HTML tags to sort on text only - Case insensitive
35790      * @param {Mixed} s The value being converted
35791      * @return {String} The comparison value
35792      */
35793     asUCText : function(s){
35794         return String(s).toUpperCase().replace(this.stripTagsRE, "");
35795     },
35796     
35797     /**
35798      * Case insensitive string
35799      * @param {Mixed} s The value being converted
35800      * @return {String} The comparison value
35801      */
35802     asUCString : function(s) {
35803         return String(s).toUpperCase();
35804     },
35805     
35806     /**
35807      * Date sorting
35808      * @param {Mixed} s The value being converted
35809      * @return {Number} The comparison value
35810      */
35811     asDate : function(s) {
35812         if(!s){
35813             return 0;
35814         }
35815         if(Ext.isDate(s)){
35816             return s.getTime();
35817         }
35818         return Date.parse(String(s));
35819     },
35820     
35821     /**
35822      * Float sorting
35823      * @param {Mixed} s The value being converted
35824      * @return {Float} The comparison value
35825      */
35826     asFloat : function(s) {
35827         var val = parseFloat(String(s).replace(/,/g, ""));
35828         return isNaN(val) ? 0 : val;
35829     },
35830     
35831     /**
35832      * Integer sorting
35833      * @param {Mixed} s The value being converted
35834      * @return {Number} The comparison value
35835      */
35836     asInt : function(s) {
35837         var val = parseInt(String(s).replace(/,/g, ""), 10);
35838         return isNaN(val) ? 0 : val;
35839     }
35840 };/**
35841  * @class Ext.data.Record
35842  * <p>Instances of this class encapsulate both Record <em>definition</em> information, and Record
35843  * <em>value</em> information for use in {@link Ext.data.Store} objects, or any code which needs
35844  * to access Records cached in an {@link Ext.data.Store} object.</p>
35845  * <p>Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
35846  * Instances are usually only created by {@link Ext.data.Reader} implementations when processing unformatted data
35847  * objects.</p>
35848  * <p>Note that an instance of a Record class may only belong to one {@link Ext.data.Store Store} at a time.
35849  * In order to copy data from one Store to another, use the {@link #copy} method to create an exact
35850  * copy of the Record, and insert the new instance into the other Store.</p>
35851  * <p>When serializing a Record for submission to the server, be aware that it contains many private
35852  * properties, and also a reference to its owning Store which in turn holds references to its Records.
35853  * This means that a whole Record may not be encoded using {@link Ext.util.JSON.encode}. Instead, use the
35854  * <code>{@link #data}</code> and <code>{@link #id}</code> properties.</p>
35855  * <p>Record objects generated by this constructor inherit all the methods of Ext.data.Record listed below.</p>
35856  * @constructor
35857  * <p>This constructor should not be used to create Record objects. Instead, use {@link #create} to
35858  * generate a subclass of Ext.data.Record configured with information about its constituent fields.<p>
35859  * <p><b>The generated constructor has the same signature as this constructor.</b></p>
35860  * @param {Object} data (Optional) An object, the properties of which provide values for the new Record's
35861  * fields. If not specified the <code>{@link Ext.data.Field#defaultValue defaultValue}</code>
35862  * for each field will be assigned.
35863  * @param {Object} id (Optional) The id of the Record. The id is used by the
35864  * {@link Ext.data.Store} object which owns the Record to index its collection
35865  * of Records (therefore this id should be unique within each store). If an
35866  * <code>id</code> is not specified a <b><code>{@link #phantom}</code></b>
35867  * Record will be created with an {@link #Record.id automatically generated id}.
35868  */
35869 Ext.data.Record = function(data, id){
35870     // if no id, call the auto id method
35871     this.id = (id || id === 0) ? id : Ext.data.Record.id(this);
35872     this.data = data || {};
35873 };
35874
35875 /**
35876  * Generate a constructor for a specific Record layout.
35877  * @param {Array} o An Array of <b>{@link Ext.data.Field Field}</b> definition objects.
35878  * The constructor generated by this method may be used to create new Record instances. The data
35879  * object must contain properties named after the {@link Ext.data.Field field}
35880  * <b><tt>{@link Ext.data.Field#name}s</tt></b>.  Example usage:<pre><code>
35881 // create a Record constructor from a description of the fields
35882 var TopicRecord = Ext.data.Record.create([ // creates a subclass of Ext.data.Record
35883     {{@link Ext.data.Field#name name}: 'title', {@link Ext.data.Field#mapping mapping}: 'topic_title'},
35884     {name: 'author', mapping: 'username', allowBlank: false},
35885     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
35886     {name: 'lastPost', mapping: 'post_time', type: 'date'},
35887     {name: 'lastPoster', mapping: 'user2'},
35888     {name: 'excerpt', mapping: 'post_text', allowBlank: false},
35889     // In the simplest case, if no properties other than <tt>name</tt> are required,
35890     // a field definition may consist of just a String for the field name.
35891     'signature'
35892 ]);
35893
35894 // create Record instance
35895 var myNewRecord = new TopicRecord(
35896     {
35897         title: 'Do my job please',
35898         author: 'noobie',
35899         totalPosts: 1,
35900         lastPost: new Date(),
35901         lastPoster: 'Animal',
35902         excerpt: 'No way dude!',
35903         signature: ''
35904     },
35905     id // optionally specify the id of the record otherwise {@link #Record.id one is auto-assigned}
35906 );
35907 myStore.{@link Ext.data.Store#add add}(myNewRecord);
35908 </code></pre>
35909  * @method create
35910  * @return {Function} A constructor which is used to create new Records according
35911  * to the definition. The constructor has the same signature as {@link #Record}.
35912  * @static
35913  */
35914 Ext.data.Record.create = function(o){
35915     var f = Ext.extend(Ext.data.Record, {});
35916     var p = f.prototype;
35917     p.fields = new Ext.util.MixedCollection(false, function(field){
35918         return field.name;
35919     });
35920     for(var i = 0, len = o.length; i < len; i++){
35921         p.fields.add(new Ext.data.Field(o[i]));
35922     }
35923     f.getField = function(name){
35924         return p.fields.get(name);
35925     };
35926     return f;
35927 };
35928
35929 Ext.data.Record.PREFIX = 'ext-record';
35930 Ext.data.Record.AUTO_ID = 1;
35931 Ext.data.Record.EDIT = 'edit';
35932 Ext.data.Record.REJECT = 'reject';
35933 Ext.data.Record.COMMIT = 'commit';
35934
35935
35936 /**
35937  * Generates a sequential id. This method is typically called when a record is {@link #create}d
35938  * and {@link #Record no id has been specified}. The returned id takes the form:
35939  * <tt>&#123;PREFIX}-&#123;AUTO_ID}</tt>.<div class="mdetail-params"><ul>
35940  * <li><b><tt>PREFIX</tt></b> : String<p class="sub-desc"><tt>Ext.data.Record.PREFIX</tt>
35941  * (defaults to <tt>'ext-record'</tt>)</p></li>
35942  * <li><b><tt>AUTO_ID</tt></b> : String<p class="sub-desc"><tt>Ext.data.Record.AUTO_ID</tt>
35943  * (defaults to <tt>1</tt> initially)</p></li>
35944  * </ul></div>
35945  * @param {Record} rec The record being created.  The record does not exist, it's a {@link #phantom}.
35946  * @return {String} auto-generated string id, <tt>"ext-record-i++'</tt>;
35947  */
35948 Ext.data.Record.id = function(rec) {
35949     rec.phantom = true;
35950     return [Ext.data.Record.PREFIX, '-', Ext.data.Record.AUTO_ID++].join('');
35951 };
35952
35953 Ext.data.Record.prototype = {
35954     /**
35955      * <p><b>This property is stored in the Record definition's <u>prototype</u></b></p>
35956      * A MixedCollection containing the defined {@link Ext.data.Field Field}s for this Record.  Read-only.
35957      * @property fields
35958      * @type Ext.util.MixedCollection
35959      */
35960     /**
35961      * An object hash representing the data for this Record. Every field name in the Record definition
35962      * is represented by a property of that name in this object. Note that unless you specified a field
35963      * with {@link Ext.data.Field#name name} "id" in the Record definition, this will <b>not</b> contain
35964      * an <tt>id</tt> property.
35965      * @property data
35966      * @type {Object}
35967      */
35968     /**
35969      * The unique ID of the Record {@link #Record as specified at construction time}.
35970      * @property id
35971      * @type {Object}
35972      */
35973     /**
35974      * <p><b>Only present if this Record was created by an {@link Ext.data.XmlReader XmlReader}</b>.</p>
35975      * <p>The XML element which was the source of the data for this Record.</p>
35976      * @property node
35977      * @type {XMLElement}
35978      */
35979     /**
35980      * <p><b>Only present if this Record was created by an {@link Ext.data.ArrayReader ArrayReader} or a {@link Ext.data.JsonReader JsonReader}</b>.</p>
35981      * <p>The Array or object which was the source of the data for this Record.</p>
35982      * @property json
35983      * @type {Array|Object}
35984      */
35985     /**
35986      * Readonly flag - true if this Record has been modified.
35987      * @type Boolean
35988      */
35989     dirty : false,
35990     editing : false,
35991     error : null,
35992     /**
35993      * This object contains a key and value storing the original values of all modified
35994      * fields or is null if no fields have been modified.
35995      * @property modified
35996      * @type {Object}
35997      */
35998     modified : null,
35999     /**
36000      * <tt>true</tt> when the record does not yet exist in a server-side database (see
36001      * {@link #markDirty}).  Any record which has a real database pk set as its id property
36002      * is NOT a phantom -- it's real.
36003      * @property phantom
36004      * @type {Boolean}
36005      */
36006     phantom : false,
36007
36008     // private
36009     join : function(store){
36010         /**
36011          * The {@link Ext.data.Store} to which this Record belongs.
36012          * @property store
36013          * @type {Ext.data.Store}
36014          */
36015         this.store = store;
36016     },
36017
36018     /**
36019      * Set the {@link Ext.data.Field#name named field} to the specified value.  For example:
36020      * <pre><code>
36021 // record has a field named 'firstname'
36022 var Employee = Ext.data.Record.{@link #create}([
36023     {name: 'firstname'},
36024     ...
36025 ]);
36026
36027 // update the 2nd record in the store:
36028 var rec = myStore.{@link Ext.data.Store#getAt getAt}(1);
36029
36030 // set the value (shows dirty flag):
36031 rec.set('firstname', 'Betty');
36032
36033 // commit the change (removes dirty flag):
36034 rec.{@link #commit}();
36035
36036 // update the record in the store, bypass setting dirty flag,
36037 // and do not store the change in the {@link Ext.data.Store#getModifiedRecords modified records}
36038 rec.{@link #data}['firstname'] = 'Wilma'; // updates record, but not the view
36039 rec.{@link #commit}(); // updates the view
36040      * </code></pre>
36041      * <b>Notes</b>:<div class="mdetail-params"><ul>
36042      * <li>If the store has a writer and <code>autoSave=true</code>, each set()
36043      * will execute an XHR to the server.</li>
36044      * <li>Use <code>{@link #beginEdit}</code> to prevent the store's <code>update</code>
36045      * event firing while using set().</li>
36046      * <li>Use <code>{@link #endEdit}</code> to have the store's <code>update</code>
36047      * event fire.</li>
36048      * </ul></div>
36049      * @param {String} name The {@link Ext.data.Field#name name of the field} to set.
36050      * @param {String/Object/Array} value The value to set the field to.
36051      */
36052     set : function(name, value){
36053         var encode = Ext.isPrimitive(value) ? String : Ext.encode;
36054         if(encode(this.data[name]) == encode(value)) {
36055             return;
36056         }        
36057         this.dirty = true;
36058         if(!this.modified){
36059             this.modified = {};
36060         }
36061         if(this.modified[name] === undefined){
36062             this.modified[name] = this.data[name];
36063         }
36064         this.data[name] = value;
36065         if(!this.editing){
36066             this.afterEdit();
36067         }
36068     },
36069
36070     // private
36071     afterEdit : function(){
36072         if (this.store != undefined && typeof this.store.afterEdit == "function") {
36073             this.store.afterEdit(this);
36074         }
36075     },
36076
36077     // private
36078     afterReject : function(){
36079         if(this.store){
36080             this.store.afterReject(this);
36081         }
36082     },
36083
36084     // private
36085     afterCommit : function(){
36086         if(this.store){
36087             this.store.afterCommit(this);
36088         }
36089     },
36090
36091     /**
36092      * Get the value of the {@link Ext.data.Field#name named field}.
36093      * @param {String} name The {@link Ext.data.Field#name name of the field} to get the value of.
36094      * @return {Object} The value of the field.
36095      */
36096     get : function(name){
36097         return this.data[name];
36098     },
36099
36100     /**
36101      * Begin an edit. While in edit mode, no events (e.g.. the <code>update</code> event)
36102      * are relayed to the containing store.
36103      * See also: <code>{@link #endEdit}</code> and <code>{@link #cancelEdit}</code>.
36104      */
36105     beginEdit : function(){
36106         this.editing = true;
36107         this.modified = this.modified || {};
36108     },
36109
36110     /**
36111      * Cancels all changes made in the current edit operation.
36112      */
36113     cancelEdit : function(){
36114         this.editing = false;
36115         delete this.modified;
36116     },
36117
36118     /**
36119      * End an edit. If any data was modified, the containing store is notified
36120      * (ie, the store's <code>update</code> event will fire).
36121      */
36122     endEdit : function(){
36123         this.editing = false;
36124         if(this.dirty){
36125             this.afterEdit();
36126         }
36127     },
36128
36129     /**
36130      * Usually called by the {@link Ext.data.Store} which owns the Record.
36131      * Rejects all changes made to the Record since either creation, or the last commit operation.
36132      * Modified fields are reverted to their original values.
36133      * <p>Developers should subscribe to the {@link Ext.data.Store#update} event
36134      * to have their code notified of reject operations.</p>
36135      * @param {Boolean} silent (optional) True to skip notification of the owning
36136      * store of the change (defaults to false)
36137      */
36138     reject : function(silent){
36139         var m = this.modified;
36140         for(var n in m){
36141             if(typeof m[n] != "function"){
36142                 this.data[n] = m[n];
36143             }
36144         }
36145         this.dirty = false;
36146         delete this.modified;
36147         this.editing = false;
36148         if(silent !== true){
36149             this.afterReject();
36150         }
36151     },
36152
36153     /**
36154      * Usually called by the {@link Ext.data.Store} which owns the Record.
36155      * Commits all changes made to the Record since either creation, or the last commit operation.
36156      * <p>Developers should subscribe to the {@link Ext.data.Store#update} event
36157      * to have their code notified of commit operations.</p>
36158      * @param {Boolean} silent (optional) True to skip notification of the owning
36159      * store of the change (defaults to false)
36160      */
36161     commit : function(silent){
36162         this.dirty = false;
36163         delete this.modified;
36164         this.editing = false;
36165         if(silent !== true){
36166             this.afterCommit();
36167         }
36168     },
36169
36170     /**
36171      * Gets a hash of only the fields that have been modified since this Record was created or commited.
36172      * @return Object
36173      */
36174     getChanges : function(){
36175         var m = this.modified, cs = {};
36176         for(var n in m){
36177             if(m.hasOwnProperty(n)){
36178                 cs[n] = this.data[n];
36179             }
36180         }
36181         return cs;
36182     },
36183
36184     // private
36185     hasError : function(){
36186         return this.error !== null;
36187     },
36188
36189     // private
36190     clearError : function(){
36191         this.error = null;
36192     },
36193
36194     /**
36195      * Creates a copy (clone) of this Record.
36196      * @param {String} id (optional) A new Record id, defaults to the id
36197      * of the record being copied. See <code>{@link #id}</code>. 
36198      * To generate a phantom record with a new id use:<pre><code>
36199 var rec = record.copy(); // clone the record
36200 Ext.data.Record.id(rec); // automatically generate a unique sequential id
36201      * </code></pre>
36202      * @return {Record}
36203      */
36204     copy : function(newId) {
36205         return new this.constructor(Ext.apply({}, this.data), newId || this.id);
36206     },
36207
36208     /**
36209      * Returns <tt>true</tt> if the passed field name has been <code>{@link #modified}</code>
36210      * since the load or last commit.
36211      * @param {String} fieldName {@link Ext.data.Field.{@link Ext.data.Field#name}
36212      * @return {Boolean}
36213      */
36214     isModified : function(fieldName){
36215         return !!(this.modified && this.modified.hasOwnProperty(fieldName));
36216     },
36217
36218     /**
36219      * By default returns <tt>false</tt> if any {@link Ext.data.Field field} within the
36220      * record configured with <tt>{@link Ext.data.Field#allowBlank} = false</tt> returns
36221      * <tt>true</tt> from an {@link Ext}.{@link Ext#isEmpty isempty} test.
36222      * @return {Boolean}
36223      */
36224     isValid : function() {
36225         return this.fields.find(function(f) {
36226             return (f.allowBlank === false && Ext.isEmpty(this.data[f.name])) ? true : false;
36227         },this) ? false : true;
36228     },
36229
36230     /**
36231      * <p>Marks this <b>Record</b> as <code>{@link #dirty}</code>.  This method
36232      * is used interally when adding <code>{@link #phantom}</code> records to a
36233      * {@link Ext.data.Store#writer writer enabled store}.</p>
36234      * <br><p>Marking a record <code>{@link #dirty}</code> causes the phantom to
36235      * be returned by {@link Ext.data.Store#getModifiedRecords} where it will
36236      * have a create action composed for it during {@link Ext.data.Store#save store save}
36237      * operations.</p>
36238      */
36239     markDirty : function(){
36240         this.dirty = true;
36241         if(!this.modified){
36242             this.modified = {};
36243         }
36244         this.fields.each(function(f) {
36245             this.modified[f.name] = this.data[f.name];
36246         },this);
36247     }
36248 };
36249 /**
36250  * @class Ext.StoreMgr
36251  * @extends Ext.util.MixedCollection
36252  * The default global group of stores.
36253  * @singleton
36254  */
36255 Ext.StoreMgr = Ext.apply(new Ext.util.MixedCollection(), {
36256     /**
36257      * @cfg {Object} listeners @hide
36258      */
36259
36260     /**
36261      * Registers one or more Stores with the StoreMgr. You do not normally need to register stores
36262      * manually.  Any store initialized with a {@link Ext.data.Store#storeId} will be auto-registered. 
36263      * @param {Ext.data.Store} store1 A Store instance
36264      * @param {Ext.data.Store} store2 (optional)
36265      * @param {Ext.data.Store} etc... (optional)
36266      */
36267     register : function(){
36268         for(var i = 0, s; (s = arguments[i]); i++){
36269             this.add(s);
36270         }
36271     },
36272
36273     /**
36274      * Unregisters one or more Stores with the StoreMgr
36275      * @param {String/Object} id1 The id of the Store, or a Store instance
36276      * @param {String/Object} id2 (optional)
36277      * @param {String/Object} etc... (optional)
36278      */
36279     unregister : function(){
36280         for(var i = 0, s; (s = arguments[i]); i++){
36281             this.remove(this.lookup(s));
36282         }
36283     },
36284
36285     /**
36286      * Gets a registered Store by id
36287      * @param {String/Object} id The id of the Store, or a Store instance
36288      * @return {Ext.data.Store}
36289      */
36290     lookup : function(id){
36291         if(Ext.isArray(id)){
36292             var fields = ['field1'], expand = !Ext.isArray(id[0]);
36293             if(!expand){
36294                 for(var i = 2, len = id[0].length; i <= len; ++i){
36295                     fields.push('field' + i);
36296                 }
36297             }
36298             return new Ext.data.ArrayStore({
36299                 fields: fields,
36300                 data: id,
36301                 expandData: expand,
36302                 autoDestroy: true,
36303                 autoCreated: true
36304
36305             });
36306         }
36307         return Ext.isObject(id) ? (id.events ? id : Ext.create(id, 'store')) : this.get(id);
36308     },
36309
36310     // getKey implementation for MixedCollection
36311     getKey : function(o){
36312          return o.storeId;
36313     }
36314 });/**
36315  * @class Ext.data.Store
36316  * @extends Ext.util.Observable
36317  * <p>The Store class encapsulates a client side cache of {@link Ext.data.Record Record}
36318  * objects which provide input data for Components such as the {@link Ext.grid.GridPanel GridPanel},
36319  * the {@link Ext.form.ComboBox ComboBox}, or the {@link Ext.DataView DataView}.</p>
36320  * <p><u>Retrieving Data</u></p>
36321  * <p>A Store object may access a data object using:<div class="mdetail-params"><ul>
36322  * <li>{@link #proxy configured implementation} of {@link Ext.data.DataProxy DataProxy}</li>
36323  * <li>{@link #data} to automatically pass in data</li>
36324  * <li>{@link #loadData} to manually pass in data</li>
36325  * </ul></div></p>
36326  * <p><u>Reading Data</u></p>
36327  * <p>A Store object has no inherent knowledge of the format of the data object (it could be
36328  * an Array, XML, or JSON). A Store object uses an appropriate {@link #reader configured implementation}
36329  * of a {@link Ext.data.DataReader DataReader} to create {@link Ext.data.Record Record} instances from the data
36330  * object.</p>
36331  * <p><u>Store Types</u></p>
36332  * <p>There are several implementations of Store available which are customized for use with
36333  * a specific DataReader implementation.  Here is an example using an ArrayStore which implicitly
36334  * creates a reader commensurate to an Array data object.</p>
36335  * <pre><code>
36336 var myStore = new Ext.data.ArrayStore({
36337     fields: ['fullname', 'first'],
36338     idIndex: 0 // id for each record will be the first element
36339 });
36340  * </code></pre>
36341  * <p>For custom implementations create a basic {@link Ext.data.Store} configured as needed:</p>
36342  * <pre><code>
36343 // create a {@link Ext.data.Record Record} constructor:
36344 var rt = Ext.data.Record.create([
36345     {name: 'fullname'},
36346     {name: 'first'}
36347 ]);
36348 var myStore = new Ext.data.Store({
36349     // explicitly create reader
36350     reader: new Ext.data.ArrayReader(
36351         {
36352             idIndex: 0  // id for each record will be the first element
36353         },
36354         rt // recordType
36355     )
36356 });
36357  * </code></pre>
36358  * <p>Load some data into store (note the data object is an array which corresponds to the reader):</p>
36359  * <pre><code>
36360 var myData = [
36361     [1, 'Fred Flintstone', 'Fred'],  // note that id for the record is the first element
36362     [2, 'Barney Rubble', 'Barney']
36363 ];
36364 myStore.loadData(myData);
36365  * </code></pre>
36366  * <p>Records are cached and made available through accessor functions.  An example of adding
36367  * a record to the store:</p>
36368  * <pre><code>
36369 var defaultData = {
36370     fullname: 'Full Name',
36371     first: 'First Name'
36372 };
36373 var recId = 100; // provide unique id for the record
36374 var r = new myStore.recordType(defaultData, ++recId); // create new record
36375 myStore.{@link #insert}(0, r); // insert a new record into the store (also see {@link #add})
36376  * </code></pre>
36377  * <p><u>Writing Data</u></p>
36378  * <p>And <b>new in Ext version 3</b>, use the new {@link Ext.data.DataWriter DataWriter} to create an automated, <a href="http://extjs.com/deploy/dev/examples/writer/writer.html">Writable Store</a>
36379  * along with <a href="http://extjs.com/deploy/dev/examples/restful/restful.html">RESTful features.</a>
36380  * @constructor
36381  * Creates a new Store.
36382  * @param {Object} config A config object containing the objects needed for the Store to access data,
36383  * and read the data into Records.
36384  * @xtype store
36385  */
36386 Ext.data.Store = Ext.extend(Ext.util.Observable, {
36387     /**
36388      * @cfg {String} storeId If passed, the id to use to register with the <b>{@link Ext.StoreMgr StoreMgr}</b>.
36389      * <p><b>Note</b>: if a (deprecated) <tt>{@link #id}</tt> is specified it will supersede the <tt>storeId</tt>
36390      * assignment.</p>
36391      */
36392     /**
36393      * @cfg {String} url If a <tt>{@link #proxy}</tt> is not specified the <tt>url</tt> will be used to
36394      * implicitly configure a {@link Ext.data.HttpProxy HttpProxy} if an <tt>url</tt> is specified.
36395      * Typically this option, or the <code>{@link #data}</code> option will be specified.
36396      */
36397     /**
36398      * @cfg {Boolean/Object} autoLoad If <tt>{@link #data}</tt> is not specified, and if <tt>autoLoad</tt>
36399      * is <tt>true</tt> or an <tt>Object</tt>, this store's {@link #load} method is automatically called
36400      * after creation. If the value of <tt>autoLoad</tt> is an <tt>Object</tt>, this <tt>Object</tt> will
36401      * be passed to the store's {@link #load} method.
36402      */
36403     /**
36404      * @cfg {Ext.data.DataProxy} proxy The {@link Ext.data.DataProxy DataProxy} object which provides
36405      * access to a data object.  See <code>{@link #url}</code>.
36406      */
36407     /**
36408      * @cfg {Array} data An inline data object readable by the <code>{@link #reader}</code>.
36409      * Typically this option, or the <code>{@link #url}</code> option will be specified.
36410      */
36411     /**
36412      * @cfg {Ext.data.DataReader} reader The {@link Ext.data.DataReader Reader} object which processes the
36413      * data object and returns an Array of {@link Ext.data.Record} objects which are cached keyed by their
36414      * <b><tt>{@link Ext.data.Record#id id}</tt></b> property.
36415      */
36416     /**
36417      * @cfg {Ext.data.DataWriter} writer
36418      * <p>The {@link Ext.data.DataWriter Writer} object which processes a record object for being written
36419      * to the server-side database.</p>
36420      * <br><p>When a writer is installed into a Store the {@link #add}, {@link #remove}, and {@link #update}
36421      * events on the store are monitored in order to remotely {@link #createRecords create records},
36422      * {@link #destroyRecord destroy records}, or {@link #updateRecord update records}.</p>
36423      * <br><p>The proxy for this store will relay any {@link #writexception} events to this store.</p>
36424      * <br><p>Sample implementation:
36425      * <pre><code>
36426 var writer = new {@link Ext.data.JsonWriter}({
36427     encode: true,
36428     writeAllFields: true // write all fields, not just those that changed
36429 });
36430
36431 // Typical Store collecting the Proxy, Reader and Writer together.
36432 var store = new Ext.data.Store({
36433     storeId: 'user',
36434     root: 'records',
36435     proxy: proxy,
36436     reader: reader,
36437     writer: writer,     // <-- plug a DataWriter into the store just as you would a Reader
36438     paramsAsHash: true,
36439     autoSave: false    // <-- false to delay executing create, update, destroy requests
36440                         //     until specifically told to do so.
36441 });
36442      * </code></pre></p>
36443      */
36444     writer : undefined,
36445     /**
36446      * @cfg {Object} baseParams
36447      * <p>An object containing properties which are to be sent as parameters
36448      * for <i>every</i> HTTP request.</p>
36449      * <p>Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.</p>
36450      * <p><b>Note</b>: <code>baseParams</code> may be superseded by any <code>params</code>
36451      * specified in a <code>{@link #load}</code> request, see <code>{@link #load}</code>
36452      * for more details.</p>
36453      * This property may be modified after creation using the <code>{@link #setBaseParam}</code>
36454      * method.
36455      * @property
36456      */
36457     /**
36458      * @cfg {Object} sortInfo A config object to specify the sort order in the request of a Store's
36459      * {@link #load} operation.  Note that for local sorting, the <tt>direction</tt> property is
36460      * case-sensitive. See also {@link #remoteSort} and {@link #paramNames}.
36461      * For example:<pre><code>
36462 sortInfo: {
36463     field: 'fieldName',
36464     direction: 'ASC' // or 'DESC' (case sensitive for local sorting)
36465 }
36466 </code></pre>
36467      */
36468     /**
36469      * @cfg {boolean} remoteSort <tt>true</tt> if sorting is to be handled by requesting the <tt>{@link #proxy Proxy}</tt>
36470      * to provide a refreshed version of the data object in sorted order, as opposed to sorting the Record cache
36471      * in place (defaults to <tt>false</tt>).
36472      * <p>If <tt>remoteSort</tt> is <tt>true</tt>, then clicking on a {@link Ext.grid.Column Grid Column}'s
36473      * {@link Ext.grid.Column#header header} causes the current page to be requested from the server appending
36474      * the following two parameters to the <b><tt>{@link #load params}</tt></b>:<div class="mdetail-params"><ul>
36475      * <li><b><tt>sort</tt></b> : String<p class="sub-desc">The <tt>name</tt> (as specified in the Record's
36476      * {@link Ext.data.Field Field definition}) of the field to sort on.</p></li>
36477      * <li><b><tt>dir</tt></b> : String<p class="sub-desc">The direction of the sort, 'ASC' or 'DESC' (case-sensitive).</p></li>
36478      * </ul></div></p>
36479      */
36480     remoteSort : false,
36481
36482     /**
36483      * @cfg {Boolean} autoDestroy <tt>true</tt> to destroy the store when the component the store is bound
36484      * to is destroyed (defaults to <tt>false</tt>).
36485      * <p><b>Note</b>: this should be set to true when using stores that are bound to only 1 component.</p>
36486      */
36487     autoDestroy : false,
36488
36489     /**
36490      * @cfg {Boolean} pruneModifiedRecords <tt>true</tt> to clear all modified record information each time
36491      * the store is loaded or when a record is removed (defaults to <tt>false</tt>). See {@link #getModifiedRecords}
36492      * for the accessor method to retrieve the modified records.
36493      */
36494     pruneModifiedRecords : false,
36495
36496     /**
36497      * Contains the last options object used as the parameter to the {@link #load} method. See {@link #load}
36498      * for the details of what this may contain. This may be useful for accessing any params which were used
36499      * to load the current Record cache.
36500      * @property
36501      */
36502     lastOptions : null,
36503
36504     /**
36505      * @cfg {Boolean} autoSave
36506      * <p>Defaults to <tt>true</tt> causing the store to automatically {@link #save} records to
36507      * the server when a record is modified (ie: becomes 'dirty'). Specify <tt>false</tt> to manually call {@link #save}
36508      * to send all modifiedRecords to the server.</p>
36509      * <br><p><b>Note</b>: each CRUD action will be sent as a separate request.</p>
36510      */
36511     autoSave : true,
36512
36513     /**
36514      * @cfg {Boolean} batch
36515      * <p>Defaults to <tt>true</tt> (unless <code>{@link #restful}:true</code>). Multiple
36516      * requests for each CRUD action (CREATE, READ, UPDATE and DESTROY) will be combined
36517      * and sent as one transaction. Only applies when <code>{@link #autoSave}</code> is set
36518      * to <tt>false</tt>.</p>
36519      * <br><p>If Store is RESTful, the DataProxy is also RESTful, and a unique transaction is
36520      * generated for each record.</p>
36521      */
36522     batch : true,
36523
36524     /**
36525      * @cfg {Boolean} restful
36526      * Defaults to <tt>false</tt>.  Set to <tt>true</tt> to have the Store and the set
36527      * Proxy operate in a RESTful manner. The store will automatically generate GET, POST,
36528      * PUT and DELETE requests to the server. The HTTP method used for any given CRUD
36529      * action is described in {@link Ext.data.Api#restActions}.  For additional information
36530      * see {@link Ext.data.DataProxy#restful}.
36531      * <p><b>Note</b>: if <code>{@link #restful}:true</code> <code>batch</code> will
36532      * internally be set to <tt>false</tt>.</p>
36533      */
36534     restful: false,
36535
36536     /**
36537      * @cfg {Object} paramNames
36538      * <p>An object containing properties which specify the names of the paging and
36539      * sorting parameters passed to remote servers when loading blocks of data. By default, this
36540      * object takes the following form:</p><pre><code>
36541 {
36542     start : 'start',  // The parameter name which specifies the start row
36543     limit : 'limit',  // The parameter name which specifies number of rows to return
36544     sort : 'sort',    // The parameter name which specifies the column to sort on
36545     dir : 'dir'       // The parameter name which specifies the sort direction
36546 }
36547 </code></pre>
36548      * <p>The server must produce the requested data block upon receipt of these parameter names.
36549      * If different parameter names are required, this property can be overriden using a configuration
36550      * property.</p>
36551      * <p>A {@link Ext.PagingToolbar PagingToolbar} bound to this Store uses this property to determine
36552      * the parameter names to use in its {@link #load requests}.
36553      */
36554     paramNames : undefined,
36555
36556     /**
36557      * @cfg {Object} defaultParamNames
36558      * Provides the default values for the {@link #paramNames} property. To globally modify the parameters
36559      * for all stores, this object should be changed on the store prototype.
36560      */
36561     defaultParamNames : {
36562         start : 'start',
36563         limit : 'limit',
36564         sort : 'sort',
36565         dir : 'dir'
36566     },
36567
36568     isDestroyed: false,    
36569     hasMultiSort: false,
36570
36571     // private
36572     batchKey : '_ext_batch_',
36573
36574     constructor : function(config){
36575         /**
36576          * @property multiSort
36577          * @type Boolean
36578          * True if this store is currently sorted by more than one field/direction combination.
36579          */
36580         
36581         /**
36582          * @property isDestroyed
36583          * @type Boolean
36584          * True if the store has been destroyed already. Read only
36585          */
36586         
36587         this.data = new Ext.util.MixedCollection(false);
36588         this.data.getKey = function(o){
36589             return o.id;
36590         };
36591         
36592
36593         // temporary removed-records cache
36594         this.removed = [];
36595
36596         if(config && config.data){
36597             this.inlineData = config.data;
36598             delete config.data;
36599         }
36600
36601         Ext.apply(this, config);
36602
36603         /**
36604          * See the <code>{@link #baseParams corresponding configuration option}</code>
36605          * for a description of this property.
36606          * To modify this property see <code>{@link #setBaseParam}</code>.
36607          * @property
36608          */
36609         this.baseParams = Ext.isObject(this.baseParams) ? this.baseParams : {};
36610
36611         this.paramNames = Ext.applyIf(this.paramNames || {}, this.defaultParamNames);
36612
36613         if((this.url || this.api) && !this.proxy){
36614             this.proxy = new Ext.data.HttpProxy({url: this.url, api: this.api});
36615         }
36616         // If Store is RESTful, so too is the DataProxy
36617         if (this.restful === true && this.proxy) {
36618             // When operating RESTfully, a unique transaction is generated for each record.
36619             // TODO might want to allow implemention of faux REST where batch is possible using RESTful routes only.
36620             this.batch = false;
36621             Ext.data.Api.restify(this.proxy);
36622         }
36623
36624         if(this.reader){ // reader passed
36625             if(!this.recordType){
36626                 this.recordType = this.reader.recordType;
36627             }
36628             if(this.reader.onMetaChange){
36629                 this.reader.onMetaChange = this.reader.onMetaChange.createSequence(this.onMetaChange, this);
36630             }
36631             if (this.writer) { // writer passed
36632                 if (this.writer instanceof(Ext.data.DataWriter) === false) {    // <-- config-object instead of instance.
36633                     this.writer = this.buildWriter(this.writer);
36634                 }
36635                 this.writer.meta = this.reader.meta;
36636                 this.pruneModifiedRecords = true;
36637             }
36638         }
36639
36640         /**
36641          * The {@link Ext.data.Record Record} constructor as supplied to (or created by) the
36642          * {@link Ext.data.DataReader Reader}. Read-only.
36643          * <p>If the Reader was constructed by passing in an Array of {@link Ext.data.Field} definition objects,
36644          * instead of a Record constructor, it will implicitly create a Record constructor from that Array (see
36645          * {@link Ext.data.Record}.{@link Ext.data.Record#create create} for additional details).</p>
36646          * <p>This property may be used to create new Records of the type held in this Store, for example:</p><pre><code>
36647     // create the data store
36648     var store = new Ext.data.ArrayStore({
36649         autoDestroy: true,
36650         fields: [
36651            {name: 'company'},
36652            {name: 'price', type: 'float'},
36653            {name: 'change', type: 'float'},
36654            {name: 'pctChange', type: 'float'},
36655            {name: 'lastChange', type: 'date', dateFormat: 'n/j h:ia'}
36656         ]
36657     });
36658     store.loadData(myData);
36659
36660     // create the Grid
36661     var grid = new Ext.grid.EditorGridPanel({
36662         store: store,
36663         colModel: new Ext.grid.ColumnModel({
36664             columns: [
36665                 {id:'company', header: 'Company', width: 160, dataIndex: 'company'},
36666                 {header: 'Price', renderer: 'usMoney', dataIndex: 'price'},
36667                 {header: 'Change', renderer: change, dataIndex: 'change'},
36668                 {header: '% Change', renderer: pctChange, dataIndex: 'pctChange'},
36669                 {header: 'Last Updated', width: 85,
36670                     renderer: Ext.util.Format.dateRenderer('m/d/Y'),
36671                     dataIndex: 'lastChange'}
36672             ],
36673             defaults: {
36674                 sortable: true,
36675                 width: 75
36676             }
36677         }),
36678         autoExpandColumn: 'company', // match the id specified in the column model
36679         height:350,
36680         width:600,
36681         title:'Array Grid',
36682         tbar: [{
36683             text: 'Add Record',
36684             handler : function(){
36685                 var defaultData = {
36686                     change: 0,
36687                     company: 'New Company',
36688                     lastChange: (new Date()).clearTime(),
36689                     pctChange: 0,
36690                     price: 10
36691                 };
36692                 var recId = 3; // provide unique id
36693                 var p = new store.recordType(defaultData, recId); // create new record
36694                 grid.stopEditing();
36695                 store.{@link #insert}(0, p); // insert a new record into the store (also see {@link #add})
36696                 grid.startEditing(0, 0);
36697             }
36698         }]
36699     });
36700          * </code></pre>
36701          * @property recordType
36702          * @type Function
36703          */
36704
36705         if(this.recordType){
36706             /**
36707              * A {@link Ext.util.MixedCollection MixedCollection} containing the defined {@link Ext.data.Field Field}s
36708              * for the {@link Ext.data.Record Records} stored in this Store. Read-only.
36709              * @property fields
36710              * @type Ext.util.MixedCollection
36711              */
36712             this.fields = this.recordType.prototype.fields;
36713         }
36714         this.modified = [];
36715
36716         this.addEvents(
36717             /**
36718              * @event datachanged
36719              * Fires when the data cache has changed in a bulk manner (e.g., it has been sorted, filtered, etc.) and a
36720              * widget that is using this Store as a Record cache should refresh its view.
36721              * @param {Store} this
36722              */
36723             'datachanged',
36724             /**
36725              * @event metachange
36726              * Fires when this store's reader provides new metadata (fields). This is currently only supported for JsonReaders.
36727              * @param {Store} this
36728              * @param {Object} meta The JSON metadata
36729              */
36730             'metachange',
36731             /**
36732              * @event add
36733              * Fires when Records have been {@link #add}ed to the Store
36734              * @param {Store} this
36735              * @param {Ext.data.Record[]} records The array of Records added
36736              * @param {Number} index The index at which the record(s) were added
36737              */
36738             'add',
36739             /**
36740              * @event remove
36741              * Fires when a Record has been {@link #remove}d from the Store
36742              * @param {Store} this
36743              * @param {Ext.data.Record} record The Record that was removed
36744              * @param {Number} index The index at which the record was removed
36745              */
36746             'remove',
36747             /**
36748              * @event update
36749              * Fires when a Record has been updated
36750              * @param {Store} this
36751              * @param {Ext.data.Record} record The Record that was updated
36752              * @param {String} operation The update operation being performed.  Value may be one of:
36753              * <pre><code>
36754      Ext.data.Record.EDIT
36755      Ext.data.Record.REJECT
36756      Ext.data.Record.COMMIT
36757              * </code></pre>
36758              */
36759             'update',
36760             /**
36761              * @event clear
36762              * Fires when the data cache has been cleared.
36763              * @param {Store} this
36764              * @param {Record[]} records The records that were cleared.
36765              */
36766             'clear',
36767             /**
36768              * @event exception
36769              * <p>Fires if an exception occurs in the Proxy during a remote request.
36770              * This event is relayed through the corresponding {@link Ext.data.DataProxy}.
36771              * See {@link Ext.data.DataProxy}.{@link Ext.data.DataProxy#exception exception}
36772              * for additional details.
36773              * @param {misc} misc See {@link Ext.data.DataProxy}.{@link Ext.data.DataProxy#exception exception}
36774              * for description.
36775              */
36776             'exception',
36777             /**
36778              * @event beforeload
36779              * Fires before a request is made for a new data object.  If the beforeload handler returns
36780              * <tt>false</tt> the {@link #load} action will be canceled.
36781              * @param {Store} this
36782              * @param {Object} options The loading options that were specified (see {@link #load} for details)
36783              */
36784             'beforeload',
36785             /**
36786              * @event load
36787              * Fires after a new set of Records has been loaded.
36788              * @param {Store} this
36789              * @param {Ext.data.Record[]} records The Records that were loaded
36790              * @param {Object} options The loading options that were specified (see {@link #load} for details)
36791              */
36792             'load',
36793             /**
36794              * @event loadexception
36795              * <p>This event is <b>deprecated</b> in favor of the catch-all <b><code>{@link #exception}</code></b>
36796              * event instead.</p>
36797              * <p>This event is relayed through the corresponding {@link Ext.data.DataProxy}.
36798              * See {@link Ext.data.DataProxy}.{@link Ext.data.DataProxy#loadexception loadexception}
36799              * for additional details.
36800              * @param {misc} misc See {@link Ext.data.DataProxy}.{@link Ext.data.DataProxy#loadexception loadexception}
36801              * for description.
36802              */
36803             'loadexception',
36804             /**
36805              * @event beforewrite
36806              * @param {Ext.data.Store} store
36807              * @param {String} action [Ext.data.Api.actions.create|update|destroy]
36808              * @param {Record/Record[]} rs The Record(s) being written.
36809              * @param {Object} options The loading options that were specified. Edit <code>options.params</code> to add Http parameters to the request.  (see {@link #save} for details)
36810              * @param {Object} arg The callback's arg object passed to the {@link #request} function
36811              */
36812             'beforewrite',
36813             /**
36814              * @event write
36815              * Fires if the server returns 200 after an Ext.data.Api.actions CRUD action.
36816              * Success of the action is determined in the <code>result['successProperty']</code>property (<b>NOTE</b> for RESTful stores,
36817              * a simple 20x response is sufficient for the actions "destroy" and "update".  The "create" action should should return 200 along with a database pk).
36818              * @param {Ext.data.Store} store
36819              * @param {String} action [Ext.data.Api.actions.create|update|destroy]
36820              * @param {Object} result The 'data' picked-out out of the response for convenience.
36821              * @param {Ext.Direct.Transaction} res
36822              * @param {Record/Record[]} rs Store's records, the subject(s) of the write-action
36823              */
36824             'write',
36825             /**
36826              * @event beforesave
36827              * Fires before a save action is called. A save encompasses destroying records, updating records and creating records.
36828              * @param {Ext.data.Store} store
36829              * @param {Object} data An object containing the data that is to be saved. The object will contain a key for each appropriate action,
36830              * with an array of records for each action.
36831              */
36832             'beforesave',
36833             /**
36834              * @event save
36835              * Fires after a save is completed. A save encompasses destroying records, updating records and creating records.
36836              * @param {Ext.data.Store} store
36837              * @param {Number} batch The identifier for the batch that was saved.
36838              * @param {Object} data An object containing the data that is to be saved. The object will contain a key for each appropriate action,
36839              * with an array of records for each action.
36840              */
36841             'save'
36842
36843         );
36844
36845         if(this.proxy){
36846             // TODO remove deprecated loadexception with ext-3.0.1
36847             this.relayEvents(this.proxy,  ['loadexception', 'exception']);
36848         }
36849         // With a writer set for the Store, we want to listen to add/remove events to remotely create/destroy records.
36850         if (this.writer) {
36851             this.on({
36852                 scope: this,
36853                 add: this.createRecords,
36854                 remove: this.destroyRecord,
36855                 update: this.updateRecord,
36856                 clear: this.onClear
36857             });
36858         }
36859
36860         this.sortToggle = {};
36861         if(this.sortField){
36862             this.setDefaultSort(this.sortField, this.sortDir);
36863         }else if(this.sortInfo){
36864             this.setDefaultSort(this.sortInfo.field, this.sortInfo.direction);
36865         }
36866
36867         Ext.data.Store.superclass.constructor.call(this);
36868
36869         if(this.id){
36870             this.storeId = this.id;
36871             delete this.id;
36872         }
36873         if(this.storeId){
36874             Ext.StoreMgr.register(this);
36875         }
36876         if(this.inlineData){
36877             this.loadData(this.inlineData);
36878             delete this.inlineData;
36879         }else if(this.autoLoad){
36880             this.load.defer(10, this, [
36881                 typeof this.autoLoad == 'object' ?
36882                     this.autoLoad : undefined]);
36883         }
36884         // used internally to uniquely identify a batch
36885         this.batchCounter = 0;
36886         this.batches = {};
36887     },
36888
36889     /**
36890      * builds a DataWriter instance when Store constructor is provided with a writer config-object instead of an instace.
36891      * @param {Object} config Writer configuration
36892      * @return {Ext.data.DataWriter}
36893      * @private
36894      */
36895     buildWriter : function(config) {
36896         var klass = undefined,
36897             type = (config.format || 'json').toLowerCase();
36898         switch (type) {
36899             case 'json':
36900                 klass = Ext.data.JsonWriter;
36901                 break;
36902             case 'xml':
36903                 klass = Ext.data.XmlWriter;
36904                 break;
36905             default:
36906                 klass = Ext.data.JsonWriter;
36907         }
36908         return new klass(config);
36909     },
36910
36911     /**
36912      * Destroys the store.
36913      */
36914     destroy : function(){
36915         if(!this.isDestroyed){
36916             if(this.storeId){
36917                 Ext.StoreMgr.unregister(this);
36918             }
36919             this.clearData();
36920             this.data = null;
36921             Ext.destroy(this.proxy);
36922             this.reader = this.writer = null;
36923             this.purgeListeners();
36924             this.isDestroyed = true;
36925         }
36926     },
36927
36928     /**
36929      * Add Records to the Store and fires the {@link #add} event.  To add Records
36930      * to the store from a remote source use <code>{@link #load}({add:true})</code>.
36931      * See also <code>{@link #recordType}</code> and <code>{@link #insert}</code>.
36932      * @param {Ext.data.Record[]} records An Array of Ext.data.Record objects
36933      * to add to the cache. See {@link #recordType}.
36934      */
36935     add : function(records) {
36936         var i, len, record, index;
36937         
36938         records = [].concat(records);
36939         if (records.length < 1) {
36940             return;
36941         }
36942         
36943         for (i = 0, len = records.length; i < len; i++) {
36944             record = records[i];
36945             
36946             record.join(this);
36947             
36948             if (record.dirty || record.phantom) {
36949                 this.modified.push(record);
36950             }
36951         }
36952         
36953         index = this.data.length;
36954         this.data.addAll(records);
36955         
36956         if (this.snapshot) {
36957             this.snapshot.addAll(records);
36958         }
36959         
36960         this.fireEvent('add', this, records, index);
36961     },
36962
36963     /**
36964      * (Local sort only) Inserts the passed Record into the Store at the index where it
36965      * should go based on the current sort information.
36966      * @param {Ext.data.Record} record
36967      */
36968     addSorted : function(record){
36969         var index = this.findInsertIndex(record);
36970         this.insert(index, record);
36971     },
36972     
36973     /**
36974      * @private
36975      * Update a record within the store with a new reference
36976      */
36977     doUpdate : function(rec){
36978         this.data.replace(rec.id, rec);
36979         if(this.snapshot){
36980             this.snapshot.replace(rec.id, rec);
36981         }
36982         this.fireEvent('update', this, rec, Ext.data.Record.COMMIT);
36983     },
36984
36985     /**
36986      * Remove Records from the Store and fires the {@link #remove} event.
36987      * @param {Ext.data.Record/Ext.data.Record[]} record The record object or array of records to remove from the cache.
36988      */
36989     remove : function(record){
36990         if(Ext.isArray(record)){
36991             Ext.each(record, function(r){
36992                 this.remove(r);
36993             }, this);
36994             return;
36995         }
36996         var index = this.data.indexOf(record);
36997         if(index > -1){
36998             record.join(null);
36999             this.data.removeAt(index);
37000         }
37001         if(this.pruneModifiedRecords){
37002             this.modified.remove(record);
37003         }
37004         if(this.snapshot){
37005             this.snapshot.remove(record);
37006         }
37007         if(index > -1){
37008             this.fireEvent('remove', this, record, index);
37009         }
37010     },
37011
37012     /**
37013      * Remove a Record from the Store at the specified index. Fires the {@link #remove} event.
37014      * @param {Number} index The index of the record to remove.
37015      */
37016     removeAt : function(index){
37017         this.remove(this.getAt(index));
37018     },
37019
37020     /**
37021      * Remove all Records from the Store and fires the {@link #clear} event.
37022      * @param {Boolean} silent [false] Defaults to <tt>false</tt>.  Set <tt>true</tt> to not fire clear event.
37023      */
37024     removeAll : function(silent){
37025         var items = [];
37026         this.each(function(rec){
37027             items.push(rec);
37028         });
37029         this.clearData();
37030         if(this.snapshot){
37031             this.snapshot.clear();
37032         }
37033         if(this.pruneModifiedRecords){
37034             this.modified = [];
37035         }
37036         if (silent !== true) {  // <-- prevents write-actions when we just want to clear a store.
37037             this.fireEvent('clear', this, items);
37038         }
37039     },
37040
37041     // private
37042     onClear: function(store, records){
37043         Ext.each(records, function(rec, index){
37044             this.destroyRecord(this, rec, index);
37045         }, this);
37046     },
37047
37048     /**
37049      * Inserts Records into the Store at the given index and fires the {@link #add} event.
37050      * See also <code>{@link #add}</code> and <code>{@link #addSorted}</code>.
37051      * @param {Number} index The start index at which to insert the passed Records.
37052      * @param {Ext.data.Record[]} records An Array of Ext.data.Record objects to add to the cache.
37053      */
37054     insert : function(index, records) {
37055         var i, len, record;
37056         
37057         records = [].concat(records);
37058         for (i = 0, len = records.length; i < len; i++) {
37059             record = records[i];
37060             
37061             this.data.insert(index + i, record);
37062             record.join(this);
37063             
37064             if (record.dirty || record.phantom) {
37065                 this.modified.push(record);
37066             }
37067         }
37068         
37069         if (this.snapshot) {
37070             this.snapshot.addAll(records);
37071         }
37072         
37073         this.fireEvent('add', this, records, index);
37074     },
37075
37076     /**
37077      * Get the index within the cache of the passed Record.
37078      * @param {Ext.data.Record} record The Ext.data.Record object to find.
37079      * @return {Number} The index of the passed Record. Returns -1 if not found.
37080      */
37081     indexOf : function(record){
37082         return this.data.indexOf(record);
37083     },
37084
37085     /**
37086      * Get the index within the cache of the Record with the passed id.
37087      * @param {String} id The id of the Record to find.
37088      * @return {Number} The index of the Record. Returns -1 if not found.
37089      */
37090     indexOfId : function(id){
37091         return this.data.indexOfKey(id);
37092     },
37093
37094     /**
37095      * Get the Record with the specified id.
37096      * @param {String} id The id of the Record to find.
37097      * @return {Ext.data.Record} The Record with the passed id. Returns undefined if not found.
37098      */
37099     getById : function(id){
37100         return (this.snapshot || this.data).key(id);
37101     },
37102
37103     /**
37104      * Get the Record at the specified index.
37105      * @param {Number} index The index of the Record to find.
37106      * @return {Ext.data.Record} The Record at the passed index. Returns undefined if not found.
37107      */
37108     getAt : function(index){
37109         return this.data.itemAt(index);
37110     },
37111
37112     /**
37113      * Returns a range of Records between specified indices.
37114      * @param {Number} startIndex (optional) The starting index (defaults to 0)
37115      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
37116      * @return {Ext.data.Record[]} An array of Records
37117      */
37118     getRange : function(start, end){
37119         return this.data.getRange(start, end);
37120     },
37121
37122     // private
37123     storeOptions : function(o){
37124         o = Ext.apply({}, o);
37125         delete o.callback;
37126         delete o.scope;
37127         this.lastOptions = o;
37128     },
37129
37130     // private
37131     clearData: function(){
37132         this.data.each(function(rec) {
37133             rec.join(null);
37134         });
37135         this.data.clear();
37136     },
37137
37138     /**
37139      * <p>Loads the Record cache from the configured <tt>{@link #proxy}</tt> using the configured <tt>{@link #reader}</tt>.</p>
37140      * <br><p>Notes:</p><div class="mdetail-params"><ul>
37141      * <li><b><u>Important</u></b>: loading is asynchronous! This call will return before the new data has been
37142      * loaded. To perform any post-processing where information from the load call is required, specify
37143      * the <tt>callback</tt> function to be called, or use a {@link Ext.util.Observable#listeners a 'load' event handler}.</li>
37144      * <li>If using {@link Ext.PagingToolbar remote paging}, the first load call must specify the <tt>start</tt> and <tt>limit</tt>
37145      * properties in the <code>options.params</code> property to establish the initial position within the
37146      * dataset, and the number of Records to cache on each read from the Proxy.</li>
37147      * <li>If using {@link #remoteSort remote sorting}, the configured <code>{@link #sortInfo}</code>
37148      * will be automatically included with the posted parameters according to the specified
37149      * <code>{@link #paramNames}</code>.</li>
37150      * </ul></div>
37151      * @param {Object} options An object containing properties which control loading options:<ul>
37152      * <li><b><tt>params</tt></b> :Object<div class="sub-desc"><p>An object containing properties to pass as HTTP
37153      * parameters to a remote data source. <b>Note</b>: <code>params</code> will override any
37154      * <code>{@link #baseParams}</code> of the same name.</p>
37155      * <p>Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.</p></div></li>
37156      * <li><b>callback</b> : Function<div class="sub-desc"><p>A function to be called after the Records
37157      * have been loaded. The callback is called after the load event is fired, and is passed the following arguments:<ul>
37158      * <li>r : Ext.data.Record[] An Array of Records loaded.</li>
37159      * <li>options : Options object from the load call.</li>
37160      * <li>success : Boolean success indicator.</li></ul></p></div></li>
37161      * <li><b>scope</b> : Object<div class="sub-desc"><p>Scope with which to call the callback (defaults
37162      * to the Store object)</p></div></li>
37163      * <li><b>add</b> : Boolean<div class="sub-desc"><p>Indicator to append loaded records rather than
37164      * replace the current cache.  <b>Note</b>: see note for <tt>{@link #loadData}</tt></p></div></li>
37165      * </ul>
37166      * @return {Boolean} If the <i>developer</i> provided <tt>{@link #beforeload}</tt> event handler returns
37167      * <tt>false</tt>, the load call will abort and will return <tt>false</tt>; otherwise will return <tt>true</tt>.
37168      */
37169     load : function(options) {
37170         options = Ext.apply({}, options);
37171         this.storeOptions(options);
37172         if(this.sortInfo && this.remoteSort){
37173             var pn = this.paramNames;
37174             options.params = Ext.apply({}, options.params);
37175             options.params[pn.sort] = this.sortInfo.field;
37176             options.params[pn.dir] = this.sortInfo.direction;
37177         }
37178         try {
37179             return this.execute('read', null, options); // <-- null represents rs.  No rs for load actions.
37180         } catch(e) {
37181             this.handleException(e);
37182             return false;
37183         }
37184     },
37185
37186     /**
37187      * updateRecord  Should not be used directly.  This method will be called automatically if a Writer is set.
37188      * Listens to 'update' event.
37189      * @param {Object} store
37190      * @param {Object} record
37191      * @param {Object} action
37192      * @private
37193      */
37194     updateRecord : function(store, record, action) {
37195         if (action == Ext.data.Record.EDIT && this.autoSave === true && (!record.phantom || (record.phantom && record.isValid()))) {
37196             this.save();
37197         }
37198     },
37199
37200     /**
37201      * @private
37202      * Should not be used directly.  Store#add will call this automatically if a Writer is set
37203      * @param {Object} store
37204      * @param {Object} records
37205      * @param {Object} index
37206      */
37207     createRecords : function(store, records, index) {
37208         var modified = this.modified,
37209             length   = records.length,
37210             record, i;
37211         
37212         for (i = 0; i < length; i++) {
37213             record = records[i];
37214             
37215             if (record.phantom && record.isValid()) {
37216                 record.markDirty();  // <-- Mark new records dirty (Ed: why?)
37217                 
37218                 if (modified.indexOf(record) == -1) {
37219                     modified.push(record);
37220                 }
37221             }
37222         }
37223         if (this.autoSave === true) {
37224             this.save();
37225         }
37226     },
37227
37228     /**
37229      * Destroys a Record.  Should not be used directly.  It's called by Store#remove if a Writer is set.
37230      * @param {Store} store this
37231      * @param {Ext.data.Record} record
37232      * @param {Number} index
37233      * @private
37234      */
37235     destroyRecord : function(store, record, index) {
37236         if (this.modified.indexOf(record) != -1) {  // <-- handled already if @cfg pruneModifiedRecords == true
37237             this.modified.remove(record);
37238         }
37239         if (!record.phantom) {
37240             this.removed.push(record);
37241
37242             // since the record has already been removed from the store but the server request has not yet been executed,
37243             // must keep track of the last known index this record existed.  If a server error occurs, the record can be
37244             // put back into the store.  @see Store#createCallback where the record is returned when response status === false
37245             record.lastIndex = index;
37246
37247             if (this.autoSave === true) {
37248                 this.save();
37249             }
37250         }
37251     },
37252
37253     /**
37254      * This method should generally not be used directly.  This method is called internally
37255      * by {@link #load}, or if a Writer is set will be called automatically when {@link #add},
37256      * {@link #remove}, or {@link #update} events fire.
37257      * @param {String} action Action name ('read', 'create', 'update', or 'destroy')
37258      * @param {Record/Record[]} rs
37259      * @param {Object} options
37260      * @throws Error
37261      * @private
37262      */
37263     execute : function(action, rs, options, /* private */ batch) {
37264         // blow up if action not Ext.data.CREATE, READ, UPDATE, DESTROY
37265         if (!Ext.data.Api.isAction(action)) {
37266             throw new Ext.data.Api.Error('execute', action);
37267         }
37268         // make sure options has a fresh, new params hash
37269         options = Ext.applyIf(options||{}, {
37270             params: {}
37271         });
37272         if(batch !== undefined){
37273             this.addToBatch(batch);
37274         }
37275         // have to separate before-events since load has a different signature than create,destroy and save events since load does not
37276         // include the rs (record resultset) parameter.  Capture return values from the beforeaction into doRequest flag.
37277         var doRequest = true;
37278
37279         if (action === 'read') {
37280             doRequest = this.fireEvent('beforeload', this, options);
37281             Ext.applyIf(options.params, this.baseParams);
37282         }
37283         else {
37284             // if Writer is configured as listful, force single-record rs to be [{}] instead of {}
37285             // TODO Move listful rendering into DataWriter where the @cfg is defined.  Should be easy now.
37286             if (this.writer.listful === true && this.restful !== true) {
37287                 rs = (Ext.isArray(rs)) ? rs : [rs];
37288             }
37289             // if rs has just a single record, shift it off so that Writer writes data as '{}' rather than '[{}]'
37290             else if (Ext.isArray(rs) && rs.length == 1) {
37291                 rs = rs.shift();
37292             }
37293             // Write the action to options.params
37294             if ((doRequest = this.fireEvent('beforewrite', this, action, rs, options)) !== false) {
37295                 this.writer.apply(options.params, this.baseParams, action, rs);
37296             }
37297         }
37298         if (doRequest !== false) {
37299             // Send request to proxy.
37300             if (this.writer && this.proxy.url && !this.proxy.restful && !Ext.data.Api.hasUniqueUrl(this.proxy, action)) {
37301                 options.params.xaction = action;    // <-- really old, probaby unecessary.
37302             }
37303             // Note:  Up until this point we've been dealing with 'action' as a key from Ext.data.Api.actions.
37304             // We'll flip it now and send the value into DataProxy#request, since it's the value which maps to
37305             // the user's configured DataProxy#api
37306             // TODO Refactor all Proxies to accept an instance of Ext.data.Request (not yet defined) instead of this looooooong list
37307             // of params.  This method is an artifact from Ext2.
37308             this.proxy.request(Ext.data.Api.actions[action], rs, options.params, this.reader, this.createCallback(action, rs, batch), this, options);
37309         }
37310         return doRequest;
37311     },
37312
37313     /**
37314      * Saves all pending changes to the store.  If the commensurate Ext.data.Api.actions action is not configured, then
37315      * the configured <code>{@link #url}</code> will be used.
37316      * <pre>
37317      * change            url
37318      * ---------------   --------------------
37319      * removed records   Ext.data.Api.actions.destroy
37320      * phantom records   Ext.data.Api.actions.create
37321      * {@link #getModifiedRecords modified records}  Ext.data.Api.actions.update
37322      * </pre>
37323      * @TODO:  Create extensions of Error class and send associated Record with thrown exceptions.
37324      * e.g.:  Ext.data.DataReader.Error or Ext.data.Error or Ext.data.DataProxy.Error, etc.
37325      * @return {Number} batch Returns a number to uniquely identify the "batch" of saves occurring. -1 will be returned
37326      * if there are no items to save or the save was cancelled.
37327      */
37328     save : function() {
37329         if (!this.writer) {
37330             throw new Ext.data.Store.Error('writer-undefined');
37331         }
37332
37333         var queue = [],
37334             len,
37335             trans,
37336             batch,
37337             data = {},
37338             i;
37339         // DESTROY:  First check for removed records.  Records in this.removed are guaranteed non-phantoms.  @see Store#remove
37340         if(this.removed.length){
37341             queue.push(['destroy', this.removed]);
37342         }
37343
37344         // Check for modified records. Use a copy so Store#rejectChanges will work if server returns error.
37345         var rs = [].concat(this.getModifiedRecords());
37346         if(rs.length){
37347             // CREATE:  Next check for phantoms within rs.  splice-off and execute create.
37348             var phantoms = [];
37349             for(i = rs.length-1; i >= 0; i--){
37350                 if(rs[i].phantom === true){
37351                     var rec = rs.splice(i, 1).shift();
37352                     if(rec.isValid()){
37353                         phantoms.push(rec);
37354                     }
37355                 }else if(!rs[i].isValid()){ // <-- while we're here, splice-off any !isValid real records
37356                     rs.splice(i,1);
37357                 }
37358             }
37359             // If we have valid phantoms, create them...
37360             if(phantoms.length){
37361                 queue.push(['create', phantoms]);
37362             }
37363
37364             // UPDATE:  And finally, if we're still here after splicing-off phantoms and !isValid real records, update the rest...
37365             if(rs.length){
37366                 queue.push(['update', rs]);
37367             }
37368         }
37369         len = queue.length;
37370         if(len){
37371             batch = ++this.batchCounter;
37372             for(i = 0; i < len; ++i){
37373                 trans = queue[i];
37374                 data[trans[0]] = trans[1];
37375             }
37376             if(this.fireEvent('beforesave', this, data) !== false){
37377                 for(i = 0; i < len; ++i){
37378                     trans = queue[i];
37379                     this.doTransaction(trans[0], trans[1], batch);
37380                 }
37381                 return batch;
37382             }
37383         }
37384         return -1;
37385     },
37386
37387     // private.  Simply wraps call to Store#execute in try/catch.  Defers to Store#handleException on error.  Loops if batch: false
37388     doTransaction : function(action, rs, batch) {
37389         function transaction(records) {
37390             try{
37391                 this.execute(action, records, undefined, batch);
37392             }catch (e){
37393                 this.handleException(e);
37394             }
37395         }
37396         if(this.batch === false){
37397             for(var i = 0, len = rs.length; i < len; i++){
37398                 transaction.call(this, rs[i]);
37399             }
37400         }else{
37401             transaction.call(this, rs);
37402         }
37403     },
37404
37405     // private
37406     addToBatch : function(batch){
37407         var b = this.batches,
37408             key = this.batchKey + batch,
37409             o = b[key];
37410
37411         if(!o){
37412             b[key] = o = {
37413                 id: batch,
37414                 count: 0,
37415                 data: {}
37416             };
37417         }
37418         ++o.count;
37419     },
37420
37421     removeFromBatch : function(batch, action, data){
37422         var b = this.batches,
37423             key = this.batchKey + batch,
37424             o = b[key],
37425             arr;
37426
37427
37428         if(o){
37429             arr = o.data[action] || [];
37430             o.data[action] = arr.concat(data);
37431             if(o.count === 1){
37432                 data = o.data;
37433                 delete b[key];
37434                 this.fireEvent('save', this, batch, data);
37435             }else{
37436                 --o.count;
37437             }
37438         }
37439     },
37440
37441     // @private callback-handler for remote CRUD actions
37442     // Do not override -- override loadRecords, onCreateRecords, onDestroyRecords and onUpdateRecords instead.
37443     createCallback : function(action, rs, batch) {
37444         var actions = Ext.data.Api.actions;
37445         return (action == 'read') ? this.loadRecords : function(data, response, success) {
37446             // calls: onCreateRecords | onUpdateRecords | onDestroyRecords
37447             this['on' + Ext.util.Format.capitalize(action) + 'Records'](success, rs, [].concat(data));
37448             // If success === false here, exception will have been called in DataProxy
37449             if (success === true) {
37450                 this.fireEvent('write', this, action, data, response, rs);
37451             }
37452             this.removeFromBatch(batch, action, data);
37453         };
37454     },
37455
37456     // Clears records from modified array after an exception event.
37457     // NOTE:  records are left marked dirty.  Do we want to commit them even though they were not updated/realized?
37458     // TODO remove this method?
37459     clearModified : function(rs) {
37460         if (Ext.isArray(rs)) {
37461             for (var n=rs.length-1;n>=0;n--) {
37462                 this.modified.splice(this.modified.indexOf(rs[n]), 1);
37463             }
37464         } else {
37465             this.modified.splice(this.modified.indexOf(rs), 1);
37466         }
37467     },
37468
37469     // remap record ids in MixedCollection after records have been realized.  @see Store#onCreateRecords, @see DataReader#realize
37470     reMap : function(record) {
37471         if (Ext.isArray(record)) {
37472             for (var i = 0, len = record.length; i < len; i++) {
37473                 this.reMap(record[i]);
37474             }
37475         } else {
37476             delete this.data.map[record._phid];
37477             this.data.map[record.id] = record;
37478             var index = this.data.keys.indexOf(record._phid);
37479             this.data.keys.splice(index, 1, record.id);
37480             delete record._phid;
37481         }
37482     },
37483
37484     // @protected onCreateRecord proxy callback for create action
37485     onCreateRecords : function(success, rs, data) {
37486         if (success === true) {
37487             try {
37488                 this.reader.realize(rs, data);
37489                 this.reMap(rs);
37490             }
37491             catch (e) {
37492                 this.handleException(e);
37493                 if (Ext.isArray(rs)) {
37494                     // Recurse to run back into the try {}.  DataReader#realize splices-off the rs until empty.
37495                     this.onCreateRecords(success, rs, data);
37496                 }
37497             }
37498         }
37499     },
37500
37501     // @protected, onUpdateRecords proxy callback for update action
37502     onUpdateRecords : function(success, rs, data) {
37503         if (success === true) {
37504             try {
37505                 this.reader.update(rs, data);
37506             } catch (e) {
37507                 this.handleException(e);
37508                 if (Ext.isArray(rs)) {
37509                     // Recurse to run back into the try {}.  DataReader#update splices-off the rs until empty.
37510                     this.onUpdateRecords(success, rs, data);
37511                 }
37512             }
37513         }
37514     },
37515
37516     // @protected onDestroyRecords proxy callback for destroy action
37517     onDestroyRecords : function(success, rs, data) {
37518         // splice each rec out of this.removed
37519         rs = (rs instanceof Ext.data.Record) ? [rs] : [].concat(rs);
37520         for (var i=0,len=rs.length;i<len;i++) {
37521             this.removed.splice(this.removed.indexOf(rs[i]), 1);
37522         }
37523         if (success === false) {
37524             // put records back into store if remote destroy fails.
37525             // @TODO: Might want to let developer decide.
37526             for (i=rs.length-1;i>=0;i--) {
37527                 this.insert(rs[i].lastIndex, rs[i]);    // <-- lastIndex set in Store#destroyRecord
37528             }
37529         }
37530     },
37531
37532     // protected handleException.  Possibly temporary until Ext framework has an exception-handler.
37533     handleException : function(e) {
37534         // @see core/Error.js
37535         Ext.handleError(e);
37536     },
37537
37538     /**
37539      * <p>Reloads the Record cache from the configured Proxy using the configured
37540      * {@link Ext.data.Reader Reader} and the options from the last load operation
37541      * performed.</p>
37542      * <p><b>Note</b>: see the Important note in {@link #load}.</p>
37543      * @param {Object} options <p>(optional) An <tt>Object</tt> containing
37544      * {@link #load loading options} which may override the {@link #lastOptions options}
37545      * used in the last {@link #load} operation. See {@link #load} for details
37546      * (defaults to <tt>null</tt>, in which case the {@link #lastOptions} are
37547      * used).</p>
37548      * <br><p>To add new params to the existing params:</p><pre><code>
37549 lastOptions = myStore.lastOptions;
37550 Ext.apply(lastOptions.params, {
37551     myNewParam: true
37552 });
37553 myStore.reload(lastOptions);
37554      * </code></pre>
37555      */
37556     reload : function(options){
37557         this.load(Ext.applyIf(options||{}, this.lastOptions));
37558     },
37559
37560     // private
37561     // Called as a callback by the Reader during a load operation.
37562     loadRecords : function(o, options, success){
37563         var i, len;
37564         
37565         if (this.isDestroyed === true) {
37566             return;
37567         }
37568         if(!o || success === false){
37569             if(success !== false){
37570                 this.fireEvent('load', this, [], options);
37571             }
37572             if(options.callback){
37573                 options.callback.call(options.scope || this, [], options, false, o);
37574             }
37575             return;
37576         }
37577         var r = o.records, t = o.totalRecords || r.length;
37578         if(!options || options.add !== true){
37579             if(this.pruneModifiedRecords){
37580                 this.modified = [];
37581             }
37582             for(i = 0, len = r.length; i < len; i++){
37583                 r[i].join(this);
37584             }
37585             if(this.snapshot){
37586                 this.data = this.snapshot;
37587                 delete this.snapshot;
37588             }
37589             this.clearData();
37590             this.data.addAll(r);
37591             this.totalLength = t;
37592             this.applySort();
37593             this.fireEvent('datachanged', this);
37594         }else{
37595             var toAdd = [],
37596                 rec,
37597                 cnt = 0;
37598             for(i = 0, len = r.length; i < len; ++i){
37599                 rec = r[i];
37600                 if(this.indexOfId(rec.id) > -1){
37601                     this.doUpdate(rec);
37602                 }else{
37603                     toAdd.push(rec);
37604                     ++cnt;
37605                 }
37606             }
37607             this.totalLength = Math.max(t, this.data.length + cnt);
37608             this.add(toAdd);
37609         }
37610         this.fireEvent('load', this, r, options);
37611         if(options.callback){
37612             options.callback.call(options.scope || this, r, options, true);
37613         }
37614     },
37615
37616     /**
37617      * Loads data from a passed data block and fires the {@link #load} event. A {@link Ext.data.Reader Reader}
37618      * which understands the format of the data must have been configured in the constructor.
37619      * @param {Object} data The data block from which to read the Records.  The format of the data expected
37620      * is dependent on the type of {@link Ext.data.Reader Reader} that is configured and should correspond to
37621      * that {@link Ext.data.Reader Reader}'s <tt>{@link Ext.data.Reader#readRecords}</tt> parameter.
37622      * @param {Boolean} append (Optional) <tt>true</tt> to append the new Records rather the default to replace
37623      * the existing cache.
37624      * <b>Note</b>: that Records in a Store are keyed by their {@link Ext.data.Record#id id}, so added Records
37625      * with ids which are already present in the Store will <i>replace</i> existing Records. Only Records with
37626      * new, unique ids will be added.
37627      */
37628     loadData : function(o, append){
37629         var r = this.reader.readRecords(o);
37630         this.loadRecords(r, {add: append}, true);
37631     },
37632
37633     /**
37634      * Gets the number of cached records.
37635      * <p>If using paging, this may not be the total size of the dataset. If the data object
37636      * used by the Reader contains the dataset size, then the {@link #getTotalCount} function returns
37637      * the dataset size.  <b>Note</b>: see the Important note in {@link #load}.</p>
37638      * @return {Number} The number of Records in the Store's cache.
37639      */
37640     getCount : function(){
37641         return this.data.length || 0;
37642     },
37643
37644     /**
37645      * Gets the total number of records in the dataset as returned by the server.
37646      * <p>If using paging, for this to be accurate, the data object used by the {@link #reader Reader}
37647      * must contain the dataset size. For remote data sources, the value for this property
37648      * (<tt>totalProperty</tt> for {@link Ext.data.JsonReader JsonReader},
37649      * <tt>totalRecords</tt> for {@link Ext.data.XmlReader XmlReader}) shall be returned by a query on the server.
37650      * <b>Note</b>: see the Important note in {@link #load}.</p>
37651      * @return {Number} The number of Records as specified in the data object passed to the Reader
37652      * by the Proxy.
37653      * <p><b>Note</b>: this value is not updated when changing the contents of the Store locally.</p>
37654      */
37655     getTotalCount : function(){
37656         return this.totalLength || 0;
37657     },
37658
37659     /**
37660      * Returns an object describing the current sort state of this Store.
37661      * @return {Object} The sort state of the Store. An object with two properties:<ul>
37662      * <li><b>field : String<p class="sub-desc">The name of the field by which the Records are sorted.</p></li>
37663      * <li><b>direction : String<p class="sub-desc">The sort order, 'ASC' or 'DESC' (case-sensitive).</p></li>
37664      * </ul>
37665      * See <tt>{@link #sortInfo}</tt> for additional details.
37666      */
37667     getSortState : function(){
37668         return this.sortInfo;
37669     },
37670
37671     /**
37672      * @private
37673      * Invokes sortData if we have sortInfo to sort on and are not sorting remotely
37674      */
37675     applySort : function(){
37676         if ((this.sortInfo || this.multiSortInfo) && !this.remoteSort) {
37677             this.sortData();
37678         }
37679     },
37680
37681     /**
37682      * @private
37683      * Performs the actual sorting of data. This checks to see if we currently have a multi sort or not. It applies
37684      * each sorter field/direction pair in turn by building an OR'ed master sorting function and running it against
37685      * the full dataset
37686      */
37687     sortData : function() {
37688         var sortInfo  = this.hasMultiSort ? this.multiSortInfo : this.sortInfo,
37689             direction = sortInfo.direction || "ASC",
37690             sorters   = sortInfo.sorters,
37691             sortFns   = [];
37692
37693         //if we just have a single sorter, pretend it's the first in an array
37694         if (!this.hasMultiSort) {
37695             sorters = [{direction: direction, field: sortInfo.field}];
37696         }
37697
37698         //create a sorter function for each sorter field/direction combo
37699         for (var i=0, j = sorters.length; i < j; i++) {
37700             sortFns.push(this.createSortFunction(sorters[i].field, sorters[i].direction));
37701         }
37702         
37703         if (sortFns.length == 0) {
37704             return;
37705         }
37706
37707         //the direction modifier is multiplied with the result of the sorting functions to provide overall sort direction
37708         //(as opposed to direction per field)
37709         var directionModifier = direction.toUpperCase() == "DESC" ? -1 : 1;
37710
37711         //create a function which ORs each sorter together to enable multi-sort
37712         var fn = function(r1, r2) {
37713           var result = sortFns[0].call(this, r1, r2);
37714
37715           //if we have more than one sorter, OR any additional sorter functions together
37716           if (sortFns.length > 1) {
37717               for (var i=1, j = sortFns.length; i < j; i++) {
37718                   result = result || sortFns[i].call(this, r1, r2);
37719               }
37720           }
37721
37722           return directionModifier * result;
37723         };
37724
37725         //sort the data
37726         this.data.sort(direction, fn);
37727         if (this.snapshot && this.snapshot != this.data) {
37728             this.snapshot.sort(direction, fn);
37729         }
37730     },
37731
37732     /**
37733      * @private
37734      * Creates and returns a function which sorts an array by the given field and direction
37735      * @param {String} field The field to create the sorter for
37736      * @param {String} direction The direction to sort by (defaults to "ASC")
37737      * @return {Function} A function which sorts by the field/direction combination provided
37738      */
37739     createSortFunction: function(field, direction) {
37740         direction = direction || "ASC";
37741         var directionModifier = direction.toUpperCase() == "DESC" ? -1 : 1;
37742
37743         var sortType = this.fields.get(field).sortType;
37744
37745         //create a comparison function. Takes 2 records, returns 1 if record 1 is greater,
37746         //-1 if record 2 is greater or 0 if they are equal
37747         return function(r1, r2) {
37748             var v1 = sortType(r1.data[field]),
37749                 v2 = sortType(r2.data[field]);
37750
37751             return directionModifier * (v1 > v2 ? 1 : (v1 < v2 ? -1 : 0));
37752         };
37753     },
37754
37755     /**
37756      * Sets the default sort column and order to be used by the next {@link #load} operation.
37757      * @param {String} fieldName The name of the field to sort by.
37758      * @param {String} dir (optional) The sort order, 'ASC' or 'DESC' (case-sensitive, defaults to <tt>'ASC'</tt>)
37759      */
37760     setDefaultSort : function(field, dir) {
37761         dir = dir ? dir.toUpperCase() : 'ASC';
37762         this.sortInfo = {field: field, direction: dir};
37763         this.sortToggle[field] = dir;
37764     },
37765
37766     /**
37767      * Sort the Records.
37768      * If remote sorting is used, the sort is performed on the server, and the cache is reloaded. If local
37769      * sorting is used, the cache is sorted internally. See also {@link #remoteSort} and {@link #paramNames}.
37770      * This function accepts two call signatures - pass in a field name as the first argument to sort on a single
37771      * field, or pass in an array of sort configuration objects to sort by multiple fields.
37772      * Single sort example:
37773      * store.sort('name', 'ASC');
37774      * Multi sort example:
37775      * store.sort([
37776      *   {
37777      *     field    : 'name',
37778      *     direction: 'ASC'
37779      *   },
37780      *   {
37781      *     field    : 'salary',
37782      *     direction: 'DESC'
37783      *   }
37784      * ], 'ASC');
37785      * In this second form, the sort configs are applied in order, with later sorters sorting within earlier sorters' results.
37786      * For example, if two records with the same name are present they will also be sorted by salary if given the sort configs
37787      * above. Any number of sort configs can be added.
37788      * @param {String/Array} fieldName The name of the field to sort by, or an array of ordered sort configs
37789      * @param {String} dir (optional) The sort order, 'ASC' or 'DESC' (case-sensitive, defaults to <tt>'ASC'</tt>)
37790      */
37791     sort : function(fieldName, dir) {
37792         if (Ext.isArray(arguments[0])) {
37793             return this.multiSort.call(this, fieldName, dir);
37794         } else {
37795             return this.singleSort(fieldName, dir);
37796         }
37797     },
37798
37799     /**
37800      * Sorts the store contents by a single field and direction. This is called internally by {@link sort} and would
37801      * not usually be called manually
37802      * @param {String} fieldName The name of the field to sort by.
37803      * @param {String} dir (optional) The sort order, 'ASC' or 'DESC' (case-sensitive, defaults to <tt>'ASC'</tt>)
37804      */
37805     singleSort: function(fieldName, dir) {
37806         var field = this.fields.get(fieldName);
37807         if (!field) {
37808             return false;
37809         }
37810
37811         var name       = field.name,
37812             sortInfo   = this.sortInfo || null,
37813             sortToggle = this.sortToggle ? this.sortToggle[name] : null;
37814
37815         if (!dir) {
37816             if (sortInfo && sortInfo.field == name) { // toggle sort dir
37817                 dir = (this.sortToggle[name] || 'ASC').toggle('ASC', 'DESC');
37818             } else {
37819                 dir = field.sortDir;
37820             }
37821         }
37822
37823         this.sortToggle[name] = dir;
37824         this.sortInfo = {field: name, direction: dir};
37825         this.hasMultiSort = false;
37826
37827         if (this.remoteSort) {
37828             if (!this.load(this.lastOptions)) {
37829                 if (sortToggle) {
37830                     this.sortToggle[name] = sortToggle;
37831                 }
37832                 if (sortInfo) {
37833                     this.sortInfo = sortInfo;
37834                 }
37835             }
37836         } else {
37837             this.applySort();
37838             this.fireEvent('datachanged', this);
37839         }
37840         return true;
37841     },
37842
37843     /**
37844      * Sorts the contents of this store by multiple field/direction sorters. This is called internally by {@link sort}
37845      * and would not usually be called manually.
37846      * Multi sorting only currently applies to local datasets - multiple sort data is not currently sent to a proxy
37847      * if remoteSort is used.
37848      * @param {Array} sorters Array of sorter objects (field and direction)
37849      * @param {String} direction Overall direction to sort the ordered results by (defaults to "ASC")
37850      */
37851     multiSort: function(sorters, direction) {
37852         this.hasMultiSort = true;
37853         direction = direction || "ASC";
37854
37855         //toggle sort direction
37856         if (this.multiSortInfo && direction == this.multiSortInfo.direction) {
37857             direction = direction.toggle("ASC", "DESC");
37858         }
37859
37860         /**
37861          * Object containing overall sort direction and an ordered array of sorter configs used when sorting on multiple fields
37862          * @property multiSortInfo
37863          * @type Object
37864          */
37865         this.multiSortInfo = {
37866             sorters  : sorters,
37867             direction: direction
37868         };
37869         
37870         if (this.remoteSort) {
37871             this.singleSort(sorters[0].field, sorters[0].direction);
37872
37873         } else {
37874             this.applySort();
37875             this.fireEvent('datachanged', this);
37876         }
37877     },
37878
37879     /**
37880      * Calls the specified function for each of the {@link Ext.data.Record Records} in the cache.
37881      * @param {Function} fn The function to call. The {@link Ext.data.Record Record} is passed as the first parameter.
37882      * Returning <tt>false</tt> aborts and exits the iteration.
37883      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed.
37884      * Defaults to the current {@link Ext.data.Record Record} in the iteration.
37885      */
37886     each : function(fn, scope){
37887         this.data.each(fn, scope);
37888     },
37889
37890     /**
37891      * Gets all {@link Ext.data.Record records} modified since the last commit.  Modified records are
37892      * persisted across load operations (e.g., during paging). <b>Note</b>: deleted records are not
37893      * included.  See also <tt>{@link #pruneModifiedRecords}</tt> and
37894      * {@link Ext.data.Record}<tt>{@link Ext.data.Record#markDirty markDirty}.</tt>.
37895      * @return {Ext.data.Record[]} An array of {@link Ext.data.Record Records} containing outstanding
37896      * modifications.  To obtain modified fields within a modified record see
37897      *{@link Ext.data.Record}<tt>{@link Ext.data.Record#modified modified}.</tt>.
37898      */
37899     getModifiedRecords : function(){
37900         return this.modified;
37901     },
37902
37903     /**
37904      * Sums the value of <tt>property</tt> for each {@link Ext.data.Record record} between <tt>start</tt>
37905      * and <tt>end</tt> and returns the result.
37906      * @param {String} property A field in each record
37907      * @param {Number} start (optional) The record index to start at (defaults to <tt>0</tt>)
37908      * @param {Number} end (optional) The last record index to include (defaults to length - 1)
37909      * @return {Number} The sum
37910      */
37911     sum : function(property, start, end){
37912         var rs = this.data.items, v = 0;
37913         start = start || 0;
37914         end = (end || end === 0) ? end : rs.length-1;
37915
37916         for(var i = start; i <= end; i++){
37917             v += (rs[i].data[property] || 0);
37918         }
37919         return v;
37920     },
37921
37922     /**
37923      * @private
37924      * Returns a filter function used to test a the given property's value. Defers most of the work to
37925      * Ext.util.MixedCollection's createValueMatcher function
37926      * @param {String} property The property to create the filter function for
37927      * @param {String/RegExp} value The string/regex to compare the property value to
37928      * @param {Boolean} anyMatch True if we don't care if the filter value is not the full value (defaults to false)
37929      * @param {Boolean} caseSensitive True to create a case-sensitive regex (defaults to false)
37930      * @param {Boolean} exactMatch True to force exact match (^ and $ characters added to the regex). Defaults to false. Ignored if anyMatch is true.
37931      */
37932     createFilterFn : function(property, value, anyMatch, caseSensitive, exactMatch){
37933         if(Ext.isEmpty(value, false)){
37934             return false;
37935         }
37936         value = this.data.createValueMatcher(value, anyMatch, caseSensitive, exactMatch);
37937         return function(r) {
37938             return value.test(r.data[property]);
37939         };
37940     },
37941
37942     /**
37943      * @private
37944      * Given an array of filter functions (each with optional scope), constructs and returns a single function that returns
37945      * the result of all of the filters ANDed together
37946      * @param {Array} filters The array of filter objects (each object should contain an 'fn' and optional scope)
37947      * @return {Function} The multiple filter function
37948      */
37949     createMultipleFilterFn: function(filters) {
37950         return function(record) {
37951             var isMatch = true;
37952
37953             for (var i=0, j = filters.length; i < j; i++) {
37954                 var filter = filters[i],
37955                     fn     = filter.fn,
37956                     scope  = filter.scope;
37957
37958                 isMatch = isMatch && fn.call(scope, record);
37959             }
37960
37961             return isMatch;
37962         };
37963     },
37964
37965     /**
37966      * Filter the {@link Ext.data.Record records} by a specified property. Alternatively, pass an array of filter
37967      * options to filter by more than one property.
37968      * Single filter example:
37969      * store.filter('name', 'Ed', true, true); //finds all records containing the substring 'Ed'
37970      * Multiple filter example:
37971      * <pre><code>
37972      * store.filter([
37973      *   {
37974      *     property     : 'name',
37975      *     value        : 'Ed',
37976      *     anyMatch     : true, //optional, defaults to true
37977      *     caseSensitive: true  //optional, defaults to true
37978      *   },
37979      *
37980      *   //filter functions can also be passed
37981      *   {
37982      *     fn   : function(record) {
37983      *       return record.get('age') == 24
37984      *     },
37985      *     scope: this
37986      *   }
37987      * ]);
37988      * </code></pre>
37989      * @param {String|Array} field A field on your records, or an array containing multiple filter options
37990      * @param {String/RegExp} value Either a string that the field should begin with, or a RegExp to test
37991      * against the field.
37992      * @param {Boolean} anyMatch (optional) <tt>true</tt> to match any part not just the beginning
37993      * @param {Boolean} caseSensitive (optional) <tt>true</tt> for case sensitive comparison
37994      * @param {Boolean} exactMatch True to force exact match (^ and $ characters added to the regex). Defaults to false. Ignored if anyMatch is true.
37995      */
37996     filter : function(property, value, anyMatch, caseSensitive, exactMatch){
37997         var fn;
37998         //we can accept an array of filter objects, or a single filter object - normalize them here
37999         if (Ext.isObject(property)) {
38000             property = [property];
38001         }
38002
38003         if (Ext.isArray(property)) {
38004             var filters = [];
38005
38006             //normalize the filters passed into an array of filter functions
38007             for (var i=0, j = property.length; i < j; i++) {
38008                 var filter = property[i],
38009                     func   = filter.fn,
38010                     scope  = filter.scope || this;
38011
38012                 //if we weren't given a filter function, construct one now
38013                 if (!Ext.isFunction(func)) {
38014                     func = this.createFilterFn(filter.property, filter.value, filter.anyMatch, filter.caseSensitive, filter.exactMatch);
38015                 }
38016
38017                 filters.push({fn: func, scope: scope});
38018             }
38019
38020             fn = this.createMultipleFilterFn(filters);
38021         } else {
38022             //classic single property filter
38023             fn = this.createFilterFn(property, value, anyMatch, caseSensitive, exactMatch);
38024         }
38025
38026         return fn ? this.filterBy(fn) : this.clearFilter();
38027     },
38028
38029     /**
38030      * Filter by a function. The specified function will be called for each
38031      * Record in this Store. If the function returns <tt>true</tt> the Record is included,
38032      * otherwise it is filtered out.
38033      * @param {Function} fn The function to be called. It will be passed the following parameters:<ul>
38034      * <li><b>record</b> : Ext.data.Record<p class="sub-desc">The {@link Ext.data.Record record}
38035      * to test for filtering. Access field values using {@link Ext.data.Record#get}.</p></li>
38036      * <li><b>id</b> : Object<p class="sub-desc">The ID of the Record passed.</p></li>
38037      * </ul>
38038      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this Store.
38039      */
38040     filterBy : function(fn, scope){
38041         this.snapshot = this.snapshot || this.data;
38042         this.data = this.queryBy(fn, scope || this);
38043         this.fireEvent('datachanged', this);
38044     },
38045
38046     /**
38047      * Revert to a view of the Record cache with no filtering applied.
38048      * @param {Boolean} suppressEvent If <tt>true</tt> the filter is cleared silently without firing the
38049      * {@link #datachanged} event.
38050      */
38051     clearFilter : function(suppressEvent){
38052         if(this.isFiltered()){
38053             this.data = this.snapshot;
38054             delete this.snapshot;
38055             if(suppressEvent !== true){
38056                 this.fireEvent('datachanged', this);
38057             }
38058         }
38059     },
38060
38061     /**
38062      * Returns true if this store is currently filtered
38063      * @return {Boolean}
38064      */
38065     isFiltered : function(){
38066         return !!this.snapshot && this.snapshot != this.data;
38067     },
38068
38069     /**
38070      * Query the records by a specified property.
38071      * @param {String} field A field on your records
38072      * @param {String/RegExp} value Either a string that the field
38073      * should begin with, or a RegExp to test against the field.
38074      * @param {Boolean} anyMatch (optional) True to match any part not just the beginning
38075      * @param {Boolean} caseSensitive (optional) True for case sensitive comparison
38076      * @return {MixedCollection} Returns an Ext.util.MixedCollection of the matched records
38077      */
38078     query : function(property, value, anyMatch, caseSensitive){
38079         var fn = this.createFilterFn(property, value, anyMatch, caseSensitive);
38080         return fn ? this.queryBy(fn) : this.data.clone();
38081     },
38082
38083     /**
38084      * Query the cached records in this Store using a filtering function. The specified function
38085      * will be called with each record in this Store. If the function returns <tt>true</tt> the record is
38086      * included in the results.
38087      * @param {Function} fn The function to be called. It will be passed the following parameters:<ul>
38088      * <li><b>record</b> : Ext.data.Record<p class="sub-desc">The {@link Ext.data.Record record}
38089      * to test for filtering. Access field values using {@link Ext.data.Record#get}.</p></li>
38090      * <li><b>id</b> : Object<p class="sub-desc">The ID of the Record passed.</p></li>
38091      * </ul>
38092      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this Store.
38093      * @return {MixedCollection} Returns an Ext.util.MixedCollection of the matched records
38094      **/
38095     queryBy : function(fn, scope){
38096         var data = this.snapshot || this.data;
38097         return data.filterBy(fn, scope||this);
38098     },
38099
38100     /**
38101      * Finds the index of the first matching Record in this store by a specific field value.
38102      * @param {String} fieldName The name of the Record field to test.
38103      * @param {String/RegExp} value Either a string that the field value
38104      * should begin with, or a RegExp to test against the field.
38105      * @param {Number} startIndex (optional) The index to start searching at
38106      * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning
38107      * @param {Boolean} caseSensitive (optional) True for case sensitive comparison
38108      * @return {Number} The matched index or -1
38109      */
38110     find : function(property, value, start, anyMatch, caseSensitive){
38111         var fn = this.createFilterFn(property, value, anyMatch, caseSensitive);
38112         return fn ? this.data.findIndexBy(fn, null, start) : -1;
38113     },
38114
38115     /**
38116      * Finds the index of the first matching Record in this store by a specific field value.
38117      * @param {String} fieldName The name of the Record field to test.
38118      * @param {Mixed} value The value to match the field against.
38119      * @param {Number} startIndex (optional) The index to start searching at
38120      * @return {Number} The matched index or -1
38121      */
38122     findExact: function(property, value, start){
38123         return this.data.findIndexBy(function(rec){
38124             return rec.get(property) === value;
38125         }, this, start);
38126     },
38127
38128     /**
38129      * Find the index of the first matching Record in this Store by a function.
38130      * If the function returns <tt>true</tt> it is considered a match.
38131      * @param {Function} fn The function to be called. It will be passed the following parameters:<ul>
38132      * <li><b>record</b> : Ext.data.Record<p class="sub-desc">The {@link Ext.data.Record record}
38133      * to test for filtering. Access field values using {@link Ext.data.Record#get}.</p></li>
38134      * <li><b>id</b> : Object<p class="sub-desc">The ID of the Record passed.</p></li>
38135      * </ul>
38136      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this Store.
38137      * @param {Number} startIndex (optional) The index to start searching at
38138      * @return {Number} The matched index or -1
38139      */
38140     findBy : function(fn, scope, start){
38141         return this.data.findIndexBy(fn, scope, start);
38142     },
38143
38144     /**
38145      * Collects unique values for a particular dataIndex from this store.
38146      * @param {String} dataIndex The property to collect
38147      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
38148      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
38149      * @return {Array} An array of the unique values
38150      **/
38151     collect : function(dataIndex, allowNull, bypassFilter){
38152         var d = (bypassFilter === true && this.snapshot) ?
38153                 this.snapshot.items : this.data.items;
38154         var v, sv, r = [], l = {};
38155         for(var i = 0, len = d.length; i < len; i++){
38156             v = d[i].data[dataIndex];
38157             sv = String(v);
38158             if((allowNull || !Ext.isEmpty(v)) && !l[sv]){
38159                 l[sv] = true;
38160                 r[r.length] = v;
38161             }
38162         }
38163         return r;
38164     },
38165
38166     // private
38167     afterEdit : function(record){
38168         if(this.modified.indexOf(record) == -1){
38169             this.modified.push(record);
38170         }
38171         this.fireEvent('update', this, record, Ext.data.Record.EDIT);
38172     },
38173
38174     // private
38175     afterReject : function(record){
38176         this.modified.remove(record);
38177         this.fireEvent('update', this, record, Ext.data.Record.REJECT);
38178     },
38179
38180     // private
38181     afterCommit : function(record){
38182         this.modified.remove(record);
38183         this.fireEvent('update', this, record, Ext.data.Record.COMMIT);
38184     },
38185
38186     /**
38187      * Commit all Records with {@link #getModifiedRecords outstanding changes}. To handle updates for changes,
38188      * subscribe to the Store's {@link #update update event}, and perform updating when the third parameter is
38189      * Ext.data.Record.COMMIT.
38190      */
38191     commitChanges : function(){
38192         var modified = this.modified.slice(0),
38193             length   = modified.length,
38194             i;
38195             
38196         for (i = 0; i < length; i++){
38197             modified[i].commit();
38198         }
38199         
38200         this.modified = [];
38201         this.removed  = [];
38202     },
38203
38204     /**
38205      * {@link Ext.data.Record#reject Reject} outstanding changes on all {@link #getModifiedRecords modified records}.
38206      */
38207     rejectChanges : function() {
38208         var modified = this.modified.slice(0),
38209             removed  = this.removed.slice(0).reverse(),
38210             mLength  = modified.length,
38211             rLength  = removed.length,
38212             i;
38213         
38214         for (i = 0; i < mLength; i++) {
38215             modified[i].reject();
38216         }
38217         
38218         for (i = 0; i < rLength; i++) {
38219             this.insert(removed[i].lastIndex || 0, removed[i]);
38220             removed[i].reject();
38221         }
38222         
38223         this.modified = [];
38224         this.removed  = [];
38225     },
38226
38227     // private
38228     onMetaChange : function(meta){
38229         this.recordType = this.reader.recordType;
38230         this.fields = this.recordType.prototype.fields;
38231         delete this.snapshot;
38232         if(this.reader.meta.sortInfo){
38233             this.sortInfo = this.reader.meta.sortInfo;
38234         }else if(this.sortInfo  && !this.fields.get(this.sortInfo.field)){
38235             delete this.sortInfo;
38236         }
38237         if(this.writer){
38238             this.writer.meta = this.reader.meta;
38239         }
38240         this.modified = [];
38241         this.fireEvent('metachange', this, this.reader.meta);
38242     },
38243
38244     // private
38245     findInsertIndex : function(record){
38246         this.suspendEvents();
38247         var data = this.data.clone();
38248         this.data.add(record);
38249         this.applySort();
38250         var index = this.data.indexOf(record);
38251         this.data = data;
38252         this.resumeEvents();
38253         return index;
38254     },
38255
38256     /**
38257      * Set the value for a property name in this store's {@link #baseParams}.  Usage:</p><pre><code>
38258 myStore.setBaseParam('foo', {bar:3});
38259 </code></pre>
38260      * @param {String} name Name of the property to assign
38261      * @param {Mixed} value Value to assign the <tt>name</tt>d property
38262      **/
38263     setBaseParam : function (name, value){
38264         this.baseParams = this.baseParams || {};
38265         this.baseParams[name] = value;
38266     }
38267 });
38268
38269 Ext.reg('store', Ext.data.Store);
38270
38271 /**
38272  * @class Ext.data.Store.Error
38273  * @extends Ext.Error
38274  * Store Error extension.
38275  * @param {String} name
38276  */
38277 Ext.data.Store.Error = Ext.extend(Ext.Error, {
38278     name: 'Ext.data.Store'
38279 });
38280 Ext.apply(Ext.data.Store.Error.prototype, {
38281     lang: {
38282         'writer-undefined' : 'Attempted to execute a write-action without a DataWriter installed.'
38283     }
38284 });
38285 /**
38286  * @class Ext.data.Field
38287  * <p>This class encapsulates the field definition information specified in the field definition objects
38288  * passed to {@link Ext.data.Record#create}.</p>
38289  * <p>Developers do not need to instantiate this class. Instances are created by {@link Ext.data.Record.create}
38290  * and cached in the {@link Ext.data.Record#fields fields} property of the created Record constructor's <b>prototype.</b></p>
38291  */
38292 Ext.data.Field = Ext.extend(Object, {
38293     
38294     constructor : function(config){
38295         if(Ext.isString(config)){
38296             config = {name: config};
38297         }
38298         Ext.apply(this, config);
38299         
38300         var types = Ext.data.Types,
38301             st = this.sortType,
38302             t;
38303
38304         if(this.type){
38305             if(Ext.isString(this.type)){
38306                 this.type = Ext.data.Types[this.type.toUpperCase()] || types.AUTO;
38307             }
38308         }else{
38309             this.type = types.AUTO;
38310         }
38311
38312         // named sortTypes are supported, here we look them up
38313         if(Ext.isString(st)){
38314             this.sortType = Ext.data.SortTypes[st];
38315         }else if(Ext.isEmpty(st)){
38316             this.sortType = this.type.sortType;
38317         }
38318
38319         if(!this.convert){
38320             this.convert = this.type.convert;
38321         }
38322     },
38323     
38324     /**
38325      * @cfg {String} name
38326      * The name by which the field is referenced within the Record. This is referenced by, for example,
38327      * the <code>dataIndex</code> property in column definition objects passed to {@link Ext.grid.ColumnModel}.
38328      * <p>Note: In the simplest case, if no properties other than <code>name</code> are required, a field
38329      * definition may consist of just a String for the field name.</p>
38330      */
38331     /**
38332      * @cfg {Mixed} type
38333      * (Optional) The data type for automatic conversion from received data to the <i>stored</i> value if <code>{@link Ext.data.Field#convert convert}</code>
38334      * has not been specified. This may be specified as a string value. Possible values are
38335      * <div class="mdetail-params"><ul>
38336      * <li>auto (Default, implies no conversion)</li>
38337      * <li>string</li>
38338      * <li>int</li>
38339      * <li>float</li>
38340      * <li>boolean</li>
38341      * <li>date</li></ul></div>
38342      * <p>This may also be specified by referencing a member of the {@link Ext.data.Types} class.</p>
38343      * <p>Developers may create their own application-specific data types by defining new members of the
38344      * {@link Ext.data.Types} class.</p>
38345      */
38346     /**
38347      * @cfg {Function} convert
38348      * (Optional) A function which converts the value provided by the Reader into an object that will be stored
38349      * in the Record. It is passed the following parameters:<div class="mdetail-params"><ul>
38350      * <li><b>v</b> : Mixed<div class="sub-desc">The data value as read by the Reader, if undefined will use
38351      * the configured <code>{@link Ext.data.Field#defaultValue defaultValue}</code>.</div></li>
38352      * <li><b>rec</b> : Mixed<div class="sub-desc">The data object containing the row as read by the Reader.
38353      * Depending on the Reader type, this could be an Array ({@link Ext.data.ArrayReader ArrayReader}), an object
38354      *  ({@link Ext.data.JsonReader JsonReader}), or an XML element ({@link Ext.data.XMLReader XMLReader}).</div></li>
38355      * </ul></div>
38356      * <pre><code>
38357 // example of convert function
38358 function fullName(v, record){
38359     return record.name.last + ', ' + record.name.first;
38360 }
38361
38362 function location(v, record){
38363     return !record.city ? '' : (record.city + ', ' + record.state);
38364 }
38365
38366 var Dude = Ext.data.Record.create([
38367     {name: 'fullname',  convert: fullName},
38368     {name: 'firstname', mapping: 'name.first'},
38369     {name: 'lastname',  mapping: 'name.last'},
38370     {name: 'city', defaultValue: 'homeless'},
38371     'state',
38372     {name: 'location',  convert: location}
38373 ]);
38374
38375 // create the data store
38376 var store = new Ext.data.Store({
38377     reader: new Ext.data.JsonReader(
38378         {
38379             idProperty: 'key',
38380             root: 'daRoot',
38381             totalProperty: 'total'
38382         },
38383         Dude  // recordType
38384     )
38385 });
38386
38387 var myData = [
38388     { key: 1,
38389       name: { first: 'Fat',    last:  'Albert' }
38390       // notice no city, state provided in data object
38391     },
38392     { key: 2,
38393       name: { first: 'Barney', last:  'Rubble' },
38394       city: 'Bedrock', state: 'Stoneridge'
38395     },
38396     { key: 3,
38397       name: { first: 'Cliff',  last:  'Claven' },
38398       city: 'Boston',  state: 'MA'
38399     }
38400 ];
38401      * </code></pre>
38402      */
38403     /**
38404      * @cfg {String} dateFormat
38405      * <p>(Optional) Used when converting received data into a Date when the {@link #type} is specified as <code>"date"</code>.</p>
38406      * <p>A format string for the {@link Date#parseDate Date.parseDate} function, or "timestamp" if the
38407      * value provided by the Reader is a UNIX timestamp, or "time" if the value provided by the Reader is a
38408      * javascript millisecond timestamp. See {@link Date}</p>
38409      */
38410     dateFormat: null,
38411     
38412     /**
38413      * @cfg {Boolean} useNull
38414      * <p>(Optional) Use when converting received data into a Number type (either int or float). If the value cannot be parsed,
38415      * null will be used if useNull is true, otherwise the value will be 0. Defaults to <tt>false</tt>
38416      */
38417     useNull: false,
38418     
38419     /**
38420      * @cfg {Mixed} defaultValue
38421      * (Optional) The default value used <b>when a Record is being created by a {@link Ext.data.Reader Reader}</b>
38422      * when the item referenced by the <code>{@link Ext.data.Field#mapping mapping}</code> does not exist in the data
38423      * object (i.e. undefined). (defaults to "")
38424      */
38425     defaultValue: "",
38426     /**
38427      * @cfg {String/Number} mapping
38428      * <p>(Optional) A path expression for use by the {@link Ext.data.DataReader} implementation
38429      * that is creating the {@link Ext.data.Record Record} to extract the Field value from the data object.
38430      * If the path expression is the same as the field name, the mapping may be omitted.</p>
38431      * <p>The form of the mapping expression depends on the Reader being used.</p>
38432      * <div class="mdetail-params"><ul>
38433      * <li>{@link Ext.data.JsonReader}<div class="sub-desc">The mapping is a string containing the javascript
38434      * expression to reference the data from an element of the data item's {@link Ext.data.JsonReader#root root} Array. Defaults to the field name.</div></li>
38435      * <li>{@link Ext.data.XmlReader}<div class="sub-desc">The mapping is an {@link Ext.DomQuery} path to the data
38436      * item relative to the DOM element that represents the {@link Ext.data.XmlReader#record record}. Defaults to the field name.</div></li>
38437      * <li>{@link Ext.data.ArrayReader}<div class="sub-desc">The mapping is a number indicating the Array index
38438      * of the field's value. Defaults to the field specification's Array position.</div></li>
38439      * </ul></div>
38440      * <p>If a more complex value extraction strategy is required, then configure the Field with a {@link #convert}
38441      * function. This is passed the whole row object, and may interrogate it in whatever way is necessary in order to
38442      * return the desired data.</p>
38443      */
38444     mapping: null,
38445     /**
38446      * @cfg {Function} sortType
38447      * (Optional) A function which converts a Field's value to a comparable value in order to ensure
38448      * correct sort ordering. Predefined functions are provided in {@link Ext.data.SortTypes}. A custom
38449      * sort example:<pre><code>
38450 // current sort     after sort we want
38451 // +-+------+          +-+------+
38452 // |1|First |          |1|First |
38453 // |2|Last  |          |3|Second|
38454 // |3|Second|          |2|Last  |
38455 // +-+------+          +-+------+
38456
38457 sortType: function(value) {
38458    switch (value.toLowerCase()) // native toLowerCase():
38459    {
38460       case 'first': return 1;
38461       case 'second': return 2;
38462       default: return 3;
38463    }
38464 }
38465      * </code></pre>
38466      */
38467     sortType : null,
38468     /**
38469      * @cfg {String} sortDir
38470      * (Optional) Initial direction to sort (<code>"ASC"</code> or  <code>"DESC"</code>).  Defaults to
38471      * <code>"ASC"</code>.
38472      */
38473     sortDir : "ASC",
38474     /**
38475      * @cfg {Boolean} allowBlank
38476      * (Optional) Used for validating a {@link Ext.data.Record record}, defaults to <code>true</code>.
38477      * An empty value here will cause {@link Ext.data.Record}.{@link Ext.data.Record#isValid isValid}
38478      * to evaluate to <code>false</code>.
38479      */
38480     allowBlank : true
38481 });
38482 /**
38483  * @class Ext.data.DataReader
38484  * Abstract base class for reading structured data from a data source and converting
38485  * it into an object containing {@link Ext.data.Record} objects and metadata for use
38486  * by an {@link Ext.data.Store}.  This class is intended to be extended and should not
38487  * be created directly. For existing implementations, see {@link Ext.data.ArrayReader},
38488  * {@link Ext.data.JsonReader} and {@link Ext.data.XmlReader}.
38489  * @constructor Create a new DataReader
38490  * @param {Object} meta Metadata configuration options (implementation-specific).
38491  * @param {Array/Object} recordType
38492  * <p>Either an Array of {@link Ext.data.Field Field} definition objects (which
38493  * will be passed to {@link Ext.data.Record#create}, or a {@link Ext.data.Record Record}
38494  * constructor created using {@link Ext.data.Record#create}.</p>
38495  */
38496 Ext.data.DataReader = function(meta, recordType){
38497     /**
38498      * This DataReader's configured metadata as passed to the constructor.
38499      * @type Mixed
38500      * @property meta
38501      */
38502     this.meta = meta;
38503     /**
38504      * @cfg {Array/Object} fields
38505      * <p>Either an Array of {@link Ext.data.Field Field} definition objects (which
38506      * will be passed to {@link Ext.data.Record#create}, or a {@link Ext.data.Record Record}
38507      * constructor created from {@link Ext.data.Record#create}.</p>
38508      */
38509     this.recordType = Ext.isArray(recordType) ?
38510         Ext.data.Record.create(recordType) : recordType;
38511
38512     // if recordType defined make sure extraction functions are defined
38513     if (this.recordType){
38514         this.buildExtractors();
38515     }
38516 };
38517
38518 Ext.data.DataReader.prototype = {
38519     /**
38520      * @cfg {String} messageProperty [undefined] Optional name of a property within a server-response that represents a user-feedback message.
38521      */
38522     /**
38523      * Abstract method created in extension's buildExtractors impl.
38524      */
38525     getTotal: Ext.emptyFn,
38526     /**
38527      * Abstract method created in extension's buildExtractors impl.
38528      */
38529     getRoot: Ext.emptyFn,
38530     /**
38531      * Abstract method created in extension's buildExtractors impl.
38532      */
38533     getMessage: Ext.emptyFn,
38534     /**
38535      * Abstract method created in extension's buildExtractors impl.
38536      */
38537     getSuccess: Ext.emptyFn,
38538     /**
38539      * Abstract method created in extension's buildExtractors impl.
38540      */
38541     getId: Ext.emptyFn,
38542     /**
38543      * Abstract method, overridden in DataReader extensions such as {@link Ext.data.JsonReader} and {@link Ext.data.XmlReader}
38544      */
38545     buildExtractors : Ext.emptyFn,
38546     /**
38547      * Abstract method overridden in DataReader extensions such as {@link Ext.data.JsonReader} and {@link Ext.data.XmlReader}
38548      */
38549     extractValues : Ext.emptyFn,
38550
38551     /**
38552      * Used for un-phantoming a record after a successful database insert.  Sets the records pk along with new data from server.
38553      * You <b>must</b> return at least the database pk using the idProperty defined in your DataReader configuration.  The incoming
38554      * data from server will be merged with the data in the local record.
38555      * In addition, you <b>must</b> return record-data from the server in the same order received.
38556      * Will perform a commit as well, un-marking dirty-fields.  Store's "update" event will be suppressed.
38557      * @param {Record/Record[]} record The phantom record to be realized.
38558      * @param {Object/Object[]} data The new record data to apply.  Must include the primary-key from database defined in idProperty field.
38559      */
38560     realize: function(rs, data){
38561         if (Ext.isArray(rs)) {
38562             for (var i = rs.length - 1; i >= 0; i--) {
38563                 // recurse
38564                 if (Ext.isArray(data)) {
38565                     this.realize(rs.splice(i,1).shift(), data.splice(i,1).shift());
38566                 }
38567                 else {
38568                     // weird...rs is an array but data isn't??  recurse but just send in the whole invalid data object.
38569                     // the else clause below will detect !this.isData and throw exception.
38570                     this.realize(rs.splice(i,1).shift(), data);
38571                 }
38572             }
38573         }
38574         else {
38575             // If rs is NOT an array but data IS, see if data contains just 1 record.  If so extract it and carry on.
38576             if (Ext.isArray(data) && data.length == 1) {
38577                 data = data.shift();
38578             }
38579             if (!this.isData(data)) {
38580                 // TODO: Let exception-handler choose to commit or not rather than blindly rs.commit() here.
38581                 //rs.commit();
38582                 throw new Ext.data.DataReader.Error('realize', rs);
38583             }
38584             rs.phantom = false; // <-- That's what it's all about
38585             rs._phid = rs.id;  // <-- copy phantom-id -> _phid, so we can remap in Store#onCreateRecords
38586             rs.id = this.getId(data);
38587             rs.data = data;
38588
38589             rs.commit();
38590         }
38591     },
38592
38593     /**
38594      * Used for updating a non-phantom or "real" record's data with fresh data from server after remote-save.
38595      * If returning data from multiple-records after a batch-update, you <b>must</b> return record-data from the server in
38596      * the same order received.  Will perform a commit as well, un-marking dirty-fields.  Store's "update" event will be
38597      * suppressed as the record receives fresh new data-hash
38598      * @param {Record/Record[]} rs
38599      * @param {Object/Object[]} data
38600      */
38601     update : function(rs, data) {
38602         if (Ext.isArray(rs)) {
38603             for (var i=rs.length-1; i >= 0; i--) {
38604                 if (Ext.isArray(data)) {
38605                     this.update(rs.splice(i,1).shift(), data.splice(i,1).shift());
38606                 }
38607                 else {
38608                     // weird...rs is an array but data isn't??  recurse but just send in the whole data object.
38609                     // the else clause below will detect !this.isData and throw exception.
38610                     this.update(rs.splice(i,1).shift(), data);
38611                 }
38612             }
38613         }
38614         else {
38615             // If rs is NOT an array but data IS, see if data contains just 1 record.  If so extract it and carry on.
38616             if (Ext.isArray(data) && data.length == 1) {
38617                 data = data.shift();
38618             }
38619             if (this.isData(data)) {
38620                 rs.data = Ext.apply(rs.data, data);
38621             }
38622             rs.commit();
38623         }
38624     },
38625
38626     /**
38627      * returns extracted, type-cast rows of data.  Iterates to call #extractValues for each row
38628      * @param {Object[]/Object} data-root from server response
38629      * @param {Boolean} returnRecords [false] Set true to return instances of Ext.data.Record
38630      * @private
38631      */
38632     extractData : function(root, returnRecords) {
38633         // A bit ugly this, too bad the Record's raw data couldn't be saved in a common property named "raw" or something.
38634         var rawName = (this instanceof Ext.data.JsonReader) ? 'json' : 'node';
38635
38636         var rs = [];
38637
38638         // Had to add Check for XmlReader, #isData returns true if root is an Xml-object.  Want to check in order to re-factor
38639         // #extractData into DataReader base, since the implementations are almost identical for JsonReader, XmlReader
38640         if (this.isData(root) && !(this instanceof Ext.data.XmlReader)) {
38641             root = [root];
38642         }
38643         var f       = this.recordType.prototype.fields,
38644             fi      = f.items,
38645             fl      = f.length,
38646             rs      = [];
38647         if (returnRecords === true) {
38648             var Record = this.recordType;
38649             for (var i = 0; i < root.length; i++) {
38650                 var n = root[i];
38651                 var record = new Record(this.extractValues(n, fi, fl), this.getId(n));
38652                 record[rawName] = n;    // <-- There's implementation of ugly bit, setting the raw record-data.
38653                 rs.push(record);
38654             }
38655         }
38656         else {
38657             for (var i = 0; i < root.length; i++) {
38658                 var data = this.extractValues(root[i], fi, fl);
38659                 data[this.meta.idProperty] = this.getId(root[i]);
38660                 rs.push(data);
38661             }
38662         }
38663         return rs;
38664     },
38665
38666     /**
38667      * Returns true if the supplied data-hash <b>looks</b> and quacks like data.  Checks to see if it has a key
38668      * corresponding to idProperty defined in your DataReader config containing non-empty pk.
38669      * @param {Object} data
38670      * @return {Boolean}
38671      */
38672     isData : function(data) {
38673         return (data && Ext.isObject(data) && !Ext.isEmpty(this.getId(data))) ? true : false;
38674     },
38675
38676     // private function a store will createSequence upon
38677     onMetaChange : function(meta){
38678         delete this.ef;
38679         this.meta = meta;
38680         this.recordType = Ext.data.Record.create(meta.fields);
38681         this.buildExtractors();
38682     }
38683 };
38684
38685 /**
38686  * @class Ext.data.DataReader.Error
38687  * @extends Ext.Error
38688  * General error class for Ext.data.DataReader
38689  */
38690 Ext.data.DataReader.Error = Ext.extend(Ext.Error, {
38691     constructor : function(message, arg) {
38692         this.arg = arg;
38693         Ext.Error.call(this, message);
38694     },
38695     name: 'Ext.data.DataReader'
38696 });
38697 Ext.apply(Ext.data.DataReader.Error.prototype, {
38698     lang : {
38699         'update': "#update received invalid data from server.  Please see docs for DataReader#update and review your DataReader configuration.",
38700         'realize': "#realize was called with invalid remote-data.  Please see the docs for DataReader#realize and review your DataReader configuration.",
38701         'invalid-response': "#readResponse received an invalid response from the server."
38702     }
38703 });
38704 /**
38705  * @class Ext.data.DataWriter
38706  * <p>Ext.data.DataWriter facilitates create, update, and destroy actions between
38707  * an Ext.data.Store and a server-side framework. A Writer enabled Store will
38708  * automatically manage the Ajax requests to perform CRUD actions on a Store.</p>
38709  * <p>Ext.data.DataWriter is an abstract base class which is intended to be extended
38710  * and should not be created directly. For existing implementations, see
38711  * {@link Ext.data.JsonWriter}.</p>
38712  * <p>Creating a writer is simple:</p>
38713  * <pre><code>
38714 var writer = new Ext.data.JsonWriter({
38715     encode: false   // &lt;--- false causes data to be printed to jsonData config-property of Ext.Ajax#reqeust
38716 });
38717  * </code></pre>
38718  * * <p>Same old JsonReader as Ext-2.x:</p>
38719  * <pre><code>
38720 var reader = new Ext.data.JsonReader({idProperty: 'id'}, [{name: 'first'}, {name: 'last'}, {name: 'email'}]);
38721  * </code></pre>
38722  *
38723  * <p>The proxy for a writer enabled store can be configured with a simple <code>url</code>:</p>
38724  * <pre><code>
38725 // Create a standard HttpProxy instance.
38726 var proxy = new Ext.data.HttpProxy({
38727     url: 'app.php/users'    // &lt;--- Supports "provides"-type urls, such as '/users.json', '/products.xml' (Hello Rails/Merb)
38728 });
38729  * </code></pre>
38730  * <p>For finer grained control, the proxy may also be configured with an <code>API</code>:</p>
38731  * <pre><code>
38732 // Maximum flexibility with the API-configuration
38733 var proxy = new Ext.data.HttpProxy({
38734     api: {
38735         read    : 'app.php/users/read',
38736         create  : 'app.php/users/create',
38737         update  : 'app.php/users/update',
38738         destroy : {  // &lt;--- Supports object-syntax as well
38739             url: 'app.php/users/destroy',
38740             method: "DELETE"
38741         }
38742     }
38743 });
38744  * </code></pre>
38745  * <p>Pulling it all together into a Writer-enabled Store:</p>
38746  * <pre><code>
38747 var store = new Ext.data.Store({
38748     proxy: proxy,
38749     reader: reader,
38750     writer: writer,
38751     autoLoad: true,
38752     autoSave: true  // -- Cell-level updates.
38753 });
38754  * </code></pre>
38755  * <p>Initiating write-actions <b>automatically</b>, using the existing Ext2.0 Store/Record API:</p>
38756  * <pre><code>
38757 var rec = store.getAt(0);
38758 rec.set('email', 'foo@bar.com');  // &lt;--- Immediately initiates an UPDATE action through configured proxy.
38759
38760 store.remove(rec);  // &lt;---- Immediately initiates a DESTROY action through configured proxy.
38761  * </code></pre>
38762  * <p>For <b>record/batch</b> updates, use the Store-configuration {@link Ext.data.Store#autoSave autoSave:false}</p>
38763  * <pre><code>
38764 var store = new Ext.data.Store({
38765     proxy: proxy,
38766     reader: reader,
38767     writer: writer,
38768     autoLoad: true,
38769     autoSave: false  // -- disable cell-updates
38770 });
38771
38772 var urec = store.getAt(0);
38773 urec.set('email', 'foo@bar.com');
38774
38775 var drec = store.getAt(1);
38776 store.remove(drec);
38777
38778 // Push the button!
38779 store.save();
38780  * </code></pre>
38781  * @constructor Create a new DataWriter
38782  * @param {Object} meta Metadata configuration options (implementation-specific)
38783  * @param {Object} recordType Either an Array of field definition objects as specified
38784  * in {@link Ext.data.Record#create}, or an {@link Ext.data.Record} object created
38785  * using {@link Ext.data.Record#create}.
38786  */
38787 Ext.data.DataWriter = function(config){
38788     Ext.apply(this, config);
38789 };
38790 Ext.data.DataWriter.prototype = {
38791
38792     /**
38793      * @cfg {Boolean} writeAllFields
38794      * <tt>false</tt> by default.  Set <tt>true</tt> to have DataWriter return ALL fields of a modified
38795      * record -- not just those that changed.
38796      * <tt>false</tt> to have DataWriter only request modified fields from a record.
38797      */
38798     writeAllFields : false,
38799     /**
38800      * @cfg {Boolean} listful
38801      * <tt>false</tt> by default.  Set <tt>true</tt> to have the DataWriter <b>always</b> write HTTP params as a list,
38802      * even when acting upon a single record.
38803      */
38804     listful : false,    // <-- listful is actually not used internally here in DataWriter.  @see Ext.data.Store#execute.
38805
38806     /**
38807      * Compiles a Store recordset into a data-format defined by an extension such as {@link Ext.data.JsonWriter} or {@link Ext.data.XmlWriter} in preparation for a {@link Ext.data.Api#actions server-write action}.  The first two params are similar similar in nature to {@link Ext#apply},
38808      * Where the first parameter is the <i>receiver</i> of paramaters and the second, baseParams, <i>the source</i>.
38809      * @param {Object} params The request-params receiver.
38810      * @param {Object} baseParams as defined by {@link Ext.data.Store#baseParams}.  The baseParms must be encoded by the extending class, eg: {@link Ext.data.JsonWriter}, {@link Ext.data.XmlWriter}.
38811      * @param {String} action [{@link Ext.data.Api#actions create|update|destroy}]
38812      * @param {Record/Record[]} rs The recordset to write, the subject(s) of the write action.
38813      */
38814     apply : function(params, baseParams, action, rs) {
38815         var data    = [],
38816         renderer    = action + 'Record';
38817         // TODO implement @cfg listful here
38818         if (Ext.isArray(rs)) {
38819             Ext.each(rs, function(rec){
38820                 data.push(this[renderer](rec));
38821             }, this);
38822         }
38823         else if (rs instanceof Ext.data.Record) {
38824             data = this[renderer](rs);
38825         }
38826         this.render(params, baseParams, data);
38827     },
38828
38829     /**
38830      * abstract method meant to be overridden by all DataWriter extensions.  It's the extension's job to apply the "data" to the "params".
38831      * The data-object provided to render is populated with data according to the meta-info defined in the user's DataReader config,
38832      * @param {String} action [Ext.data.Api.actions.create|read|update|destroy]
38833      * @param {Record[]} rs Store recordset
38834      * @param {Object} params Http params to be sent to server.
38835      * @param {Object} data object populated according to DataReader meta-data.
38836      */
38837     render : Ext.emptyFn,
38838
38839     /**
38840      * @cfg {Function} updateRecord Abstract method that should be implemented in all subclasses
38841      * (e.g.: {@link Ext.data.JsonWriter#updateRecord JsonWriter.updateRecord}
38842      */
38843     updateRecord : Ext.emptyFn,
38844
38845     /**
38846      * @cfg {Function} createRecord Abstract method that should be implemented in all subclasses
38847      * (e.g.: {@link Ext.data.JsonWriter#createRecord JsonWriter.createRecord})
38848      */
38849     createRecord : Ext.emptyFn,
38850
38851     /**
38852      * @cfg {Function} destroyRecord Abstract method that should be implemented in all subclasses
38853      * (e.g.: {@link Ext.data.JsonWriter#destroyRecord JsonWriter.destroyRecord})
38854      */
38855     destroyRecord : Ext.emptyFn,
38856
38857     /**
38858      * Converts a Record to a hash, taking into account the state of the Ext.data.Record along with configuration properties
38859      * related to its rendering, such as {@link #writeAllFields}, {@link Ext.data.Record#phantom phantom}, {@link Ext.data.Record#getChanges getChanges} and
38860      * {@link Ext.data.DataReader#idProperty idProperty}
38861      * @param {Ext.data.Record} rec The Record from which to create a hash.
38862      * @param {Object} config <b>NOT YET IMPLEMENTED</b>.  Will implement an exlude/only configuration for fine-control over which fields do/don't get rendered.
38863      * @return {Object}
38864      * @protected
38865      * TODO Implement excludes/only configuration with 2nd param?
38866      */
38867     toHash : function(rec, config) {
38868         var map = rec.fields.map,
38869             data = {},
38870             raw = (this.writeAllFields === false && rec.phantom === false) ? rec.getChanges() : rec.data,
38871             m;
38872         Ext.iterate(raw, function(prop, value){
38873             if((m = map[prop])){
38874                 data[m.mapping ? m.mapping : m.name] = value;
38875             }
38876         });
38877         // we don't want to write Ext auto-generated id to hash.  Careful not to remove it on Models not having auto-increment pk though.
38878         // We can tell its not auto-increment if the user defined a DataReader field for it *and* that field's value is non-empty.
38879         // we could also do a RegExp here for the Ext.data.Record AUTO_ID prefix.
38880         if (rec.phantom) {
38881             if (rec.fields.containsKey(this.meta.idProperty) && Ext.isEmpty(rec.data[this.meta.idProperty])) {
38882                 delete data[this.meta.idProperty];
38883             }
38884         } else {
38885             data[this.meta.idProperty] = rec.id;
38886         }
38887         return data;
38888     },
38889
38890     /**
38891      * Converts a {@link Ext.data.DataWriter#toHash Hashed} {@link Ext.data.Record} to fields-array array suitable
38892      * for encoding to xml via XTemplate, eg:
38893 <code><pre>&lt;tpl for=".">&lt;{name}>{value}&lt;/{name}&lt;/tpl></pre></code>
38894      * eg, <b>non-phantom</b>:
38895 <code><pre>{id: 1, first: 'foo', last: 'bar'} --> [{name: 'id', value: 1}, {name: 'first', value: 'foo'}, {name: 'last', value: 'bar'}]</pre></code>
38896      * {@link Ext.data.Record#phantom Phantom} records will have had their idProperty omitted in {@link #toHash} if determined to be auto-generated.
38897      * Non AUTOINCREMENT pks should have been protected.
38898      * @param {Hash} data Hashed by Ext.data.DataWriter#toHash
38899      * @return {[Object]} Array of attribute-objects.
38900      * @protected
38901      */
38902     toArray : function(data) {
38903         var fields = [];
38904         Ext.iterate(data, function(k, v) {fields.push({name: k, value: v});},this);
38905         return fields;
38906     }
38907 };/**
38908  * @class Ext.data.DataProxy
38909  * @extends Ext.util.Observable
38910  * <p>Abstract base class for implementations which provide retrieval of unformatted data objects.
38911  * This class is intended to be extended and should not be created directly. For existing implementations,
38912  * see {@link Ext.data.DirectProxy}, {@link Ext.data.HttpProxy}, {@link Ext.data.ScriptTagProxy} and
38913  * {@link Ext.data.MemoryProxy}.</p>
38914  * <p>DataProxy implementations are usually used in conjunction with an implementation of {@link Ext.data.DataReader}
38915  * (of the appropriate type which knows how to parse the data object) to provide a block of
38916  * {@link Ext.data.Records} to an {@link Ext.data.Store}.</p>
38917  * <p>The parameter to a DataProxy constructor may be an {@link Ext.data.Connection} or can also be the
38918  * config object to an {@link Ext.data.Connection}.</p>
38919  * <p>Custom implementations must implement either the <code><b>doRequest</b></code> method (preferred) or the
38920  * <code>load</code> method (deprecated). See
38921  * {@link Ext.data.HttpProxy}.{@link Ext.data.HttpProxy#doRequest doRequest} or
38922  * {@link Ext.data.HttpProxy}.{@link Ext.data.HttpProxy#load load} for additional details.</p>
38923  * <p><b><u>Example 1</u></b></p>
38924  * <pre><code>
38925 proxy: new Ext.data.ScriptTagProxy({
38926     {@link Ext.data.Connection#url url}: 'http://extjs.com/forum/topics-remote.php'
38927 }),
38928  * </code></pre>
38929  * <p><b><u>Example 2</u></b></p>
38930  * <pre><code>
38931 proxy : new Ext.data.HttpProxy({
38932     {@link Ext.data.Connection#method method}: 'GET',
38933     {@link Ext.data.HttpProxy#prettyUrls prettyUrls}: false,
38934     {@link Ext.data.Connection#url url}: 'local/default.php', // see options parameter for {@link Ext.Ajax#request}
38935     {@link #api}: {
38936         // all actions except the following will use above url
38937         create  : 'local/new.php',
38938         update  : 'local/update.php'
38939     }
38940 }),
38941  * </code></pre>
38942  * <p>And <b>new in Ext version 3</b>, attach centralized event-listeners upon the DataProxy class itself!  This is a great place
38943  * to implement a <i>messaging system</i> to centralize your application's user-feedback and error-handling.</p>
38944  * <pre><code>
38945 // Listen to all "beforewrite" event fired by all proxies.
38946 Ext.data.DataProxy.on('beforewrite', function(proxy, action) {
38947     console.log('beforewrite: ', action);
38948 });
38949
38950 // Listen to "write" event fired by all proxies
38951 Ext.data.DataProxy.on('write', function(proxy, action, data, res, rs) {
38952     console.info('write: ', action);
38953 });
38954
38955 // Listen to "exception" event fired by all proxies
38956 Ext.data.DataProxy.on('exception', function(proxy, type, action, exception) {
38957     console.error(type + action + ' exception);
38958 });
38959  * </code></pre>
38960  * <b>Note:</b> These three events are all fired with the signature of the corresponding <i>DataProxy instance</i> event {@link #beforewrite beforewrite}, {@link #write write} and {@link #exception exception}.
38961  */
38962 Ext.data.DataProxy = function(conn){
38963     // make sure we have a config object here to support ux proxies.
38964     // All proxies should now send config into superclass constructor.
38965     conn = conn || {};
38966
38967     // This line caused a bug when people use custom Connection object having its own request method.
38968     // http://extjs.com/forum/showthread.php?t=67194.  Have to set DataProxy config
38969     //Ext.applyIf(this, conn);
38970
38971     this.api     = conn.api;
38972     this.url     = conn.url;
38973     this.restful = conn.restful;
38974     this.listeners = conn.listeners;
38975
38976     // deprecated
38977     this.prettyUrls = conn.prettyUrls;
38978
38979     /**
38980      * @cfg {Object} api
38981      * Specific urls to call on CRUD action methods "read", "create", "update" and "destroy".
38982      * Defaults to:<pre><code>
38983 api: {
38984     read    : undefined,
38985     create  : undefined,
38986     update  : undefined,
38987     destroy : undefined
38988 }
38989      * </code></pre>
38990      * <p>The url is built based upon the action being executed <tt>[load|create|save|destroy]</tt>
38991      * using the commensurate <tt>{@link #api}</tt> property, or if undefined default to the
38992      * configured {@link Ext.data.Store}.{@link Ext.data.Store#url url}.</p><br>
38993      * <p>For example:</p>
38994      * <pre><code>
38995 api: {
38996     load :    '/controller/load',
38997     create :  '/controller/new',  // Server MUST return idProperty of new record
38998     save :    '/controller/update',
38999     destroy : '/controller/destroy_action'
39000 }
39001
39002 // Alternatively, one can use the object-form to specify each API-action
39003 api: {
39004     load: {url: 'read.php', method: 'GET'},
39005     create: 'create.php',
39006     destroy: 'destroy.php',
39007     save: 'update.php'
39008 }
39009      * </code></pre>
39010      * <p>If the specific URL for a given CRUD action is undefined, the CRUD action request
39011      * will be directed to the configured <tt>{@link Ext.data.Connection#url url}</tt>.</p>
39012      * <br><p><b>Note</b>: To modify the URL for an action dynamically the appropriate API
39013      * property should be modified before the action is requested using the corresponding before
39014      * action event.  For example to modify the URL associated with the load action:
39015      * <pre><code>
39016 // modify the url for the action
39017 myStore.on({
39018     beforeload: {
39019         fn: function (store, options) {
39020             // use <tt>{@link Ext.data.HttpProxy#setUrl setUrl}</tt> to change the URL for *just* this request.
39021             store.proxy.setUrl('changed1.php');
39022
39023             // set optional second parameter to true to make this URL change
39024             // permanent, applying this URL for all subsequent requests.
39025             store.proxy.setUrl('changed1.php', true);
39026
39027             // Altering the proxy API should be done using the public
39028             // method <tt>{@link Ext.data.DataProxy#setApi setApi}</tt>.
39029             store.proxy.setApi('read', 'changed2.php');
39030
39031             // Or set the entire API with a config-object.
39032             // When using the config-object option, you must redefine the <b>entire</b>
39033             // API -- not just a specific action of it.
39034             store.proxy.setApi({
39035                 read    : 'changed_read.php',
39036                 create  : 'changed_create.php',
39037                 update  : 'changed_update.php',
39038                 destroy : 'changed_destroy.php'
39039             });
39040         }
39041     }
39042 });
39043      * </code></pre>
39044      * </p>
39045      */
39046
39047     this.addEvents(
39048         /**
39049          * @event exception
39050          * <p>Fires if an exception occurs in the Proxy during a remote request. This event is relayed
39051          * through a corresponding {@link Ext.data.Store}.{@link Ext.data.Store#exception exception},
39052          * so any Store instance may observe this event.</p>
39053          * <p>In addition to being fired through the DataProxy instance that raised the event, this event is also fired
39054          * through the Ext.data.DataProxy <i>class</i> to allow for centralized processing of exception events from <b>all</b>
39055          * DataProxies by attaching a listener to the Ext.data.DataProxy class itself.</p>
39056          * <p>This event can be fired for one of two reasons:</p>
39057          * <div class="mdetail-params"><ul>
39058          * <li>remote-request <b>failed</b> : <div class="sub-desc">
39059          * The server did not return status === 200.
39060          * </div></li>
39061          * <li>remote-request <b>succeeded</b> : <div class="sub-desc">
39062          * The remote-request succeeded but the reader could not read the response.
39063          * This means the server returned data, but the configured Reader threw an
39064          * error while reading the response.  In this case, this event will be
39065          * raised and the caught error will be passed along into this event.
39066          * </div></li>
39067          * </ul></div>
39068          * <br><p>This event fires with two different contexts based upon the 2nd
39069          * parameter <tt>type [remote|response]</tt>.  The first four parameters
39070          * are identical between the two contexts -- only the final two parameters
39071          * differ.</p>
39072          * @param {DataProxy} this The proxy that sent the request
39073          * @param {String} type
39074          * <p>The value of this parameter will be either <tt>'response'</tt> or <tt>'remote'</tt>.</p>
39075          * <div class="mdetail-params"><ul>
39076          * <li><b><tt>'response'</tt></b> : <div class="sub-desc">
39077          * <p>An <b>invalid</b> response from the server was returned: either 404,
39078          * 500 or the response meta-data does not match that defined in the DataReader
39079          * (e.g.: root, idProperty, successProperty).</p>
39080          * </div></li>
39081          * <li><b><tt>'remote'</tt></b> : <div class="sub-desc">
39082          * <p>A <b>valid</b> response was returned from the server having
39083          * successProperty === false.  This response might contain an error-message
39084          * sent from the server.  For example, the user may have failed
39085          * authentication/authorization or a database validation error occurred.</p>
39086          * </div></li>
39087          * </ul></div>
39088          * @param {String} action Name of the action (see {@link Ext.data.Api#actions}.
39089          * @param {Object} options The options for the action that were specified in the {@link #request}.
39090          * @param {Object} response
39091          * <p>The value of this parameter depends on the value of the <code>type</code> parameter:</p>
39092          * <div class="mdetail-params"><ul>
39093          * <li><b><tt>'response'</tt></b> : <div class="sub-desc">
39094          * <p>The raw browser response object (e.g.: XMLHttpRequest)</p>
39095          * </div></li>
39096          * <li><b><tt>'remote'</tt></b> : <div class="sub-desc">
39097          * <p>The decoded response object sent from the server.</p>
39098          * </div></li>
39099          * </ul></div>
39100          * @param {Mixed} arg
39101          * <p>The type and value of this parameter depends on the value of the <code>type</code> parameter:</p>
39102          * <div class="mdetail-params"><ul>
39103          * <li><b><tt>'response'</tt></b> : Error<div class="sub-desc">
39104          * <p>The JavaScript Error object caught if the configured Reader could not read the data.
39105          * If the remote request returns success===false, this parameter will be null.</p>
39106          * </div></li>
39107          * <li><b><tt>'remote'</tt></b> : Record/Record[]<div class="sub-desc">
39108          * <p>This parameter will only exist if the <tt>action</tt> was a <b>write</b> action
39109          * (Ext.data.Api.actions.create|update|destroy).</p>
39110          * </div></li>
39111          * </ul></div>
39112          */
39113         'exception',
39114         /**
39115          * @event beforeload
39116          * Fires before a request to retrieve a data object.
39117          * @param {DataProxy} this The proxy for the request
39118          * @param {Object} params The params object passed to the {@link #request} function
39119          */
39120         'beforeload',
39121         /**
39122          * @event load
39123          * Fires before the load method's callback is called.
39124          * @param {DataProxy} this The proxy for the request
39125          * @param {Object} o The request transaction object
39126          * @param {Object} options The callback's <tt>options</tt> property as passed to the {@link #request} function
39127          */
39128         'load',
39129         /**
39130          * @event loadexception
39131          * <p>This event is <b>deprecated</b>.  The signature of the loadexception event
39132          * varies depending on the proxy, use the catch-all {@link #exception} event instead.
39133          * This event will fire in addition to the {@link #exception} event.</p>
39134          * @param {misc} misc See {@link #exception}.
39135          * @deprecated
39136          */
39137         'loadexception',
39138         /**
39139          * @event beforewrite
39140          * <p>Fires before a request is generated for one of the actions Ext.data.Api.actions.create|update|destroy</p>
39141          * <p>In addition to being fired through the DataProxy instance that raised the event, this event is also fired
39142          * through the Ext.data.DataProxy <i>class</i> to allow for centralized processing of beforewrite events from <b>all</b>
39143          * DataProxies by attaching a listener to the Ext.data.DataProxy class itself.</p>
39144          * @param {DataProxy} this The proxy for the request
39145          * @param {String} action [Ext.data.Api.actions.create|update|destroy]
39146          * @param {Record/Record[]} rs The Record(s) to create|update|destroy.
39147          * @param {Object} params The request <code>params</code> object.  Edit <code>params</code> to add parameters to the request.
39148          */
39149         'beforewrite',
39150         /**
39151          * @event write
39152          * <p>Fires before the request-callback is called</p>
39153          * <p>In addition to being fired through the DataProxy instance that raised the event, this event is also fired
39154          * through the Ext.data.DataProxy <i>class</i> to allow for centralized processing of write events from <b>all</b>
39155          * DataProxies by attaching a listener to the Ext.data.DataProxy class itself.</p>
39156          * @param {DataProxy} this The proxy that sent the request
39157          * @param {String} action [Ext.data.Api.actions.create|upate|destroy]
39158          * @param {Object} data The data object extracted from the server-response
39159          * @param {Object} response The decoded response from server
39160          * @param {Record/Record[]} rs The Record(s) from Store
39161          * @param {Object} options The callback's <tt>options</tt> property as passed to the {@link #request} function
39162          */
39163         'write'
39164     );
39165     Ext.data.DataProxy.superclass.constructor.call(this);
39166
39167     // Prepare the proxy api.  Ensures all API-actions are defined with the Object-form.
39168     try {
39169         Ext.data.Api.prepare(this);
39170     } catch (e) {
39171         if (e instanceof Ext.data.Api.Error) {
39172             e.toConsole();
39173         }
39174     }
39175     // relay each proxy's events onto Ext.data.DataProxy class for centralized Proxy-listening
39176     Ext.data.DataProxy.relayEvents(this, ['beforewrite', 'write', 'exception']);
39177 };
39178
39179 Ext.extend(Ext.data.DataProxy, Ext.util.Observable, {
39180     /**
39181      * @cfg {Boolean} restful
39182      * <p>Defaults to <tt>false</tt>.  Set to <tt>true</tt> to operate in a RESTful manner.</p>
39183      * <br><p> Note: this parameter will automatically be set to <tt>true</tt> if the
39184      * {@link Ext.data.Store} it is plugged into is set to <code>restful: true</code>. If the
39185      * Store is RESTful, there is no need to set this option on the proxy.</p>
39186      * <br><p>RESTful implementations enable the serverside framework to automatically route
39187      * actions sent to one url based upon the HTTP method, for example:
39188      * <pre><code>
39189 store: new Ext.data.Store({
39190     restful: true,
39191     proxy: new Ext.data.HttpProxy({url:'/users'}); // all requests sent to /users
39192     ...
39193 )}
39194      * </code></pre>
39195      * If there is no <code>{@link #api}</code> specified in the configuration of the proxy,
39196      * all requests will be marshalled to a single RESTful url (/users) so the serverside
39197      * framework can inspect the HTTP Method and act accordingly:
39198      * <pre>
39199 <u>Method</u>   <u>url</u>        <u>action</u>
39200 POST     /users     create
39201 GET      /users     read
39202 PUT      /users/23  update
39203 DESTROY  /users/23  delete
39204      * </pre></p>
39205      * <p>If set to <tt>true</tt>, a {@link Ext.data.Record#phantom non-phantom} record's
39206      * {@link Ext.data.Record#id id} will be appended to the url. Some MVC (e.g., Ruby on Rails,
39207      * Merb and Django) support segment based urls where the segments in the URL follow the
39208      * Model-View-Controller approach:<pre><code>
39209      * someSite.com/controller/action/id
39210      * </code></pre>
39211      * Where the segments in the url are typically:<div class="mdetail-params"><ul>
39212      * <li>The first segment : represents the controller class that should be invoked.</li>
39213      * <li>The second segment : represents the class function, or method, that should be called.</li>
39214      * <li>The third segment : represents the ID (a variable typically passed to the method).</li>
39215      * </ul></div></p>
39216      * <br><p>Refer to <code>{@link Ext.data.DataProxy#api}</code> for additional information.</p>
39217      */
39218     restful: false,
39219
39220     /**
39221      * <p>Redefines the Proxy's API or a single action of an API. Can be called with two method signatures.</p>
39222      * <p>If called with an object as the only parameter, the object should redefine the <b>entire</b> API, e.g.:</p><pre><code>
39223 proxy.setApi({
39224     read    : '/users/read',
39225     create  : '/users/create',
39226     update  : '/users/update',
39227     destroy : '/users/destroy'
39228 });
39229 </code></pre>
39230      * <p>If called with two parameters, the first parameter should be a string specifying the API action to
39231      * redefine and the second parameter should be the URL (or function if using DirectProxy) to call for that action, e.g.:</p><pre><code>
39232 proxy.setApi(Ext.data.Api.actions.read, '/users/new_load_url');
39233 </code></pre>
39234      * @param {String/Object} api An API specification object, or the name of an action.
39235      * @param {String/Function} url The URL (or function if using DirectProxy) to call for the action.
39236      */
39237     setApi : function() {
39238         if (arguments.length == 1) {
39239             var valid = Ext.data.Api.isValid(arguments[0]);
39240             if (valid === true) {
39241                 this.api = arguments[0];
39242             }
39243             else {
39244                 throw new Ext.data.Api.Error('invalid', valid);
39245             }
39246         }
39247         else if (arguments.length == 2) {
39248             if (!Ext.data.Api.isAction(arguments[0])) {
39249                 throw new Ext.data.Api.Error('invalid', arguments[0]);
39250             }
39251             this.api[arguments[0]] = arguments[1];
39252         }
39253         Ext.data.Api.prepare(this);
39254     },
39255
39256     /**
39257      * Returns true if the specified action is defined as a unique action in the api-config.
39258      * request.  If all API-actions are routed to unique urls, the xaction parameter is unecessary.  However, if no api is defined
39259      * and all Proxy actions are routed to DataProxy#url, the server-side will require the xaction parameter to perform a switch to
39260      * the corresponding code for CRUD action.
39261      * @param {String [Ext.data.Api.CREATE|READ|UPDATE|DESTROY]} action
39262      * @return {Boolean}
39263      */
39264     isApiAction : function(action) {
39265         return (this.api[action]) ? true : false;
39266     },
39267
39268     /**
39269      * All proxy actions are executed through this method.  Automatically fires the "before" + action event
39270      * @param {String} action Name of the action
39271      * @param {Ext.data.Record/Ext.data.Record[]/null} rs Will be null when action is 'load'
39272      * @param {Object} params
39273      * @param {Ext.data.DataReader} reader
39274      * @param {Function} callback
39275      * @param {Object} scope The scope (<code>this</code> reference) in which the callback function is executed. Defaults to the Proxy object.
39276      * @param {Object} options Any options specified for the action (e.g. see {@link Ext.data.Store#load}.
39277      */
39278     request : function(action, rs, params, reader, callback, scope, options) {
39279         if (!this.api[action] && !this.load) {
39280             throw new Ext.data.DataProxy.Error('action-undefined', action);
39281         }
39282         params = params || {};
39283         if ((action === Ext.data.Api.actions.read) ? this.fireEvent("beforeload", this, params) : this.fireEvent("beforewrite", this, action, rs, params) !== false) {
39284             this.doRequest.apply(this, arguments);
39285         }
39286         else {
39287             callback.call(scope || this, null, options, false);
39288         }
39289     },
39290
39291
39292     /**
39293      * <b>Deprecated</b> load method using old method signature. See {@doRequest} for preferred method.
39294      * @deprecated
39295      * @param {Object} params
39296      * @param {Object} reader
39297      * @param {Object} callback
39298      * @param {Object} scope
39299      * @param {Object} arg
39300      */
39301     load : null,
39302
39303     /**
39304      * @cfg {Function} doRequest Abstract method that should be implemented in all subclasses.  <b>Note:</b> Should only be used by custom-proxy developers.
39305      * (e.g.: {@link Ext.data.HttpProxy#doRequest HttpProxy.doRequest},
39306      * {@link Ext.data.DirectProxy#doRequest DirectProxy.doRequest}).
39307      */
39308     doRequest : function(action, rs, params, reader, callback, scope, options) {
39309         // default implementation of doRequest for backwards compatibility with 2.0 proxies.
39310         // If we're executing here, the action is probably "load".
39311         // Call with the pre-3.0 method signature.
39312         this.load(params, reader, callback, scope, options);
39313     },
39314
39315     /**
39316      * @cfg {Function} onRead Abstract method that should be implemented in all subclasses.  <b>Note:</b> Should only be used by custom-proxy developers.  Callback for read {@link Ext.data.Api#actions action}.
39317      * @param {String} action Action name as per {@link Ext.data.Api.actions#read}.
39318      * @param {Object} o The request transaction object
39319      * @param {Object} res The server response
39320      * @fires loadexception (deprecated)
39321      * @fires exception
39322      * @fires load
39323      * @protected
39324      */
39325     onRead : Ext.emptyFn,
39326     /**
39327      * @cfg {Function} onWrite Abstract method that should be implemented in all subclasses.  <b>Note:</b> Should only be used by custom-proxy developers.  Callback for <i>create, update and destroy</i> {@link Ext.data.Api#actions actions}.
39328      * @param {String} action [Ext.data.Api.actions.create|read|update|destroy]
39329      * @param {Object} trans The request transaction object
39330      * @param {Object} res The server response
39331      * @fires exception
39332      * @fires write
39333      * @protected
39334      */
39335     onWrite : Ext.emptyFn,
39336     /**
39337      * buildUrl
39338      * Sets the appropriate url based upon the action being executed.  If restful is true, and only a single record is being acted upon,
39339      * url will be built Rails-style, as in "/controller/action/32".  restful will aply iff the supplied record is an
39340      * instance of Ext.data.Record rather than an Array of them.
39341      * @param {String} action The api action being executed [read|create|update|destroy]
39342      * @param {Ext.data.Record/Ext.data.Record[]} record The record or Array of Records being acted upon.
39343      * @return {String} url
39344      * @private
39345      */
39346     buildUrl : function(action, record) {
39347         record = record || null;
39348
39349         // conn.url gets nullified after each request.  If it's NOT null here, that means the user must have intervened with a call
39350         // to DataProxy#setUrl or DataProxy#setApi and changed it before the request was executed.  If that's the case, use conn.url,
39351         // otherwise, build the url from the api or this.url.
39352         var url = (this.conn && this.conn.url) ? this.conn.url : (this.api[action]) ? this.api[action].url : this.url;
39353         if (!url) {
39354             throw new Ext.data.Api.Error('invalid-url', action);
39355         }
39356
39357         // look for urls having "provides" suffix used in some MVC frameworks like Rails/Merb and others.  The provides suffice informs
39358         // the server what data-format the client is dealing with and returns data in the same format (eg: application/json, application/xml, etc)
39359         // e.g.: /users.json, /users.xml, etc.
39360         // with restful routes, we need urls like:
39361         // PUT /users/1.json
39362         // DELETE /users/1.json
39363         var provides = null;
39364         var m = url.match(/(.*)(\.json|\.xml|\.html)$/);
39365         if (m) {
39366             provides = m[2];    // eg ".json"
39367             url      = m[1];    // eg: "/users"
39368         }
39369         // prettyUrls is deprectated in favor of restful-config
39370         if ((this.restful === true || this.prettyUrls === true) && record instanceof Ext.data.Record && !record.phantom) {
39371             url += '/' + record.id;
39372         }
39373         return (provides === null) ? url : url + provides;
39374     },
39375
39376     /**
39377      * Destroys the proxy by purging any event listeners and cancelling any active requests.
39378      */
39379     destroy: function(){
39380         this.purgeListeners();
39381     }
39382 });
39383
39384 // Apply the Observable prototype to the DataProxy class so that proxy instances can relay their
39385 // events to the class.  Allows for centralized listening of all proxy instances upon the DataProxy class.
39386 Ext.apply(Ext.data.DataProxy, Ext.util.Observable.prototype);
39387 Ext.util.Observable.call(Ext.data.DataProxy);
39388
39389 /**
39390  * @class Ext.data.DataProxy.Error
39391  * @extends Ext.Error
39392  * DataProxy Error extension.
39393  * constructor
39394  * @param {String} message Message describing the error.
39395  * @param {Record/Record[]} arg
39396  */
39397 Ext.data.DataProxy.Error = Ext.extend(Ext.Error, {
39398     constructor : function(message, arg) {
39399         this.arg = arg;
39400         Ext.Error.call(this, message);
39401     },
39402     name: 'Ext.data.DataProxy'
39403 });
39404 Ext.apply(Ext.data.DataProxy.Error.prototype, {
39405     lang: {
39406         'action-undefined': "DataProxy attempted to execute an API-action but found an undefined url / function.  Please review your Proxy url/api-configuration.",
39407         'api-invalid': 'Recieved an invalid API-configuration.  Please ensure your proxy API-configuration contains only the actions from Ext.data.Api.actions.'
39408     }
39409 });
39410
39411
39412 /**
39413  * @class Ext.data.Request
39414  * A simple Request class used internally to the data package to provide more generalized remote-requests
39415  * to a DataProxy.
39416  * TODO Not yet implemented.  Implement in Ext.data.Store#execute
39417  */
39418 Ext.data.Request = function(params) {
39419     Ext.apply(this, params);
39420 };
39421 Ext.data.Request.prototype = {
39422     /**
39423      * @cfg {String} action
39424      */
39425     action : undefined,
39426     /**
39427      * @cfg {Ext.data.Record[]/Ext.data.Record} rs The Store recordset associated with the request.
39428      */
39429     rs : undefined,
39430     /**
39431      * @cfg {Object} params HTTP request params
39432      */
39433     params: undefined,
39434     /**
39435      * @cfg {Function} callback The function to call when request is complete
39436      */
39437     callback : Ext.emptyFn,
39438     /**
39439      * @cfg {Object} scope The scope of the callback funtion
39440      */
39441     scope : undefined,
39442     /**
39443      * @cfg {Ext.data.DataReader} reader The DataReader instance which will parse the received response
39444      */
39445     reader : undefined
39446 };
39447 /**
39448  * @class Ext.data.Response
39449  * A generic response class to normalize response-handling internally to the framework.
39450  */
39451 Ext.data.Response = function(params) {
39452     Ext.apply(this, params);
39453 };
39454 Ext.data.Response.prototype = {
39455     /**
39456      * @cfg {String} action {@link Ext.data.Api#actions}
39457      */
39458     action: undefined,
39459     /**
39460      * @cfg {Boolean} success
39461      */
39462     success : undefined,
39463     /**
39464      * @cfg {String} message
39465      */
39466     message : undefined,
39467     /**
39468      * @cfg {Array/Object} data
39469      */
39470     data: undefined,
39471     /**
39472      * @cfg {Object} raw The raw response returned from server-code
39473      */
39474     raw: undefined,
39475     /**
39476      * @cfg {Ext.data.Record/Ext.data.Record[]} records related to the Request action
39477      */
39478     records: undefined
39479 };
39480 /**
39481  * @class Ext.data.ScriptTagProxy
39482  * @extends Ext.data.DataProxy
39483  * An implementation of Ext.data.DataProxy that reads a data object from a URL which may be in a domain
39484  * other than the originating domain of the running page.<br>
39485  * <p>
39486  * <b>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
39487  * of the running page, you must use this class, rather than HttpProxy.</b><br>
39488  * <p>
39489  * The content passed back from a server resource requested by a ScriptTagProxy <b>must</b> be executable JavaScript
39490  * source code because it is used as the source inside a &lt;script> tag.<br>
39491  * <p>
39492  * In order for the browser to process the returned data, the server must wrap the data object
39493  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
39494  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
39495  * depending on whether the callback name was passed:
39496  * <p>
39497  * <pre><code>
39498 boolean scriptTag = false;
39499 String cb = request.getParameter("callback");
39500 if (cb != null) {
39501     scriptTag = true;
39502     response.setContentType("text/javascript");
39503 } else {
39504     response.setContentType("application/x-json");
39505 }
39506 Writer out = response.getWriter();
39507 if (scriptTag) {
39508     out.write(cb + "(");
39509 }
39510 out.print(dataBlock.toJsonString());
39511 if (scriptTag) {
39512     out.write(");");
39513 }
39514 </code></pre>
39515  * <p>Below is a PHP example to do the same thing:</p><pre><code>
39516 $callback = $_REQUEST['callback'];
39517
39518 // Create the output object.
39519 $output = array('a' => 'Apple', 'b' => 'Banana');
39520
39521 //start output
39522 if ($callback) {
39523     header('Content-Type: text/javascript');
39524     echo $callback . '(' . json_encode($output) . ');';
39525 } else {
39526     header('Content-Type: application/x-json');
39527     echo json_encode($output);
39528 }
39529 </code></pre>
39530  * <p>Below is the ASP.Net code to do the same thing:</p><pre><code>
39531 String jsonString = "{success: true}";
39532 String cb = Request.Params.Get("callback");
39533 String responseString = "";
39534 if (!String.IsNullOrEmpty(cb)) {
39535     responseString = cb + "(" + jsonString + ")";
39536 } else {
39537     responseString = jsonString;
39538 }
39539 Response.Write(responseString);
39540 </code></pre>
39541  *
39542  * @constructor
39543  * @param {Object} config A configuration object.
39544  */
39545 Ext.data.ScriptTagProxy = function(config){
39546     Ext.apply(this, config);
39547
39548     Ext.data.ScriptTagProxy.superclass.constructor.call(this, config);
39549
39550     this.head = document.getElementsByTagName("head")[0];
39551
39552     /**
39553      * @event loadexception
39554      * <b>Deprecated</b> in favor of 'exception' event.
39555      * Fires if an exception occurs in the Proxy during data loading.  This event can be fired for one of two reasons:
39556      * <ul><li><b>The load call timed out.</b>  This means the load callback did not execute within the time limit
39557      * specified by {@link #timeout}.  In this case, this event will be raised and the
39558      * fourth parameter (read error) will be null.</li>
39559      * <li><b>The load succeeded but the reader could not read the response.</b>  This means the server returned
39560      * data, but the configured Reader threw an error while reading the data.  In this case, this event will be
39561      * raised and the caught error will be passed along as the fourth parameter of this event.</li></ul>
39562      * Note that this event is also relayed through {@link Ext.data.Store}, so you can listen for it directly
39563      * on any Store instance.
39564      * @param {Object} this
39565      * @param {Object} options The loading options that were specified (see {@link #load} for details).  If the load
39566      * call timed out, this parameter will be null.
39567      * @param {Object} arg The callback's arg object passed to the {@link #load} function
39568      * @param {Error} e The JavaScript Error object caught if the configured Reader could not read the data.
39569      * If the remote request returns success: false, this parameter will be null.
39570      */
39571 };
39572
39573 Ext.data.ScriptTagProxy.TRANS_ID = 1000;
39574
39575 Ext.extend(Ext.data.ScriptTagProxy, Ext.data.DataProxy, {
39576     /**
39577      * @cfg {String} url The URL from which to request the data object.
39578      */
39579     /**
39580      * @cfg {Number} timeout (optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
39581      */
39582     timeout : 30000,
39583     /**
39584      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
39585      * the server the name of the callback function set up by the load call to process the returned data object.
39586      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
39587      * javascript output which calls this named function passing the data object as its only parameter.
39588      */
39589     callbackParam : "callback",
39590     /**
39591      *  @cfg {Boolean} nocache (optional) Defaults to true. Disable caching by adding a unique parameter
39592      * name to the request.
39593      */
39594     nocache : true,
39595
39596     /**
39597      * HttpProxy implementation of DataProxy#doRequest
39598      * @param {String} action
39599      * @param {Ext.data.Record/Ext.data.Record[]} rs If action is <tt>read</tt>, rs will be null
39600      * @param {Object} params An object containing properties which are to be used as HTTP parameters
39601      * for the request to the remote server.
39602      * @param {Ext.data.DataReader} reader The Reader object which converts the data
39603      * object into a block of Ext.data.Records.
39604      * @param {Function} callback The function into which to pass the block of Ext.data.Records.
39605      * The function must be passed <ul>
39606      * <li>The Record block object</li>
39607      * <li>The "arg" argument from the load function</li>
39608      * <li>A boolean success indicator</li>
39609      * </ul>
39610      * @param {Object} scope The scope (<code>this</code> reference) in which the callback function is executed. Defaults to the browser window.
39611      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
39612      */
39613     doRequest : function(action, rs, params, reader, callback, scope, arg) {
39614         var p = Ext.urlEncode(Ext.apply(params, this.extraParams));
39615
39616         var url = this.buildUrl(action, rs);
39617         if (!url) {
39618             throw new Ext.data.Api.Error('invalid-url', url);
39619         }
39620         url = Ext.urlAppend(url, p);
39621
39622         if(this.nocache){
39623             url = Ext.urlAppend(url, '_dc=' + (new Date().getTime()));
39624         }
39625         var transId = ++Ext.data.ScriptTagProxy.TRANS_ID;
39626         var trans = {
39627             id : transId,
39628             action: action,
39629             cb : "stcCallback"+transId,
39630             scriptId : "stcScript"+transId,
39631             params : params,
39632             arg : arg,
39633             url : url,
39634             callback : callback,
39635             scope : scope,
39636             reader : reader
39637         };
39638         window[trans.cb] = this.createCallback(action, rs, trans);
39639         url += String.format("&{0}={1}", this.callbackParam, trans.cb);
39640         if(this.autoAbort !== false){
39641             this.abort();
39642         }
39643
39644         trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
39645
39646         var script = document.createElement("script");
39647         script.setAttribute("src", url);
39648         script.setAttribute("type", "text/javascript");
39649         script.setAttribute("id", trans.scriptId);
39650         this.head.appendChild(script);
39651
39652         this.trans = trans;
39653     },
39654
39655     // @private createCallback
39656     createCallback : function(action, rs, trans) {
39657         var self = this;
39658         return function(res) {
39659             self.trans = false;
39660             self.destroyTrans(trans, true);
39661             if (action === Ext.data.Api.actions.read) {
39662                 self.onRead.call(self, action, trans, res);
39663             } else {
39664                 self.onWrite.call(self, action, trans, res, rs);
39665             }
39666         };
39667     },
39668     /**
39669      * Callback for read actions
39670      * @param {String} action [Ext.data.Api.actions.create|read|update|destroy]
39671      * @param {Object} trans The request transaction object
39672      * @param {Object} res The server response
39673      * @protected
39674      */
39675     onRead : function(action, trans, res) {
39676         var result;
39677         try {
39678             result = trans.reader.readRecords(res);
39679         }catch(e){
39680             // @deprecated: fire loadexception
39681             this.fireEvent("loadexception", this, trans, res, e);
39682
39683             this.fireEvent('exception', this, 'response', action, trans, res, e);
39684             trans.callback.call(trans.scope||window, null, trans.arg, false);
39685             return;
39686         }
39687         if (result.success === false) {
39688             // @deprecated: fire old loadexception for backwards-compat.
39689             this.fireEvent('loadexception', this, trans, res);
39690
39691             this.fireEvent('exception', this, 'remote', action, trans, res, null);
39692         } else {
39693             this.fireEvent("load", this, res, trans.arg);
39694         }
39695         trans.callback.call(trans.scope||window, result, trans.arg, result.success);
39696     },
39697     /**
39698      * Callback for write actions
39699      * @param {String} action [Ext.data.Api.actions.create|read|update|destroy]
39700      * @param {Object} trans The request transaction object
39701      * @param {Object} res The server response
39702      * @protected
39703      */
39704     onWrite : function(action, trans, response, rs) {
39705         var reader = trans.reader;
39706         try {
39707             // though we already have a response object here in STP, run through readResponse to catch any meta-data exceptions.
39708             var res = reader.readResponse(action, response);
39709         } catch (e) {
39710             this.fireEvent('exception', this, 'response', action, trans, res, e);
39711             trans.callback.call(trans.scope||window, null, res, false);
39712             return;
39713         }
39714         if(!res.success === true){
39715             this.fireEvent('exception', this, 'remote', action, trans, res, rs);
39716             trans.callback.call(trans.scope||window, null, res, false);
39717             return;
39718         }
39719         this.fireEvent("write", this, action, res.data, res, rs, trans.arg );
39720         trans.callback.call(trans.scope||window, res.data, res, true);
39721     },
39722
39723     // private
39724     isLoading : function(){
39725         return this.trans ? true : false;
39726     },
39727
39728     /**
39729      * Abort the current server request.
39730      */
39731     abort : function(){
39732         if(this.isLoading()){
39733             this.destroyTrans(this.trans);
39734         }
39735     },
39736
39737     // private
39738     destroyTrans : function(trans, isLoaded){
39739         this.head.removeChild(document.getElementById(trans.scriptId));
39740         clearTimeout(trans.timeoutId);
39741         if(isLoaded){
39742             window[trans.cb] = undefined;
39743             try{
39744                 delete window[trans.cb];
39745             }catch(e){}
39746         }else{
39747             // if hasn't been loaded, wait for load to remove it to prevent script error
39748             window[trans.cb] = function(){
39749                 window[trans.cb] = undefined;
39750                 try{
39751                     delete window[trans.cb];
39752                 }catch(e){}
39753             };
39754         }
39755     },
39756
39757     // private
39758     handleFailure : function(trans){
39759         this.trans = false;
39760         this.destroyTrans(trans, false);
39761         if (trans.action === Ext.data.Api.actions.read) {
39762             // @deprecated firing loadexception
39763             this.fireEvent("loadexception", this, null, trans.arg);
39764         }
39765
39766         this.fireEvent('exception', this, 'response', trans.action, {
39767             response: null,
39768             options: trans.arg
39769         });
39770         trans.callback.call(trans.scope||window, null, trans.arg, false);
39771     },
39772
39773     // inherit docs
39774     destroy: function(){
39775         this.abort();
39776         Ext.data.ScriptTagProxy.superclass.destroy.call(this);
39777     }
39778 });/**
39779  * @class Ext.data.HttpProxy
39780  * @extends Ext.data.DataProxy
39781  * <p>An implementation of {@link Ext.data.DataProxy} that processes data requests within the same
39782  * domain of the originating page.</p>
39783  * <p><b>Note</b>: this class cannot be used to retrieve data from a domain other
39784  * than the domain from which the running page was served. For cross-domain requests, use a
39785  * {@link Ext.data.ScriptTagProxy ScriptTagProxy}.</p>
39786  * <p>Be aware that to enable the browser to parse an XML document, the server must set
39787  * the Content-Type header in the HTTP response to "<tt>text/xml</tt>".</p>
39788  * @constructor
39789  * @param {Object} conn
39790  * An {@link Ext.data.Connection} object, or options parameter to {@link Ext.Ajax#request}.
39791  * <p>Note that if this HttpProxy is being used by a {@link Ext.data.Store Store}, then the
39792  * Store's call to {@link #load} will override any specified <tt>callback</tt> and <tt>params</tt>
39793  * options. In this case, use the Store's {@link Ext.data.Store#events events} to modify parameters,
39794  * or react to loading events. The Store's {@link Ext.data.Store#baseParams baseParams} may also be
39795  * used to pass parameters known at instantiation time.</p>
39796  * <p>If an options parameter is passed, the singleton {@link Ext.Ajax} object will be used to make
39797  * the request.</p>
39798  */
39799 Ext.data.HttpProxy = function(conn){
39800     Ext.data.HttpProxy.superclass.constructor.call(this, conn);
39801
39802     /**
39803      * The Connection object (Or options parameter to {@link Ext.Ajax#request}) which this HttpProxy
39804      * uses to make requests to the server. Properties of this object may be changed dynamically to
39805      * change the way data is requested.
39806      * @property
39807      */
39808     this.conn = conn;
39809
39810     // nullify the connection url.  The url param has been copied to 'this' above.  The connection
39811     // url will be set during each execution of doRequest when buildUrl is called.  This makes it easier for users to override the
39812     // connection url during beforeaction events (ie: beforeload, beforewrite, etc).
39813     // Url is always re-defined during doRequest.
39814     this.conn.url = null;
39815
39816     this.useAjax = !conn || !conn.events;
39817
39818     // A hash containing active requests, keyed on action [Ext.data.Api.actions.create|read|update|destroy]
39819     var actions = Ext.data.Api.actions;
39820     this.activeRequest = {};
39821     for (var verb in actions) {
39822         this.activeRequest[actions[verb]] = undefined;
39823     }
39824 };
39825
39826 Ext.extend(Ext.data.HttpProxy, Ext.data.DataProxy, {
39827     /**
39828      * Return the {@link Ext.data.Connection} object being used by this Proxy.
39829      * @return {Connection} The Connection object. This object may be used to subscribe to events on
39830      * a finer-grained basis than the DataProxy events.
39831      */
39832     getConnection : function() {
39833         return this.useAjax ? Ext.Ajax : this.conn;
39834     },
39835
39836     /**
39837      * Used for overriding the url used for a single request.  Designed to be called during a beforeaction event.  Calling setUrl
39838      * will override any urls set via the api configuration parameter.  Set the optional parameter makePermanent to set the url for
39839      * all subsequent requests.  If not set to makePermanent, the next request will use the same url or api configuration defined
39840      * in the initial proxy configuration.
39841      * @param {String} url
39842      * @param {Boolean} makePermanent (Optional) [false]
39843      *
39844      * (e.g.: beforeload, beforesave, etc).
39845      */
39846     setUrl : function(url, makePermanent) {
39847         this.conn.url = url;
39848         if (makePermanent === true) {
39849             this.url = url;
39850             this.api = null;
39851             Ext.data.Api.prepare(this);
39852         }
39853     },
39854
39855     /**
39856      * HttpProxy implementation of DataProxy#doRequest
39857      * @param {String} action The crud action type (create, read, update, destroy)
39858      * @param {Ext.data.Record/Ext.data.Record[]} rs If action is load, rs will be null
39859      * @param {Object} params An object containing properties which are to be used as HTTP parameters
39860      * for the request to the remote server.
39861      * @param {Ext.data.DataReader} reader The Reader object which converts the data
39862      * object into a block of Ext.data.Records.
39863      * @param {Function} callback
39864      * <div class="sub-desc"><p>A function to be called after the request.
39865      * The <tt>callback</tt> is passed the following arguments:<ul>
39866      * <li><tt>r</tt> : Ext.data.Record[] The block of Ext.data.Records.</li>
39867      * <li><tt>options</tt>: Options object from the action request</li>
39868      * <li><tt>success</tt>: Boolean success indicator</li></ul></p></div>
39869      * @param {Object} scope The scope (<code>this</code> reference) in which the callback function is executed. Defaults to the browser window.
39870      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
39871      * @protected
39872      */
39873     doRequest : function(action, rs, params, reader, cb, scope, arg) {
39874         var  o = {
39875             method: (this.api[action]) ? this.api[action]['method'] : undefined,
39876             request: {
39877                 callback : cb,
39878                 scope : scope,
39879                 arg : arg
39880             },
39881             reader: reader,
39882             callback : this.createCallback(action, rs),
39883             scope: this
39884         };
39885
39886         // If possible, transmit data using jsonData || xmlData on Ext.Ajax.request (An installed DataWriter would have written it there.).
39887         // Use std HTTP params otherwise.
39888         if (params.jsonData) {
39889             o.jsonData = params.jsonData;
39890         } else if (params.xmlData) {
39891             o.xmlData = params.xmlData;
39892         } else {
39893             o.params = params || {};
39894         }
39895         // Set the connection url.  If this.conn.url is not null here,
39896         // the user must have overridden the url during a beforewrite/beforeload event-handler.
39897         // this.conn.url is nullified after each request.
39898         this.conn.url = this.buildUrl(action, rs);
39899
39900         if(this.useAjax){
39901
39902             Ext.applyIf(o, this.conn);
39903
39904             // If a currently running request is found for this action, abort it.
39905             if (this.activeRequest[action]) {
39906                 ////
39907                 // Disabled aborting activeRequest while implementing REST.  activeRequest[action] will have to become an array
39908                 // TODO ideas anyone?
39909                 //
39910                 //Ext.Ajax.abort(this.activeRequest[action]);
39911             }
39912             this.activeRequest[action] = Ext.Ajax.request(o);
39913         }else{
39914             this.conn.request(o);
39915         }
39916         // request is sent, nullify the connection url in preparation for the next request
39917         this.conn.url = null;
39918     },
39919
39920     /**
39921      * Returns a callback function for a request.  Note a special case is made for the
39922      * read action vs all the others.
39923      * @param {String} action [create|update|delete|load]
39924      * @param {Ext.data.Record[]} rs The Store-recordset being acted upon
39925      * @private
39926      */
39927     createCallback : function(action, rs) {
39928         return function(o, success, response) {
39929             this.activeRequest[action] = undefined;
39930             if (!success) {
39931                 if (action === Ext.data.Api.actions.read) {
39932                     // @deprecated: fire loadexception for backwards compat.
39933                     // TODO remove
39934                     this.fireEvent('loadexception', this, o, response);
39935                 }
39936                 this.fireEvent('exception', this, 'response', action, o, response);
39937                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
39938                 return;
39939             }
39940             if (action === Ext.data.Api.actions.read) {
39941                 this.onRead(action, o, response);
39942             } else {
39943                 this.onWrite(action, o, response, rs);
39944             }
39945         };
39946     },
39947
39948     /**
39949      * Callback for read action
39950      * @param {String} action Action name as per {@link Ext.data.Api.actions#read}.
39951      * @param {Object} o The request transaction object
39952      * @param {Object} res The server response
39953      * @fires loadexception (deprecated)
39954      * @fires exception
39955      * @fires load
39956      * @protected
39957      */
39958     onRead : function(action, o, response) {
39959         var result;
39960         try {
39961             result = o.reader.read(response);
39962         }catch(e){
39963             // @deprecated: fire old loadexception for backwards-compat.
39964             // TODO remove
39965             this.fireEvent('loadexception', this, o, response, e);
39966
39967             this.fireEvent('exception', this, 'response', action, o, response, e);
39968             o.request.callback.call(o.request.scope, null, o.request.arg, false);
39969             return;
39970         }
39971         if (result.success === false) {
39972             // @deprecated: fire old loadexception for backwards-compat.
39973             // TODO remove
39974             this.fireEvent('loadexception', this, o, response);
39975
39976             // Get DataReader read-back a response-object to pass along to exception event
39977             var res = o.reader.readResponse(action, response);
39978             this.fireEvent('exception', this, 'remote', action, o, res, null);
39979         }
39980         else {
39981             this.fireEvent('load', this, o, o.request.arg);
39982         }
39983         // TODO refactor onRead, onWrite to be more generalized now that we're dealing with Ext.data.Response instance
39984         // the calls to request.callback(...) in each will have to be made identical.
39985         // NOTE reader.readResponse does not currently return Ext.data.Response
39986         o.request.callback.call(o.request.scope, result, o.request.arg, result.success);
39987     },
39988     /**
39989      * Callback for write actions
39990      * @param {String} action [Ext.data.Api.actions.create|read|update|destroy]
39991      * @param {Object} trans The request transaction object
39992      * @param {Object} res The server response
39993      * @fires exception
39994      * @fires write
39995      * @protected
39996      */
39997     onWrite : function(action, o, response, rs) {
39998         var reader = o.reader;
39999         var res;
40000         try {
40001             res = reader.readResponse(action, response);
40002         } catch (e) {
40003             this.fireEvent('exception', this, 'response', action, o, response, e);
40004             o.request.callback.call(o.request.scope, null, o.request.arg, false);
40005             return;
40006         }
40007         if (res.success === true) {
40008             this.fireEvent('write', this, action, res.data, res, rs, o.request.arg);
40009         } else {
40010             this.fireEvent('exception', this, 'remote', action, o, res, rs);
40011         }
40012         // TODO refactor onRead, onWrite to be more generalized now that we're dealing with Ext.data.Response instance
40013         // the calls to request.callback(...) in each will have to be made similar.
40014         // NOTE reader.readResponse does not currently return Ext.data.Response
40015         o.request.callback.call(o.request.scope, res.data, res, res.success);
40016     },
40017
40018     // inherit docs
40019     destroy: function(){
40020         if(!this.useAjax){
40021             this.conn.abort();
40022         }else if(this.activeRequest){
40023             var actions = Ext.data.Api.actions;
40024             for (var verb in actions) {
40025                 if(this.activeRequest[actions[verb]]){
40026                     Ext.Ajax.abort(this.activeRequest[actions[verb]]);
40027                 }
40028             }
40029         }
40030         Ext.data.HttpProxy.superclass.destroy.call(this);
40031     }
40032 });/**
40033  * @class Ext.data.MemoryProxy
40034  * @extends Ext.data.DataProxy
40035  * An implementation of Ext.data.DataProxy that simply passes the data specified in its constructor
40036  * to the Reader when its load method is called.
40037  * @constructor
40038  * @param {Object} data The data object which the Reader uses to construct a block of Ext.data.Records.
40039  */
40040 Ext.data.MemoryProxy = function(data){
40041     // Must define a dummy api with "read" action to satisfy DataProxy#doRequest and Ext.data.Api#prepare *before* calling super
40042     var api = {};
40043     api[Ext.data.Api.actions.read] = true;
40044     Ext.data.MemoryProxy.superclass.constructor.call(this, {
40045         api: api
40046     });
40047     this.data = data;
40048 };
40049
40050 Ext.extend(Ext.data.MemoryProxy, Ext.data.DataProxy, {
40051     /**
40052      * @event loadexception
40053      * Fires if an exception occurs in the Proxy during data loading. Note that this event is also relayed
40054      * through {@link Ext.data.Store}, so you can listen for it directly on any Store instance.
40055      * @param {Object} this
40056      * @param {Object} arg The callback's arg object passed to the {@link #load} function
40057      * @param {Object} null This parameter does not apply and will always be null for MemoryProxy
40058      * @param {Error} e The JavaScript Error object caught if the configured Reader could not read the data
40059      */
40060
40061        /**
40062      * MemoryProxy implementation of DataProxy#doRequest
40063      * @param {String} action
40064      * @param {Ext.data.Record/Ext.data.Record[]} rs If action is load, rs will be null
40065      * @param {Object} params An object containing properties which are to be used as HTTP parameters
40066      * for the request to the remote server.
40067      * @param {Ext.data.DataReader} reader The Reader object which converts the data
40068      * object into a block of Ext.data.Records.
40069      * @param {Function} callback The function into which to pass the block of Ext.data.Records.
40070      * The function must be passed <ul>
40071      * <li>The Record block object</li>
40072      * <li>The "arg" argument from the load function</li>
40073      * <li>A boolean success indicator</li>
40074      * </ul>
40075      * @param {Object} scope The scope (<code>this</code> reference) in which the callback function is executed. Defaults to the browser window.
40076      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
40077      */
40078     doRequest : function(action, rs, params, reader, callback, scope, arg) {
40079         // No implementation for CRUD in MemoryProxy.  Assumes all actions are 'load'
40080         params = params || {};
40081         var result;
40082         try {
40083             result = reader.readRecords(this.data);
40084         }catch(e){
40085             // @deprecated loadexception
40086             this.fireEvent("loadexception", this, null, arg, e);
40087
40088             this.fireEvent('exception', this, 'response', action, arg, null, e);
40089             callback.call(scope, null, arg, false);
40090             return;
40091         }
40092         callback.call(scope, result, arg, true);
40093     }
40094 });/**
40095  * @class Ext.data.Types
40096  * <p>This is s static class containing the system-supplied data types which may be given to a {@link Ext.data.Field Field}.<p/>
40097  * <p>The properties in this class are used as type indicators in the {@link Ext.data.Field Field} class, so to
40098  * test whether a Field is of a certain type, compare the {@link Ext.data.Field#type type} property against properties
40099  * of this class.</p>
40100  * <p>Developers may add their own application-specific data types to this class. Definition names must be UPPERCASE.
40101  * each type definition must contain three properties:</p>
40102  * <div class="mdetail-params"><ul>
40103  * <li><code>convert</code> : <i>Function</i><div class="sub-desc">A function to convert raw data values from a data block into the data
40104  * to be stored in the Field. The function is passed the collowing parameters:
40105  * <div class="mdetail-params"><ul>
40106  * <li><b>v</b> : Mixed<div class="sub-desc">The data value as read by the Reader, if undefined will use
40107  * the configured <tt>{@link Ext.data.Field#defaultValue defaultValue}</tt>.</div></li>
40108  * <li><b>rec</b> : Mixed<div class="sub-desc">The data object containing the row as read by the Reader.
40109  * Depending on the Reader type, this could be an Array ({@link Ext.data.ArrayReader ArrayReader}), an object
40110  * ({@link Ext.data.JsonReader JsonReader}), or an XML element ({@link Ext.data.XMLReader XMLReader}).</div></li>
40111  * </ul></div></div></li>
40112  * <li><code>sortType</code> : <i>Function</i> <div class="sub-desc">A function to convert the stored data into comparable form, as defined by {@link Ext.data.SortTypes}.</div></li>
40113  * <li><code>type</code> : <i>String</i> <div class="sub-desc">A textual data type name.</div></li>
40114  * </ul></div>
40115  * <p>For example, to create a VELatLong field (See the Microsoft Bing Mapping API) containing the latitude/longitude value of a datapoint on a map from a JsonReader data block
40116  * which contained the properties <code>lat</code> and <code>long</code>, you would define a new data type like this:</p>
40117  *<pre><code>
40118 // Add a new Field data type which stores a VELatLong object in the Record.
40119 Ext.data.Types.VELATLONG = {
40120     convert: function(v, data) {
40121         return new VELatLong(data.lat, data.long);
40122     },
40123     sortType: function(v) {
40124         return v.Latitude;  // When sorting, order by latitude
40125     },
40126     type: 'VELatLong'
40127 };
40128 </code></pre>
40129  * <p>Then, when declaring a Record, use <pre><code>
40130 var types = Ext.data.Types; // allow shorthand type access
40131 UnitRecord = Ext.data.Record.create([
40132     { name: 'unitName', mapping: 'UnitName' },
40133     { name: 'curSpeed', mapping: 'CurSpeed', type: types.INT },
40134     { name: 'latitude', mapping: 'lat', type: types.FLOAT },
40135     { name: 'latitude', mapping: 'lat', type: types.FLOAT },
40136     { name: 'position', type: types.VELATLONG }
40137 ]);
40138 </code></pre>
40139  * @singleton
40140  */
40141 Ext.data.Types = new function(){
40142     var st = Ext.data.SortTypes;
40143     Ext.apply(this, {
40144         /**
40145          * @type Regexp
40146          * @property stripRe
40147          * A regular expression for stripping non-numeric characters from a numeric value. Defaults to <tt>/[\$,%]/g</tt>.
40148          * This should be overridden for localization.
40149          */
40150         stripRe: /[\$,%]/g,
40151         
40152         /**
40153          * @type Object.
40154          * @property AUTO
40155          * This data type means that no conversion is applied to the raw data before it is placed into a Record.
40156          */
40157         AUTO: {
40158             convert: function(v){ return v; },
40159             sortType: st.none,
40160             type: 'auto'
40161         },
40162
40163         /**
40164          * @type Object.
40165          * @property STRING
40166          * This data type means that the raw data is converted into a String before it is placed into a Record.
40167          */
40168         STRING: {
40169             convert: function(v){ return (v === undefined || v === null) ? '' : String(v); },
40170             sortType: st.asUCString,
40171             type: 'string'
40172         },
40173
40174         /**
40175          * @type Object.
40176          * @property INT
40177          * This data type means that the raw data is converted into an integer before it is placed into a Record.
40178          * <p>The synonym <code>INTEGER</code> is equivalent.</p>
40179          */
40180         INT: {
40181             convert: function(v){
40182                 return v !== undefined && v !== null && v !== '' ?
40183                     parseInt(String(v).replace(Ext.data.Types.stripRe, ''), 10) : (this.useNull ? null : 0);
40184             },
40185             sortType: st.none,
40186             type: 'int'
40187         },
40188         
40189         /**
40190          * @type Object.
40191          * @property FLOAT
40192          * This data type means that the raw data is converted into a number before it is placed into a Record.
40193          * <p>The synonym <code>NUMBER</code> is equivalent.</p>
40194          */
40195         FLOAT: {
40196             convert: function(v){
40197                 return v !== undefined && v !== null && v !== '' ?
40198                     parseFloat(String(v).replace(Ext.data.Types.stripRe, ''), 10) : (this.useNull ? null : 0);
40199             },
40200             sortType: st.none,
40201             type: 'float'
40202         },
40203         
40204         /**
40205          * @type Object.
40206          * @property BOOL
40207          * <p>This data type means that the raw data is converted into a boolean before it is placed into
40208          * a Record. The string "true" and the number 1 are converted to boolean <code>true</code>.</p>
40209          * <p>The synonym <code>BOOLEAN</code> is equivalent.</p>
40210          */
40211         BOOL: {
40212             convert: function(v){ return v === true || v === 'true' || v == 1; },
40213             sortType: st.none,
40214             type: 'bool'
40215         },
40216         
40217         /**
40218          * @type Object.
40219          * @property DATE
40220          * This data type means that the raw data is converted into a Date before it is placed into a Record.
40221          * The date format is specified in the constructor of the {@link Ext.data.Field} to which this type is
40222          * being applied.
40223          */
40224         DATE: {
40225             convert: function(v){
40226                 var df = this.dateFormat;
40227                 if(!v){
40228                     return null;
40229                 }
40230                 if(Ext.isDate(v)){
40231                     return v;
40232                 }
40233                 if(df){
40234                     if(df == 'timestamp'){
40235                         return new Date(v*1000);
40236                     }
40237                     if(df == 'time'){
40238                         return new Date(parseInt(v, 10));
40239                     }
40240                     return Date.parseDate(v, df);
40241                 }
40242                 var parsed = Date.parse(v);
40243                 return parsed ? new Date(parsed) : null;
40244             },
40245             sortType: st.asDate,
40246             type: 'date'
40247         }
40248     });
40249     
40250     Ext.apply(this, {
40251         /**
40252          * @type Object.
40253          * @property BOOLEAN
40254          * <p>This data type means that the raw data is converted into a boolean before it is placed into
40255          * a Record. The string "true" and the number 1 are converted to boolean <code>true</code>.</p>
40256          * <p>The synonym <code>BOOL</code> is equivalent.</p>
40257          */
40258         BOOLEAN: this.BOOL,
40259         /**
40260          * @type Object.
40261          * @property INTEGER
40262          * This data type means that the raw data is converted into an integer before it is placed into a Record.
40263          * <p>The synonym <code>INT</code> is equivalent.</p>
40264          */
40265         INTEGER: this.INT,
40266         /**
40267          * @type Object.
40268          * @property NUMBER
40269          * This data type means that the raw data is converted into a number before it is placed into a Record.
40270          * <p>The synonym <code>FLOAT</code> is equivalent.</p>
40271          */
40272         NUMBER: this.FLOAT    
40273     });
40274 };/**
40275  * @class Ext.data.JsonWriter
40276  * @extends Ext.data.DataWriter
40277  * DataWriter extension for writing an array or single {@link Ext.data.Record} object(s) in preparation for executing a remote CRUD action.
40278  */
40279 Ext.data.JsonWriter = Ext.extend(Ext.data.DataWriter, {
40280     /**
40281      * @cfg {Boolean} encode <p><tt>true</tt> to {@link Ext.util.JSON#encode JSON encode} the
40282      * {@link Ext.data.DataWriter#toHash hashed data} into a standard HTTP parameter named after this
40283      * Reader's <code>meta.root</code> property which, by default is imported from the associated Reader. Defaults to <tt>true</tt>.</p>
40284      * <p>If set to <code>false</code>, the hashed data is {@link Ext.util.JSON#encode JSON encoded}, along with
40285      * the associated {@link Ext.data.Store}'s {@link Ext.data.Store#baseParams baseParams}, into the POST body.</p>
40286      * <p>When using {@link Ext.data.DirectProxy}, set this to <tt>false</tt> since Ext.Direct.JsonProvider will perform
40287      * its own json-encoding.  In addition, if you're using {@link Ext.data.HttpProxy}, setting to <tt>false</tt>
40288      * will cause HttpProxy to transmit data using the <b>jsonData</b> configuration-params of {@link Ext.Ajax#request}
40289      * instead of <b>params</b>.</p>
40290      * <p>When using a {@link Ext.data.Store#restful} Store, some serverside frameworks are
40291      * tuned to expect data through the jsonData mechanism.  In those cases, one will want to set <b>encode: <tt>false</tt></b>, as in
40292      * let the lower-level connection object (eg: Ext.Ajax) do the encoding.</p>
40293      */
40294     encode : true,
40295     /**
40296      * @cfg {Boolean} encodeDelete False to send only the id to the server on delete, true to encode it in an object
40297      * literal, eg: <pre><code>
40298 {id: 1}
40299  * </code></pre> Defaults to <tt>false</tt>
40300      */
40301     encodeDelete: false,
40302     
40303     constructor : function(config){
40304         Ext.data.JsonWriter.superclass.constructor.call(this, config);    
40305     },
40306
40307     /**
40308      * <p>This method should not need to be called by application code, however it may be useful on occasion to
40309      * override it, or augment it with an {@link Function#createInterceptor interceptor} or {@link Function#createSequence sequence}.</p>
40310      * <p>The provided implementation encodes the serialized data representing the Store's modified Records into the Ajax request's
40311      * <code>params</code> according to the <code>{@link #encode}</code> setting.</p>
40312      * @param {Object} Ajax request params object to write into.
40313      * @param {Object} baseParams as defined by {@link Ext.data.Store#baseParams}.  The baseParms must be encoded by the extending class, eg: {@link Ext.data.JsonWriter}, {@link Ext.data.XmlWriter}.
40314      * @param {Object/Object[]} data Data object representing the serialized modified records from the Store. May be either a single object,
40315      * or an Array of objects - user implementations must handle both cases.
40316      */
40317     render : function(params, baseParams, data) {
40318         if (this.encode === true) {
40319             // Encode here now.
40320             Ext.apply(params, baseParams);
40321             params[this.meta.root] = Ext.encode(data);
40322         } else {
40323             // defer encoding for some other layer, probably in {@link Ext.Ajax#request}.  Place everything into "jsonData" key.
40324             var jdata = Ext.apply({}, baseParams);
40325             jdata[this.meta.root] = data;
40326             params.jsonData = jdata;
40327         }
40328     },
40329     /**
40330      * Implements abstract Ext.data.DataWriter#createRecord
40331      * @protected
40332      * @param {Ext.data.Record} rec
40333      * @return {Object}
40334      */
40335     createRecord : function(rec) {
40336        return this.toHash(rec);
40337     },
40338     /**
40339      * Implements abstract Ext.data.DataWriter#updateRecord
40340      * @protected
40341      * @param {Ext.data.Record} rec
40342      * @return {Object}
40343      */
40344     updateRecord : function(rec) {
40345         return this.toHash(rec);
40346
40347     },
40348     /**
40349      * Implements abstract Ext.data.DataWriter#destroyRecord
40350      * @protected
40351      * @param {Ext.data.Record} rec
40352      * @return {Object}
40353      */
40354     destroyRecord : function(rec){
40355         if(this.encodeDelete){
40356             var data = {};
40357             data[this.meta.idProperty] = rec.id;
40358             return data;
40359         }else{
40360             return rec.id;
40361         }
40362     }
40363 });/**
40364  * @class Ext.data.JsonReader
40365  * @extends Ext.data.DataReader
40366  * <p>Data reader class to create an Array of {@link Ext.data.Record} objects
40367  * from a JSON packet based on mappings in a provided {@link Ext.data.Record}
40368  * constructor.</p>
40369  * <p>Example code:</p>
40370  * <pre><code>
40371 var myReader = new Ext.data.JsonReader({
40372     // metadata configuration options:
40373     {@link #idProperty}: 'id'
40374     {@link #root}: 'rows',
40375     {@link #totalProperty}: 'results',
40376     {@link Ext.data.DataReader#messageProperty}: "msg"  // The element within the response that provides a user-feedback message (optional)
40377
40378     // the fields config option will internally create an {@link Ext.data.Record}
40379     // constructor that provides mapping for reading the record data objects
40380     {@link Ext.data.DataReader#fields fields}: [
40381         // map Record&#39;s 'firstname' field to data object&#39;s key of same name
40382         {name: 'name', mapping: 'firstname'},
40383         // map Record&#39;s 'job' field to data object&#39;s 'occupation' key
40384         {name: 'job', mapping: 'occupation'}
40385     ]
40386 });
40387 </code></pre>
40388  * <p>This would consume a JSON data object of the form:</p><pre><code>
40389 {
40390     results: 2000, // Reader&#39;s configured {@link #totalProperty}
40391     rows: [        // Reader&#39;s configured {@link #root}
40392         // record data objects:
40393         { {@link #idProperty id}: 1, firstname: 'Bill', occupation: 'Gardener' },
40394         { {@link #idProperty id}: 2, firstname: 'Ben' , occupation: 'Horticulturalist' },
40395         ...
40396     ]
40397 }
40398 </code></pre>
40399  * <p><b><u>Automatic configuration using metaData</u></b></p>
40400  * <p>It is possible to change a JsonReader's metadata at any time by including
40401  * a <b><tt>metaData</tt></b> property in the JSON data object. If the JSON data
40402  * object has a <b><tt>metaData</tt></b> property, a {@link Ext.data.Store Store}
40403  * object using this Reader will reconfigure itself to use the newly provided
40404  * field definition and fire its {@link Ext.data.Store#metachange metachange}
40405  * event. The metachange event handler may interrogate the <b><tt>metaData</tt></b>
40406  * property to perform any configuration required.</p>
40407  * <p>Note that reconfiguring a Store potentially invalidates objects which may
40408  * refer to Fields or Records which no longer exist.</p>
40409  * <p>To use this facility you would create the JsonReader like this:</p><pre><code>
40410 var myReader = new Ext.data.JsonReader();
40411 </code></pre>
40412  * <p>The first data packet from the server would configure the reader by
40413  * containing a <b><tt>metaData</tt></b> property <b>and</b> the data. For
40414  * example, the JSON data object might take the form:</p><pre><code>
40415 {
40416     metaData: {
40417         "{@link #idProperty}": "id",
40418         "{@link #root}": "rows",
40419         "{@link #totalProperty}": "results"
40420         "{@link #successProperty}": "success",
40421         "{@link Ext.data.DataReader#fields fields}": [
40422             {"name": "name"},
40423             {"name": "job", "mapping": "occupation"}
40424         ],
40425         // used by store to set its sortInfo
40426         "sortInfo":{
40427            "field": "name",
40428            "direction": "ASC"
40429         },
40430         // {@link Ext.PagingToolbar paging data} (if applicable)
40431         "start": 0,
40432         "limit": 2,
40433         // custom property
40434         "foo": "bar"
40435     },
40436     // Reader&#39;s configured {@link #successProperty}
40437     "success": true,
40438     // Reader&#39;s configured {@link #totalProperty}
40439     "results": 2000,
40440     // Reader&#39;s configured {@link #root}
40441     // (this data simulates 2 results {@link Ext.PagingToolbar per page})
40442     "rows": [ // <b>*Note:</b> this must be an Array
40443         { "id": 1, "name": "Bill", "occupation": "Gardener" },
40444         { "id": 2, "name":  "Ben", "occupation": "Horticulturalist" }
40445     ]
40446 }
40447  * </code></pre>
40448  * <p>The <b><tt>metaData</tt></b> property in the JSON data object should contain:</p>
40449  * <div class="mdetail-params"><ul>
40450  * <li>any of the configuration options for this class</li>
40451  * <li>a <b><tt>{@link Ext.data.Record#fields fields}</tt></b> property which
40452  * the JsonReader will use as an argument to the
40453  * {@link Ext.data.Record#create data Record create method} in order to
40454  * configure the layout of the Records it will produce.</li>
40455  * <li>a <b><tt>{@link Ext.data.Store#sortInfo sortInfo}</tt></b> property
40456  * which the JsonReader will use to set the {@link Ext.data.Store}'s
40457  * {@link Ext.data.Store#sortInfo sortInfo} property</li>
40458  * <li>any custom properties needed</li>
40459  * </ul></div>
40460  *
40461  * @constructor
40462  * Create a new JsonReader
40463  * @param {Object} meta Metadata configuration options.
40464  * @param {Array/Object} recordType
40465  * <p>Either an Array of {@link Ext.data.Field Field} definition objects (which
40466  * will be passed to {@link Ext.data.Record#create}, or a {@link Ext.data.Record Record}
40467  * constructor created from {@link Ext.data.Record#create}.</p>
40468  */
40469 Ext.data.JsonReader = function(meta, recordType){
40470     meta = meta || {};
40471     /**
40472      * @cfg {String} idProperty [id] Name of the property within a row object
40473      * that contains a record identifier value.  Defaults to <tt>id</tt>
40474      */
40475     /**
40476      * @cfg {String} successProperty [success] Name of the property from which to
40477      * retrieve the success attribute. Defaults to <tt>success</tt>.  See
40478      * {@link Ext.data.DataProxy}.{@link Ext.data.DataProxy#exception exception}
40479      * for additional information.
40480      */
40481     /**
40482      * @cfg {String} totalProperty [total] Name of the property from which to
40483      * retrieve the total number of records in the dataset. This is only needed
40484      * if the whole dataset is not passed in one go, but is being paged from
40485      * the remote server.  Defaults to <tt>total</tt>.
40486      */
40487     /**
40488      * @cfg {String} root [undefined] <b>Required</b>.  The name of the property
40489      * which contains the Array of row objects.  Defaults to <tt>undefined</tt>.
40490      * An exception will be thrown if the root property is undefined. The data
40491      * packet value for this property should be an empty array to clear the data
40492      * or show no data.
40493      */
40494     Ext.applyIf(meta, {
40495         idProperty: 'id',
40496         successProperty: 'success',
40497         totalProperty: 'total'
40498     });
40499
40500     Ext.data.JsonReader.superclass.constructor.call(this, meta, recordType || meta.fields);
40501 };
40502 Ext.extend(Ext.data.JsonReader, Ext.data.DataReader, {
40503     /**
40504      * This JsonReader's metadata as passed to the constructor, or as passed in
40505      * the last data packet's <b><tt>metaData</tt></b> property.
40506      * @type Mixed
40507      * @property meta
40508      */
40509     /**
40510      * This method is only used by a DataProxy which has retrieved data from a remote server.
40511      * @param {Object} response The XHR object which contains the JSON data in its responseText.
40512      * @return {Object} data A data block which is used by an Ext.data.Store object as
40513      * a cache of Ext.data.Records.
40514      */
40515     read : function(response){
40516         var json = response.responseText;
40517         var o = Ext.decode(json);
40518         if(!o) {
40519             throw {message: 'JsonReader.read: Json object not found'};
40520         }
40521         return this.readRecords(o);
40522     },
40523
40524     /*
40525      * TODO: refactor code between JsonReader#readRecords, #readResponse into 1 method.
40526      * there's ugly duplication going on due to maintaining backwards compat. with 2.0.  It's time to do this.
40527      */
40528     /**
40529      * Decode a JSON response from server.
40530      * @param {String} action [Ext.data.Api.actions.create|read|update|destroy]
40531      * @param {Object} response The XHR object returned through an Ajax server request.
40532      * @return {Response} A {@link Ext.data.Response Response} object containing the data response, and also status information.
40533      */
40534     readResponse : function(action, response) {
40535         var o = (response.responseText !== undefined) ? Ext.decode(response.responseText) : response;
40536         if(!o) {
40537             throw new Ext.data.JsonReader.Error('response');
40538         }
40539
40540         var root = this.getRoot(o);
40541         if (action === Ext.data.Api.actions.create) {
40542             var def = Ext.isDefined(root);
40543             if (def && Ext.isEmpty(root)) {
40544                 throw new Ext.data.JsonReader.Error('root-empty', this.meta.root);
40545             }
40546             else if (!def) {
40547                 throw new Ext.data.JsonReader.Error('root-undefined-response', this.meta.root);
40548             }
40549         }
40550
40551         // instantiate response object
40552         var res = new Ext.data.Response({
40553             action: action,
40554             success: this.getSuccess(o),
40555             data: (root) ? this.extractData(root, false) : [],
40556             message: this.getMessage(o),
40557             raw: o
40558         });
40559
40560         // blow up if no successProperty
40561         if (Ext.isEmpty(res.success)) {
40562             throw new Ext.data.JsonReader.Error('successProperty-response', this.meta.successProperty);
40563         }
40564         return res;
40565     },
40566
40567     /**
40568      * Create a data block containing Ext.data.Records from a JSON object.
40569      * @param {Object} o An object which contains an Array of row objects in the property specified
40570      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
40571      * which contains the total size of the dataset.
40572      * @return {Object} data A data block which is used by an Ext.data.Store object as
40573      * a cache of Ext.data.Records.
40574      */
40575     readRecords : function(o){
40576         /**
40577          * After any data loads, the raw JSON data is available for further custom processing.  If no data is
40578          * loaded or there is a load exception this property will be undefined.
40579          * @type Object
40580          */
40581         this.jsonData = o;
40582         if(o.metaData){
40583             this.onMetaChange(o.metaData);
40584         }
40585         var s = this.meta, Record = this.recordType,
40586             f = Record.prototype.fields, fi = f.items, fl = f.length, v;
40587
40588         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
40589         if(s.totalProperty){
40590             v = parseInt(this.getTotal(o), 10);
40591             if(!isNaN(v)){
40592                 totalRecords = v;
40593             }
40594         }
40595         if(s.successProperty){
40596             v = this.getSuccess(o);
40597             if(v === false || v === 'false'){
40598                 success = false;
40599             }
40600         }
40601
40602         // TODO return Ext.data.Response instance instead.  @see #readResponse
40603         return {
40604             success : success,
40605             records : this.extractData(root, true), // <-- true to return [Ext.data.Record]
40606             totalRecords : totalRecords
40607         };
40608     },
40609
40610     // private
40611     buildExtractors : function() {
40612         if(this.ef){
40613             return;
40614         }
40615         var s = this.meta, Record = this.recordType,
40616             f = Record.prototype.fields, fi = f.items, fl = f.length;
40617
40618         if(s.totalProperty) {
40619             this.getTotal = this.createAccessor(s.totalProperty);
40620         }
40621         if(s.successProperty) {
40622             this.getSuccess = this.createAccessor(s.successProperty);
40623         }
40624         if (s.messageProperty) {
40625             this.getMessage = this.createAccessor(s.messageProperty);
40626         }
40627         this.getRoot = s.root ? this.createAccessor(s.root) : function(p){return p;};
40628         if (s.id || s.idProperty) {
40629             var g = this.createAccessor(s.id || s.idProperty);
40630             this.getId = function(rec) {
40631                 var r = g(rec);
40632                 return (r === undefined || r === '') ? null : r;
40633             };
40634         } else {
40635             this.getId = function(){return null;};
40636         }
40637         var ef = [];
40638         for(var i = 0; i < fl; i++){
40639             f = fi[i];
40640             var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
40641             ef.push(this.createAccessor(map));
40642         }
40643         this.ef = ef;
40644     },
40645
40646     /**
40647      * @ignore
40648      * TODO This isn't used anywhere??  Don't we want to use this where possible instead of complex #createAccessor?
40649      */
40650     simpleAccess : function(obj, subsc) {
40651         return obj[subsc];
40652     },
40653
40654     /**
40655      * @ignore
40656      */
40657     createAccessor : function(){
40658         var re = /[\[\.]/;
40659         return function(expr) {
40660             if(Ext.isEmpty(expr)){
40661                 return Ext.emptyFn;
40662             }
40663             if(Ext.isFunction(expr)){
40664                 return expr;
40665             }
40666             var i = String(expr).search(re);
40667             if(i >= 0){
40668                 return new Function('obj', 'return obj' + (i > 0 ? '.' : '') + expr);
40669             }
40670             return function(obj){
40671                 return obj[expr];
40672             };
40673
40674         };
40675     }(),
40676
40677     /**
40678      * type-casts a single row of raw-data from server
40679      * @param {Object} data
40680      * @param {Array} items
40681      * @param {Integer} len
40682      * @private
40683      */
40684     extractValues : function(data, items, len) {
40685         var f, values = {};
40686         for(var j = 0; j < len; j++){
40687             f = items[j];
40688             var v = this.ef[j](data);
40689             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue, data);
40690         }
40691         return values;
40692     }
40693 });
40694
40695 /**
40696  * @class Ext.data.JsonReader.Error
40697  * Error class for JsonReader
40698  */
40699 Ext.data.JsonReader.Error = Ext.extend(Ext.Error, {
40700     constructor : function(message, arg) {
40701         this.arg = arg;
40702         Ext.Error.call(this, message);
40703     },
40704     name : 'Ext.data.JsonReader'
40705 });
40706 Ext.apply(Ext.data.JsonReader.Error.prototype, {
40707     lang: {
40708         'response': 'An error occurred while json-decoding your server response',
40709         'successProperty-response': 'Could not locate your "successProperty" in your server response.  Please review your JsonReader config to ensure the config-property "successProperty" matches the property in your server-response.  See the JsonReader docs.',
40710         'root-undefined-config': 'Your JsonReader was configured without a "root" property.  Please review your JsonReader config and make sure to define the root property.  See the JsonReader docs.',
40711         'idProperty-undefined' : 'Your JsonReader was configured without an "idProperty"  Please review your JsonReader configuration and ensure the "idProperty" is set (e.g.: "id").  See the JsonReader docs.',
40712         'root-empty': 'Data was expected to be returned by the server in the "root" property of the response.  Please review your JsonReader configuration to ensure the "root" property matches that returned in the server-response.  See JsonReader docs.'
40713     }
40714 });
40715 /**
40716  * @class Ext.data.ArrayReader
40717  * @extends Ext.data.JsonReader
40718  * <p>Data reader class to create an Array of {@link Ext.data.Record} objects from an Array.
40719  * Each element of that Array represents a row of data fields. The
40720  * fields are pulled into a Record object using as a subscript, the <code>mapping</code> property
40721  * of the field definition if it exists, or the field's ordinal position in the definition.</p>
40722  * <p>Example code:</p>
40723  * <pre><code>
40724 var Employee = Ext.data.Record.create([
40725     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
40726     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
40727 ]);
40728 var myReader = new Ext.data.ArrayReader({
40729     {@link #idIndex}: 0
40730 }, Employee);
40731 </code></pre>
40732  * <p>This would consume an Array like this:</p>
40733  * <pre><code>
40734 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
40735  * </code></pre>
40736  * @constructor
40737  * Create a new ArrayReader
40738  * @param {Object} meta Metadata configuration options.
40739  * @param {Array/Object} recordType
40740  * <p>Either an Array of {@link Ext.data.Field Field} definition objects (which
40741  * will be passed to {@link Ext.data.Record#create}, or a {@link Ext.data.Record Record}
40742  * constructor created from {@link Ext.data.Record#create}.</p>
40743  */
40744 Ext.data.ArrayReader = Ext.extend(Ext.data.JsonReader, {
40745     /**
40746      * @cfg {String} successProperty
40747      * @hide
40748      */
40749     /**
40750      * @cfg {Number} id (optional) The subscript within row Array that provides an ID for the Record.
40751      * Deprecated. Use {@link #idIndex} instead.
40752      */
40753     /**
40754      * @cfg {Number} idIndex (optional) The subscript within row Array that provides an ID for the Record.
40755      */
40756     /**
40757      * Create a data block containing Ext.data.Records from an Array.
40758      * @param {Object} o An Array of row objects which represents the dataset.
40759      * @return {Object} data A data block which is used by an Ext.data.Store object as
40760      * a cache of Ext.data.Records.
40761      */
40762     readRecords : function(o){
40763         this.arrayData = o;
40764         var s = this.meta,
40765             sid = s ? Ext.num(s.idIndex, s.id) : null,
40766             recordType = this.recordType,
40767             fields = recordType.prototype.fields,
40768             records = [],
40769             success = true,
40770             v;
40771
40772         var root = this.getRoot(o);
40773
40774         for(var i = 0, len = root.length; i < len; i++) {
40775             var n = root[i],
40776                 values = {},
40777                 id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
40778             for(var j = 0, jlen = fields.length; j < jlen; j++) {
40779                 var f = fields.items[j],
40780                     k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
40781                 v = n[k] !== undefined ? n[k] : f.defaultValue;
40782                 v = f.convert(v, n);
40783                 values[f.name] = v;
40784             }
40785             var record = new recordType(values, id);
40786             record.json = n;
40787             records[records.length] = record;
40788         }
40789
40790         var totalRecords = records.length;
40791
40792         if(s.totalProperty) {
40793             v = parseInt(this.getTotal(o), 10);
40794             if(!isNaN(v)) {
40795                 totalRecords = v;
40796             }
40797         }
40798         if(s.successProperty){
40799             v = this.getSuccess(o);
40800             if(v === false || v === 'false'){
40801                 success = false;
40802             }
40803         }
40804
40805         return {
40806             success : success,
40807             records : records,
40808             totalRecords : totalRecords
40809         };
40810     }
40811 });/**
40812  * @class Ext.data.ArrayStore
40813  * @extends Ext.data.Store
40814  * <p>Formerly known as "SimpleStore".</p>
40815  * <p>Small helper class to make creating {@link Ext.data.Store}s from Array data easier.
40816  * An ArrayStore will be automatically configured with a {@link Ext.data.ArrayReader}.</p>
40817  * <p>A store configuration would be something like:<pre><code>
40818 var store = new Ext.data.ArrayStore({
40819     // store configs
40820     autoDestroy: true,
40821     storeId: 'myStore',
40822     // reader configs
40823     idIndex: 0,  
40824     fields: [
40825        'company',
40826        {name: 'price', type: 'float'},
40827        {name: 'change', type: 'float'},
40828        {name: 'pctChange', type: 'float'},
40829        {name: 'lastChange', type: 'date', dateFormat: 'n/j h:ia'}
40830     ]
40831 });
40832  * </code></pre></p>
40833  * <p>This store is configured to consume a returned object of the form:<pre><code>
40834 var myData = [
40835     ['3m Co',71.72,0.02,0.03,'9/1 12:00am'],
40836     ['Alcoa Inc',29.01,0.42,1.47,'9/1 12:00am'],
40837     ['Boeing Co.',75.43,0.53,0.71,'9/1 12:00am'],
40838     ['Hewlett-Packard Co.',36.53,-0.03,-0.08,'9/1 12:00am'],
40839     ['Wal-Mart Stores, Inc.',45.45,0.73,1.63,'9/1 12:00am']
40840 ];
40841  * </code></pre>
40842  * An object literal of this form could also be used as the {@link #data} config option.</p>
40843  * <p><b>*Note:</b> Although not listed here, this class accepts all of the configuration options of 
40844  * <b>{@link Ext.data.ArrayReader ArrayReader}</b>.</p>
40845  * @constructor
40846  * @param {Object} config
40847  * @xtype arraystore
40848  */
40849 Ext.data.ArrayStore = Ext.extend(Ext.data.Store, {
40850     /**
40851      * @cfg {Ext.data.DataReader} reader @hide
40852      */
40853     constructor: function(config){
40854         Ext.data.ArrayStore.superclass.constructor.call(this, Ext.apply(config, {
40855             reader: new Ext.data.ArrayReader(config)
40856         }));
40857     },
40858
40859     loadData : function(data, append){
40860         if(this.expandData === true){
40861             var r = [];
40862             for(var i = 0, len = data.length; i < len; i++){
40863                 r[r.length] = [data[i]];
40864             }
40865             data = r;
40866         }
40867         Ext.data.ArrayStore.superclass.loadData.call(this, data, append);
40868     }
40869 });
40870 Ext.reg('arraystore', Ext.data.ArrayStore);
40871
40872 // backwards compat
40873 Ext.data.SimpleStore = Ext.data.ArrayStore;
40874 Ext.reg('simplestore', Ext.data.SimpleStore);/**
40875  * @class Ext.data.JsonStore
40876  * @extends Ext.data.Store
40877  * <p>Small helper class to make creating {@link Ext.data.Store}s from JSON data easier.
40878  * A JsonStore will be automatically configured with a {@link Ext.data.JsonReader}.</p>
40879  * <p>A store configuration would be something like:<pre><code>
40880 var store = new Ext.data.JsonStore({
40881     // store configs
40882     autoDestroy: true,
40883     url: 'get-images.php',
40884     storeId: 'myStore',
40885     // reader configs
40886     root: 'images',
40887     idProperty: 'name',
40888     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
40889 });
40890  * </code></pre></p>
40891  * <p>This store is configured to consume a returned object of the form:<pre><code>
40892 {
40893     images: [
40894         {name: 'Image one', url:'/GetImage.php?id=1', size:46.5, lastmod: new Date(2007, 10, 29)},
40895         {name: 'Image Two', url:'/GetImage.php?id=2', size:43.2, lastmod: new Date(2007, 10, 30)}
40896     ]
40897 }
40898  * </code></pre>
40899  * An object literal of this form could also be used as the {@link #data} config option.</p>
40900  * <p><b>*Note:</b> Although not listed here, this class accepts all of the configuration options of
40901  * <b>{@link Ext.data.JsonReader JsonReader}</b>.</p>
40902  * @constructor
40903  * @param {Object} config
40904  * @xtype jsonstore
40905  */
40906 Ext.data.JsonStore = Ext.extend(Ext.data.Store, {
40907     /**
40908      * @cfg {Ext.data.DataReader} reader @hide
40909      */
40910     constructor: function(config){
40911         Ext.data.JsonStore.superclass.constructor.call(this, Ext.apply(config, {
40912             reader: new Ext.data.JsonReader(config)
40913         }));
40914     }
40915 });
40916 Ext.reg('jsonstore', Ext.data.JsonStore);/**
40917  * @class Ext.data.XmlWriter
40918  * @extends Ext.data.DataWriter
40919  * DataWriter extension for writing an array or single {@link Ext.data.Record} object(s) in preparation for executing a remote CRUD action via XML.
40920  * XmlWriter uses an instance of {@link Ext.XTemplate} for maximum flexibility in defining your own custom XML schema if the default schema is not appropriate for your needs.
40921  * See the {@link #tpl} configuration-property.
40922  */
40923 Ext.data.XmlWriter = function(params) {
40924     Ext.data.XmlWriter.superclass.constructor.apply(this, arguments);
40925     // compile the XTemplate for rendering XML documents.
40926     this.tpl = (typeof(this.tpl) === 'string') ? new Ext.XTemplate(this.tpl).compile() : this.tpl.compile();
40927 };
40928 Ext.extend(Ext.data.XmlWriter, Ext.data.DataWriter, {
40929     /**
40930      * @cfg {String} documentRoot [xrequest] (Optional) The name of the XML document root-node.  <b>Note:</b>
40931      * this parameter is required </b>only when</b> sending extra {@link Ext.data.Store#baseParams baseParams} to the server
40932      * during a write-request -- if no baseParams are set, the {@link Ext.data.XmlReader#record} meta-property can
40933      * suffice as the XML document root-node for write-actions involving just a <b>single record</b>.  For requests
40934      * involving <b>multiple</b> records and <b>NO</b> baseParams, the {@link Ext.data.XmlWriter#root} property can
40935      * act as the XML document root.
40936      */
40937     documentRoot: 'xrequest',
40938     /**
40939      * @cfg {Boolean} forceDocumentRoot [false] Set to <tt>true</tt> to force XML documents having a root-node as defined
40940      * by {@link #documentRoot}, even with no baseParams defined.
40941      */
40942     forceDocumentRoot: false,
40943     /**
40944      * @cfg {String} root [records] The name of the containing element which will contain the nodes of an write-action involving <b>multiple</b> records.  Each
40945      * xml-record written to the server will be wrapped in an element named after {@link Ext.data.XmlReader#record} property.
40946      * eg:
40947 <code><pre>
40948 &lt;?xml version="1.0" encoding="UTF-8"?>
40949 &lt;user>&lt;first>Barney&lt;/first>&lt;/user>
40950 </code></pre>
40951      * However, when <b>multiple</b> records are written in a batch-operation, these records must be wrapped in a containing
40952      * Element.
40953      * eg:
40954 <code><pre>
40955 &lt;?xml version="1.0" encoding="UTF-8"?>
40956     &lt;records>
40957         &lt;first>Barney&lt;/first>&lt;/user>
40958         &lt;records>&lt;first>Barney&lt;/first>&lt;/user>
40959     &lt;/records>
40960 </code></pre>
40961      * Defaults to <tt>records</tt>.  Do not confuse the nature of this property with that of {@link #documentRoot}
40962      */
40963     root: 'records',
40964     /**
40965      * @cfg {String} xmlVersion [1.0] The <tt>version</tt> written to header of xml documents.
40966 <code><pre>&lt;?xml version="1.0" encoding="ISO-8859-15"?></pre></code>
40967      */
40968     xmlVersion : '1.0',
40969     /**
40970      * @cfg {String} xmlEncoding [ISO-8859-15] The <tt>encoding</tt> written to header of xml documents.
40971 <code><pre>&lt;?xml version="1.0" encoding="ISO-8859-15"?></pre></code>
40972      */
40973     xmlEncoding: 'ISO-8859-15',
40974     /**
40975      * @cfg {String/Ext.XTemplate} tpl The XML template used to render {@link Ext.data.Api#actions write-actions} to your server.
40976      * <p>One can easily provide his/her own custom {@link Ext.XTemplate#constructor template-definition} if the default does not suffice.</p>
40977      * <p>Defaults to:</p>
40978 <code><pre>
40979 &lt;?xml version="{version}" encoding="{encoding}"?>
40980     &lt;tpl if="documentRoot">&lt;{documentRoot}>
40981     &lt;tpl for="baseParams">
40982         &lt;tpl for=".">
40983             &lt;{name}>{value}&lt;/{name}>
40984         &lt;/tpl>
40985     &lt;/tpl>
40986     &lt;tpl if="records.length &gt; 1">&lt;{root}>',
40987     &lt;tpl for="records">
40988         &lt;{parent.record}>
40989         &lt;tpl for=".">
40990             &lt;{name}>{value}&lt;/{name}>
40991         &lt;/tpl>
40992         &lt;/{parent.record}>
40993     &lt;/tpl>
40994     &lt;tpl if="records.length &gt; 1">&lt;/{root}>&lt;/tpl>
40995     &lt;tpl if="documentRoot">&lt;/{documentRoot}>&lt;/tpl>
40996 </pre></code>
40997      * <p>Templates will be called with the following API</p>
40998      * <ul>
40999      * <li>{String} version [1.0] The xml version.</li>
41000      * <li>{String} encoding [ISO-8859-15] The xml encoding.</li>
41001      * <li>{String/false} documentRoot The XML document root-node name or <tt>false</tt> if not required.  See {@link #documentRoot} and {@link #forceDocumentRoot}.</li>
41002      * <li>{String} record The meta-data parameter defined on your {@link Ext.data.XmlReader#record} configuration represents the name of the xml-tag containing each record.</li>
41003      * <li>{String} root The meta-data parameter defined by {@link Ext.data.XmlWriter#root} configuration-parameter.  Represents the name of the xml root-tag when sending <b>multiple</b> records to the server.</li>
41004      * <li>{Array} records The records being sent to the server, ie: the subject of the write-action being performed.  The records parameter will be always be an array, even when only a single record is being acted upon.
41005      *     Each item within the records array will contain an array of field objects having the following properties:
41006      *     <ul>
41007      *         <li>{String} name The field-name of the record as defined by your {@link Ext.data.Record#create Ext.data.Record definition}.  The "mapping" property will be used, otherwise it will match the "name" property.  Use this parameter to define the XML tag-name of the property.</li>
41008      *         <li>{Mixed} value The record value of the field enclosed within XML tags specified by name property above.</li>
41009      *     </ul></li>
41010      * <li>{Array} baseParams.  The baseParams as defined upon {@link Ext.data.Store#baseParams}.  Note that the baseParams have been converted into an array of [{name : "foo", value: "bar"}, ...] pairs in the same manner as the <b>records</b> parameter above.  See {@link #documentRoot} and {@link #forceDocumentRoot}.</li>
41011      * </ul>
41012      */
41013     // Encoding the ? here in case it's being included by some kind of page that will parse it (eg. PHP)
41014     tpl: '<tpl for="."><\u003fxml version="{version}" encoding="{encoding}"\u003f><tpl if="documentRoot"><{documentRoot}><tpl for="baseParams"><tpl for="."><{name}>{value}</{name}></tpl></tpl></tpl><tpl if="records.length&gt;1"><{root}></tpl><tpl for="records"><{parent.record}><tpl for="."><{name}>{value}</{name}></tpl></{parent.record}></tpl><tpl if="records.length&gt;1"></{root}></tpl><tpl if="documentRoot"></{documentRoot}></tpl></tpl>',
41015
41016
41017     /**
41018      * XmlWriter implementation of the final stage of a write action.
41019      * @param {Object} params Transport-proxy's (eg: {@link Ext.Ajax#request}) params-object to write-to.
41020      * @param {Object} baseParams as defined by {@link Ext.data.Store#baseParams}.  The baseParms must be encoded by the extending class, eg: {@link Ext.data.JsonWriter}, {@link Ext.data.XmlWriter}.
41021      * @param {Object/Object[]} data Data-object representing the compiled Store-recordset.
41022      */
41023     render : function(params, baseParams, data) {
41024         baseParams = this.toArray(baseParams);
41025         params.xmlData = this.tpl.applyTemplate({
41026             version: this.xmlVersion,
41027             encoding: this.xmlEncoding,
41028             documentRoot: (baseParams.length > 0 || this.forceDocumentRoot === true) ? this.documentRoot : false,
41029             record: this.meta.record,
41030             root: this.root,
41031             baseParams: baseParams,
41032             records: (Ext.isArray(data[0])) ? data : [data]
41033         });
41034     },
41035
41036     /**
41037      * createRecord
41038      * @protected
41039      * @param {Ext.data.Record} rec
41040      * @return {Array} Array of <tt>name:value</tt> pairs for attributes of the {@link Ext.data.Record}.  See {@link Ext.data.DataWriter#toHash}.
41041      */
41042     createRecord : function(rec) {
41043         return this.toArray(this.toHash(rec));
41044     },
41045
41046     /**
41047      * updateRecord
41048      * @protected
41049      * @param {Ext.data.Record} rec
41050      * @return {Array} Array of {name:value} pairs for attributes of the {@link Ext.data.Record}.  See {@link Ext.data.DataWriter#toHash}.
41051      */
41052     updateRecord : function(rec) {
41053         return this.toArray(this.toHash(rec));
41054
41055     },
41056     /**
41057      * destroyRecord
41058      * @protected
41059      * @param {Ext.data.Record} rec
41060      * @return {Array} Array containing a attribute-object (name/value pair) representing the {@link Ext.data.DataReader#idProperty idProperty}.
41061      */
41062     destroyRecord : function(rec) {
41063         var data = {};
41064         data[this.meta.idProperty] = rec.id;
41065         return this.toArray(data);
41066     }
41067 });
41068 /**
41069  * @class Ext.data.XmlReader
41070  * @extends Ext.data.DataReader
41071  * <p>Data reader class to create an Array of {@link Ext.data.Record} objects from an XML document
41072  * based on mappings in a provided {@link Ext.data.Record} constructor.</p>
41073  * <p><b>Note</b>: that in order for the browser to parse a returned XML document, the Content-Type
41074  * header in the HTTP response must be set to "text/xml" or "application/xml".</p>
41075  * <p>Example code:</p>
41076  * <pre><code>
41077 var Employee = Ext.data.Record.create([
41078    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it is the same as "name"
41079    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
41080 ]);
41081 var myReader = new Ext.data.XmlReader({
41082    totalProperty: "results", // The element which contains the total dataset size (optional)
41083    record: "row",           // The repeated element which contains row information
41084    idProperty: "id"         // The element within the row that provides an ID for the record (optional)
41085    messageProperty: "msg"   // The element within the response that provides a user-feedback message (optional)
41086 }, Employee);
41087 </code></pre>
41088  * <p>
41089  * This would consume an XML file like this:
41090  * <pre><code>
41091 &lt;?xml version="1.0" encoding="UTF-8"?>
41092 &lt;dataset>
41093  &lt;results>2&lt;/results>
41094  &lt;row>
41095    &lt;id>1&lt;/id>
41096    &lt;name>Bill&lt;/name>
41097    &lt;occupation>Gardener&lt;/occupation>
41098  &lt;/row>
41099  &lt;row>
41100    &lt;id>2&lt;/id>
41101    &lt;name>Ben&lt;/name>
41102    &lt;occupation>Horticulturalist&lt;/occupation>
41103  &lt;/row>
41104 &lt;/dataset>
41105 </code></pre>
41106  * @cfg {String} totalProperty The DomQuery path from which to retrieve the total number of records
41107  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
41108  * paged from the remote server.
41109  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
41110  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
41111  * @cfg {String} successProperty The DomQuery path to the success attribute used by forms.
41112  * @cfg {String} idPath The DomQuery path relative from the record element to the element that contains
41113  * a record identifier value.
41114  * @constructor
41115  * Create a new XmlReader.
41116  * @param {Object} meta Metadata configuration options
41117  * @param {Object} recordType Either an Array of field definition objects as passed to
41118  * {@link Ext.data.Record#create}, or a Record constructor object created using {@link Ext.data.Record#create}.
41119  */
41120 Ext.data.XmlReader = function(meta, recordType){
41121     meta = meta || {};
41122
41123     // backwards compat, convert idPath or id / success
41124     Ext.applyIf(meta, {
41125         idProperty: meta.idProperty || meta.idPath || meta.id,
41126         successProperty: meta.successProperty || meta.success
41127     });
41128
41129     Ext.data.XmlReader.superclass.constructor.call(this, meta, recordType || meta.fields);
41130 };
41131 Ext.extend(Ext.data.XmlReader, Ext.data.DataReader, {
41132     /**
41133      * This method is only used by a DataProxy which has retrieved data from a remote server.
41134      * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
41135      * to contain a property called <tt>responseXML</tt> which refers to an XML document object.
41136      * @return {Object} records A data block which is used by an {@link Ext.data.Store} as
41137      * a cache of Ext.data.Records.
41138      */
41139     read : function(response){
41140         var doc = response.responseXML;
41141         if(!doc) {
41142             throw {message: "XmlReader.read: XML Document not available"};
41143         }
41144         return this.readRecords(doc);
41145     },
41146
41147     /**
41148      * Create a data block containing Ext.data.Records from an XML document.
41149      * @param {Object} doc A parsed XML document.
41150      * @return {Object} records A data block which is used by an {@link Ext.data.Store} as
41151      * a cache of Ext.data.Records.
41152      */
41153     readRecords : function(doc){
41154         /**
41155          * After any data loads/reads, the raw XML Document is available for further custom processing.
41156          * @type XMLDocument
41157          */
41158         this.xmlData = doc;
41159
41160         var root    = doc.documentElement || doc,
41161             q       = Ext.DomQuery,
41162             totalRecords = 0,
41163             success = true;
41164
41165         if(this.meta.totalProperty){
41166             totalRecords = this.getTotal(root, 0);
41167         }
41168         if(this.meta.successProperty){
41169             success = this.getSuccess(root);
41170         }
41171
41172         var records = this.extractData(q.select(this.meta.record, root), true); // <-- true to return Ext.data.Record[]
41173
41174         // TODO return Ext.data.Response instance.  @see #readResponse
41175         return {
41176             success : success,
41177             records : records,
41178             totalRecords : totalRecords || records.length
41179         };
41180     },
41181
41182     /**
41183      * Decode an XML response from server.
41184      * @param {String} action [{@link Ext.data.Api#actions} create|read|update|destroy]
41185      * @param {Object} response HTTP Response object from browser.
41186      * @return {Ext.data.Response} An instance of {@link Ext.data.Response}
41187      */
41188     readResponse : function(action, response) {
41189         var q = Ext.DomQuery,
41190             doc = response.responseXML,
41191             root = doc.documentElement || doc;
41192
41193         // create general Response instance.
41194         var res = new Ext.data.Response({
41195             action: action,
41196             success : this.getSuccess(root),
41197             message: this.getMessage(root),
41198             data: this.extractData(q.select(this.meta.record, root) || q.select(this.meta.root, root), false),
41199             raw: doc
41200         });
41201
41202         if (Ext.isEmpty(res.success)) {
41203             throw new Ext.data.DataReader.Error('successProperty-response', this.meta.successProperty);
41204         }
41205
41206         // Create actions from a response having status 200 must return pk
41207         if (action === Ext.data.Api.actions.create) {
41208             var def = Ext.isDefined(res.data);
41209             if (def && Ext.isEmpty(res.data)) {
41210                 throw new Ext.data.JsonReader.Error('root-empty', this.meta.root);
41211             }
41212             else if (!def) {
41213                 throw new Ext.data.JsonReader.Error('root-undefined-response', this.meta.root);
41214             }
41215         }
41216         return res;
41217     },
41218
41219     getSuccess : function() {
41220         return true;
41221     },
41222
41223     /**
41224      * build response-data extractor functions.
41225      * @private
41226      * @ignore
41227      */
41228     buildExtractors : function() {
41229         if(this.ef){
41230             return;
41231         }
41232         var s       = this.meta,
41233             Record  = this.recordType,
41234             f       = Record.prototype.fields,
41235             fi      = f.items,
41236             fl      = f.length;
41237
41238         if(s.totalProperty) {
41239             this.getTotal = this.createAccessor(s.totalProperty);
41240         }
41241         if(s.successProperty) {
41242             this.getSuccess = this.createAccessor(s.successProperty);
41243         }
41244         if (s.messageProperty) {
41245             this.getMessage = this.createAccessor(s.messageProperty);
41246         }
41247         this.getRoot = function(res) {
41248             return (!Ext.isEmpty(res[this.meta.record])) ? res[this.meta.record] : res[this.meta.root];
41249         };
41250         if (s.idPath || s.idProperty) {
41251             var g = this.createAccessor(s.idPath || s.idProperty);
41252             this.getId = function(rec) {
41253                 var id = g(rec) || rec.id;
41254                 return (id === undefined || id === '') ? null : id;
41255             };
41256         } else {
41257             this.getId = function(){return null;};
41258         }
41259         var ef = [];
41260         for(var i = 0; i < fl; i++){
41261             f = fi[i];
41262             var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
41263             ef.push(this.createAccessor(map));
41264         }
41265         this.ef = ef;
41266     },
41267
41268     /**
41269      * Creates a function to return some particular key of data from a response.
41270      * @param {String} key
41271      * @return {Function}
41272      * @private
41273      * @ignore
41274      */
41275     createAccessor : function(){
41276         var q = Ext.DomQuery;
41277         return function(key) {
41278             if (Ext.isFunction(key)) {
41279                 return key;
41280             }
41281             switch(key) {
41282                 case this.meta.totalProperty:
41283                     return function(root, def){
41284                         return q.selectNumber(key, root, def);
41285                     };
41286                     break;
41287                 case this.meta.successProperty:
41288                     return function(root, def) {
41289                         var sv = q.selectValue(key, root, true);
41290                         var success = sv !== false && sv !== 'false';
41291                         return success;
41292                     };
41293                     break;
41294                 default:
41295                     return function(root, def) {
41296                         return q.selectValue(key, root, def);
41297                     };
41298                     break;
41299             }
41300         };
41301     }(),
41302
41303     /**
41304      * extracts values and type-casts a row of data from server, extracted by #extractData
41305      * @param {Hash} data
41306      * @param {Ext.data.Field[]} items
41307      * @param {Number} len
41308      * @private
41309      * @ignore
41310      */
41311     extractValues : function(data, items, len) {
41312         var f, values = {};
41313         for(var j = 0; j < len; j++){
41314             f = items[j];
41315             var v = this.ef[j](data);
41316             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue, data);
41317         }
41318         return values;
41319     }
41320 });/**
41321  * @class Ext.data.XmlStore
41322  * @extends Ext.data.Store
41323  * <p>Small helper class to make creating {@link Ext.data.Store}s from XML data easier.
41324  * A XmlStore will be automatically configured with a {@link Ext.data.XmlReader}.</p>
41325  * <p>A store configuration would be something like:<pre><code>
41326 var store = new Ext.data.XmlStore({
41327     // store configs
41328     autoDestroy: true,
41329     storeId: 'myStore',
41330     url: 'sheldon.xml', // automatically configures a HttpProxy
41331     // reader configs
41332     record: 'Item', // records will have an "Item" tag
41333     idPath: 'ASIN',
41334     totalRecords: '@TotalResults'
41335     fields: [
41336         // set up the fields mapping into the xml doc
41337         // The first needs mapping, the others are very basic
41338         {name: 'Author', mapping: 'ItemAttributes > Author'},
41339         'Title', 'Manufacturer', 'ProductGroup'
41340     ]
41341 });
41342  * </code></pre></p>
41343  * <p>This store is configured to consume a returned object of the form:<pre><code>
41344 &#60?xml version="1.0" encoding="UTF-8"?>
41345 &#60ItemSearchResponse xmlns="http://webservices.amazon.com/AWSECommerceService/2009-05-15">
41346     &#60Items>
41347         &#60Request>
41348             &#60IsValid>True&#60/IsValid>
41349             &#60ItemSearchRequest>
41350                 &#60Author>Sidney Sheldon&#60/Author>
41351                 &#60SearchIndex>Books&#60/SearchIndex>
41352             &#60/ItemSearchRequest>
41353         &#60/Request>
41354         &#60TotalResults>203&#60/TotalResults>
41355         &#60TotalPages>21&#60/TotalPages>
41356         &#60Item>
41357             &#60ASIN>0446355453&#60/ASIN>
41358             &#60DetailPageURL>
41359                 http://www.amazon.com/
41360             &#60/DetailPageURL>
41361             &#60ItemAttributes>
41362                 &#60Author>Sidney Sheldon&#60/Author>
41363                 &#60Manufacturer>Warner Books&#60/Manufacturer>
41364                 &#60ProductGroup>Book&#60/ProductGroup>
41365                 &#60Title>Master of the Game&#60/Title>
41366             &#60/ItemAttributes>
41367         &#60/Item>
41368     &#60/Items>
41369 &#60/ItemSearchResponse>
41370  * </code></pre>
41371  * An object literal of this form could also be used as the {@link #data} config option.</p>
41372  * <p><b>Note:</b> Although not listed here, this class accepts all of the configuration options of 
41373  * <b>{@link Ext.data.XmlReader XmlReader}</b>.</p>
41374  * @constructor
41375  * @param {Object} config
41376  * @xtype xmlstore
41377  */
41378 Ext.data.XmlStore = Ext.extend(Ext.data.Store, {
41379     /**
41380      * @cfg {Ext.data.DataReader} reader @hide
41381      */
41382     constructor: function(config){
41383         Ext.data.XmlStore.superclass.constructor.call(this, Ext.apply(config, {
41384             reader: new Ext.data.XmlReader(config)
41385         }));
41386     }
41387 });
41388 Ext.reg('xmlstore', Ext.data.XmlStore);/**
41389  * @class Ext.data.GroupingStore
41390  * @extends Ext.data.Store
41391  * A specialized store implementation that provides for grouping records by one of the available fields. This
41392  * is usually used in conjunction with an {@link Ext.grid.GroupingView} to provide the data model for
41393  * a grouped GridPanel.
41394  *
41395  * Internally, GroupingStore is simply a normal Store with multi sorting enabled from the start. The grouping field
41396  * and direction are always injected as the first sorter pair. GroupingView picks up on the configured groupField and
41397  * builds grid rows appropriately.
41398  *
41399  * @constructor
41400  * Creates a new GroupingStore.
41401  * @param {Object} config A config object containing the objects needed for the Store to access data,
41402  * and read the data into Records.
41403  * @xtype groupingstore
41404  */
41405 Ext.data.GroupingStore = Ext.extend(Ext.data.Store, {
41406
41407     //inherit docs
41408     constructor: function(config) {
41409         config = config || {};
41410
41411         //We do some preprocessing here to massage the grouping + sorting options into a single
41412         //multi sort array. If grouping and sorting options are both presented to the constructor,
41413         //the sorters array consists of the grouping sorter object followed by the sorting sorter object
41414         //see Ext.data.Store's sorting functions for details about how multi sorting works
41415         this.hasMultiSort  = true;
41416         this.multiSortInfo = this.multiSortInfo || {sorters: []};
41417
41418         var sorters    = this.multiSortInfo.sorters,
41419             groupField = config.groupField || this.groupField,
41420             sortInfo   = config.sortInfo || this.sortInfo,
41421             groupDir   = config.groupDir || this.groupDir;
41422
41423         //add the grouping sorter object first
41424         if(groupField){
41425             sorters.push({
41426                 field    : groupField,
41427                 direction: groupDir
41428             });
41429         }
41430
41431         //add the sorting sorter object if it is present
41432         if (sortInfo) {
41433             sorters.push(sortInfo);
41434         }
41435
41436         Ext.data.GroupingStore.superclass.constructor.call(this, config);
41437
41438         this.addEvents(
41439           /**
41440            * @event groupchange
41441            * Fired whenever a call to store.groupBy successfully changes the grouping on the store
41442            * @param {Ext.data.GroupingStore} store The grouping store
41443            * @param {String} groupField The field that the store is now grouped by
41444            */
41445           'groupchange'
41446         );
41447
41448         this.applyGroupField();
41449     },
41450
41451     /**
41452      * @cfg {String} groupField
41453      * The field name by which to sort the store's data (defaults to '').
41454      */
41455     /**
41456      * @cfg {Boolean} remoteGroup
41457      * True if the grouping should apply on the server side, false if it is local only (defaults to false).  If the
41458      * grouping is local, it can be applied immediately to the data.  If it is remote, then it will simply act as a
41459      * helper, automatically sending the grouping field name as the 'groupBy' param with each XHR call.
41460      */
41461     remoteGroup : false,
41462     /**
41463      * @cfg {Boolean} groupOnSort
41464      * True to sort the data on the grouping field when a grouping operation occurs, false to sort based on the
41465      * existing sort info (defaults to false).
41466      */
41467     groupOnSort:false,
41468
41469     /**
41470      * @cfg {String} groupDir
41471      * The direction to sort the groups. Defaults to <tt>'ASC'</tt>.
41472      */
41473     groupDir : 'ASC',
41474
41475     /**
41476      * Clears any existing grouping and refreshes the data using the default sort.
41477      */
41478     clearGrouping : function(){
41479         this.groupField = false;
41480
41481         if(this.remoteGroup){
41482             if(this.baseParams){
41483                 delete this.baseParams.groupBy;
41484                 delete this.baseParams.groupDir;
41485             }
41486             var lo = this.lastOptions;
41487             if(lo && lo.params){
41488                 delete lo.params.groupBy;
41489                 delete lo.params.groupDir;
41490             }
41491
41492             this.reload();
41493         }else{
41494             this.sort();
41495             this.fireEvent('datachanged', this);
41496         }
41497     },
41498
41499     /**
41500      * Groups the data by the specified field.
41501      * @param {String} field The field name by which to sort the store's data
41502      * @param {Boolean} forceRegroup (optional) True to force the group to be refreshed even if the field passed
41503      * in is the same as the current grouping field, false to skip grouping on the same field (defaults to false)
41504      */
41505     groupBy : function(field, forceRegroup, direction) {
41506         direction = direction ? (String(direction).toUpperCase() == 'DESC' ? 'DESC' : 'ASC') : this.groupDir;
41507
41508         if (this.groupField == field && this.groupDir == direction && !forceRegroup) {
41509             return; // already grouped by this field
41510         }
41511
41512         //check the contents of the first sorter. If the field matches the CURRENT groupField (before it is set to the new one),
41513         //remove the sorter as it is actually the grouper. The new grouper is added back in by this.sort
41514         var sorters = this.multiSortInfo.sorters;
41515         if (sorters.length > 0 && sorters[0].field == this.groupField) {
41516             sorters.shift();
41517         }
41518
41519         this.groupField = field;
41520         this.groupDir = direction;
41521         this.applyGroupField();
41522
41523         var fireGroupEvent = function() {
41524             this.fireEvent('groupchange', this, this.getGroupState());
41525         };
41526
41527         if (this.groupOnSort) {
41528             this.sort(field, direction);
41529             fireGroupEvent.call(this);
41530             return;
41531         }
41532
41533         if (this.remoteGroup) {
41534             this.on('load', fireGroupEvent, this, {single: true});
41535             this.reload();
41536         } else {
41537             this.sort(sorters);
41538             fireGroupEvent.call(this);
41539         }
41540     },
41541
41542     //GroupingStore always uses multisorting so we intercept calls to sort here to make sure that our grouping sorter object
41543     //is always injected first.
41544     sort : function(fieldName, dir) {
41545         if (this.remoteSort) {
41546             return Ext.data.GroupingStore.superclass.sort.call(this, fieldName, dir);
41547         }
41548
41549         var sorters = [];
41550
41551         //cater for any existing valid arguments to this.sort, massage them into an array of sorter objects
41552         if (Ext.isArray(arguments[0])) {
41553             sorters = arguments[0];
41554         } else if (fieldName == undefined) {
41555             //we preserve the existing sortInfo here because this.sort is called after
41556             //clearGrouping and there may be existing sorting
41557             sorters = this.sortInfo ? [this.sortInfo] : [];
41558         } else {
41559             //TODO: this is lifted straight from Ext.data.Store's singleSort function. It should instead be
41560             //refactored into a common method if possible
41561             var field = this.fields.get(fieldName);
41562             if (!field) return false;
41563
41564             var name       = field.name,
41565                 sortInfo   = this.sortInfo || null,
41566                 sortToggle = this.sortToggle ? this.sortToggle[name] : null;
41567
41568             if (!dir) {
41569                 if (sortInfo && sortInfo.field == name) { // toggle sort dir
41570                     dir = (this.sortToggle[name] || 'ASC').toggle('ASC', 'DESC');
41571                 } else {
41572                     dir = field.sortDir;
41573                 }
41574             }
41575
41576             this.sortToggle[name] = dir;
41577             this.sortInfo = {field: name, direction: dir};
41578
41579             sorters = [this.sortInfo];
41580         }
41581
41582         //add the grouping sorter object as the first multisort sorter
41583         if (this.groupField) {
41584             sorters.unshift({direction: this.groupDir, field: this.groupField});
41585         }
41586
41587         return this.multiSort.call(this, sorters, dir);
41588     },
41589
41590     /**
41591      * @private
41592      * Saves the current grouping field and direction to this.baseParams and this.lastOptions.params
41593      * if we're using remote grouping. Does not actually perform any grouping - just stores values
41594      */
41595     applyGroupField: function(){
41596         if (this.remoteGroup) {
41597             if(!this.baseParams){
41598                 this.baseParams = {};
41599             }
41600
41601             Ext.apply(this.baseParams, {
41602                 groupBy : this.groupField,
41603                 groupDir: this.groupDir
41604             });
41605
41606             var lo = this.lastOptions;
41607             if (lo && lo.params) {
41608                 lo.params.groupDir = this.groupDir;
41609
41610                 //this is deleted because of a bug reported at http://www.extjs.com/forum/showthread.php?t=82907
41611                 delete lo.params.groupBy;
41612             }
41613         }
41614     },
41615
41616     /**
41617      * @private
41618      * TODO: This function is apparently never invoked anywhere in the framework. It has no documentation
41619      * and should be considered for deletion
41620      */
41621     applyGrouping : function(alwaysFireChange){
41622         if(this.groupField !== false){
41623             this.groupBy(this.groupField, true, this.groupDir);
41624             return true;
41625         }else{
41626             if(alwaysFireChange === true){
41627                 this.fireEvent('datachanged', this);
41628             }
41629             return false;
41630         }
41631     },
41632
41633     /**
41634      * @private
41635      * Returns the grouping field that should be used. If groupOnSort is used this will be sortInfo's field,
41636      * otherwise it will be this.groupField
41637      * @return {String} The group field
41638      */
41639     getGroupState : function(){
41640         return this.groupOnSort && this.groupField !== false ?
41641                (this.sortInfo ? this.sortInfo.field : undefined) : this.groupField;
41642     }
41643 });
41644 Ext.reg('groupingstore', Ext.data.GroupingStore);
41645 /**
41646  * @class Ext.data.DirectProxy
41647  * @extends Ext.data.DataProxy
41648  */
41649 Ext.data.DirectProxy = function(config){
41650     Ext.apply(this, config);
41651     if(typeof this.paramOrder == 'string'){
41652         this.paramOrder = this.paramOrder.split(/[\s,|]/);
41653     }
41654     Ext.data.DirectProxy.superclass.constructor.call(this, config);
41655 };
41656
41657 Ext.extend(Ext.data.DirectProxy, Ext.data.DataProxy, {
41658     /**
41659      * @cfg {Array/String} paramOrder Defaults to <tt>undefined</tt>. A list of params to be executed
41660      * server side.  Specify the params in the order in which they must be executed on the server-side
41661      * as either (1) an Array of String values, or (2) a String of params delimited by either whitespace,
41662      * comma, or pipe. For example,
41663      * any of the following would be acceptable:<pre><code>
41664 paramOrder: ['param1','param2','param3']
41665 paramOrder: 'param1 param2 param3'
41666 paramOrder: 'param1,param2,param3'
41667 paramOrder: 'param1|param2|param'
41668      </code></pre>
41669      */
41670     paramOrder: undefined,
41671
41672     /**
41673      * @cfg {Boolean} paramsAsHash
41674      * Send parameters as a collection of named arguments (defaults to <tt>true</tt>). Providing a
41675      * <tt>{@link #paramOrder}</tt> nullifies this configuration.
41676      */
41677     paramsAsHash: true,
41678
41679     /**
41680      * @cfg {Function} directFn
41681      * Function to call when executing a request.  directFn is a simple alternative to defining the api configuration-parameter
41682      * for Store's which will not implement a full CRUD api.
41683      */
41684     directFn : undefined,
41685
41686     /**
41687      * DirectProxy implementation of {@link Ext.data.DataProxy#doRequest}
41688      * @param {String} action The crud action type (create, read, update, destroy)
41689      * @param {Ext.data.Record/Ext.data.Record[]} rs If action is load, rs will be null
41690      * @param {Object} params An object containing properties which are to be used as HTTP parameters
41691      * for the request to the remote server.
41692      * @param {Ext.data.DataReader} reader The Reader object which converts the data
41693      * object into a block of Ext.data.Records.
41694      * @param {Function} callback
41695      * <div class="sub-desc"><p>A function to be called after the request.
41696      * The <tt>callback</tt> is passed the following arguments:<ul>
41697      * <li><tt>r</tt> : Ext.data.Record[] The block of Ext.data.Records.</li>
41698      * <li><tt>options</tt>: Options object from the action request</li>
41699      * <li><tt>success</tt>: Boolean success indicator</li></ul></p></div>
41700      * @param {Object} scope The scope (<code>this</code> reference) in which the callback function is executed. Defaults to the browser window.
41701      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
41702      * @protected
41703      */
41704     doRequest : function(action, rs, params, reader, callback, scope, options) {
41705         var args = [],
41706             directFn = this.api[action] || this.directFn;
41707
41708         switch (action) {
41709             case Ext.data.Api.actions.create:
41710                 args.push(params.jsonData);             // <-- create(Hash)
41711                 break;
41712             case Ext.data.Api.actions.read:
41713                 // If the method has no parameters, ignore the paramOrder/paramsAsHash.
41714                 if(directFn.directCfg.method.len > 0){
41715                     if(this.paramOrder){
41716                         for(var i = 0, len = this.paramOrder.length; i < len; i++){
41717                             args.push(params[this.paramOrder[i]]);
41718                         }
41719                     }else if(this.paramsAsHash){
41720                         args.push(params);
41721                     }
41722                 }
41723                 break;
41724             case Ext.data.Api.actions.update:
41725                 args.push(params.jsonData);        // <-- update(Hash/Hash[])
41726                 break;
41727             case Ext.data.Api.actions.destroy:
41728                 args.push(params.jsonData);        // <-- destroy(Int/Int[])
41729                 break;
41730         }
41731
41732         var trans = {
41733             params : params || {},
41734             request: {
41735                 callback : callback,
41736                 scope : scope,
41737                 arg : options
41738             },
41739             reader: reader
41740         };
41741
41742         args.push(this.createCallback(action, rs, trans), this);
41743         directFn.apply(window, args);
41744     },
41745
41746     // private
41747     createCallback : function(action, rs, trans) {
41748         var me = this;
41749         return function(result, res) {
41750             if (!res.status) {
41751                 // @deprecated fire loadexception
41752                 if (action === Ext.data.Api.actions.read) {
41753                     me.fireEvent("loadexception", me, trans, res, null);
41754                 }
41755                 me.fireEvent('exception', me, 'remote', action, trans, res, null);
41756                 trans.request.callback.call(trans.request.scope, null, trans.request.arg, false);
41757                 return;
41758             }
41759             if (action === Ext.data.Api.actions.read) {
41760                 me.onRead(action, trans, result, res);
41761             } else {
41762                 me.onWrite(action, trans, result, res, rs);
41763             }
41764         };
41765     },
41766
41767     /**
41768      * Callback for read actions
41769      * @param {String} action [Ext.data.Api.actions.create|read|update|destroy]
41770      * @param {Object} trans The request transaction object
41771      * @param {Object} result Data object picked out of the server-response.
41772      * @param {Object} res The server response
41773      * @protected
41774      */
41775     onRead : function(action, trans, result, res) {
41776         var records;
41777         try {
41778             records = trans.reader.readRecords(result);
41779         }
41780         catch (ex) {
41781             // @deprecated: Fire old loadexception for backwards-compat.
41782             this.fireEvent("loadexception", this, trans, res, ex);
41783
41784             this.fireEvent('exception', this, 'response', action, trans, res, ex);
41785             trans.request.callback.call(trans.request.scope, null, trans.request.arg, false);
41786             return;
41787         }
41788         this.fireEvent("load", this, res, trans.request.arg);
41789         trans.request.callback.call(trans.request.scope, records, trans.request.arg, true);
41790     },
41791     /**
41792      * Callback for write actions
41793      * @param {String} action [{@link Ext.data.Api#actions create|read|update|destroy}]
41794      * @param {Object} trans The request transaction object
41795      * @param {Object} result Data object picked out of the server-response.
41796      * @param {Object} res The server response
41797      * @param {Ext.data.Record/[Ext.data.Record]} rs The Store resultset associated with the action.
41798      * @protected
41799      */
41800     onWrite : function(action, trans, result, res, rs) {
41801         var data = trans.reader.extractData(trans.reader.getRoot(result), false);
41802         var success = trans.reader.getSuccess(result);
41803         success = (success !== false);
41804         if (success){
41805             this.fireEvent("write", this, action, data, res, rs, trans.request.arg);
41806         }else{
41807             this.fireEvent('exception', this, 'remote', action, trans, result, rs);
41808         }
41809         trans.request.callback.call(trans.request.scope, data, res, success);
41810     }
41811 });
41812 /**
41813  * @class Ext.data.DirectStore
41814  * @extends Ext.data.Store
41815  * <p>Small helper class to create an {@link Ext.data.Store} configured with an
41816  * {@link Ext.data.DirectProxy} and {@link Ext.data.JsonReader} to make interacting
41817  * with an {@link Ext.Direct} Server-side {@link Ext.direct.Provider Provider} easier.
41818  * To create a different proxy/reader combination create a basic {@link Ext.data.Store}
41819  * configured as needed.</p>
41820  *
41821  * <p><b>*Note:</b> Although they are not listed, this class inherits all of the config options of:</p>
41822  * <div><ul class="mdetail-params">
41823  * <li><b>{@link Ext.data.Store Store}</b></li>
41824  * <div class="sub-desc"><ul class="mdetail-params">
41825  *
41826  * </ul></div>
41827  * <li><b>{@link Ext.data.JsonReader JsonReader}</b></li>
41828  * <div class="sub-desc"><ul class="mdetail-params">
41829  * <li><tt><b>{@link Ext.data.JsonReader#root root}</b></tt></li>
41830  * <li><tt><b>{@link Ext.data.JsonReader#idProperty idProperty}</b></tt></li>
41831  * <li><tt><b>{@link Ext.data.JsonReader#totalProperty totalProperty}</b></tt></li>
41832  * </ul></div>
41833  *
41834  * <li><b>{@link Ext.data.DirectProxy DirectProxy}</b></li>
41835  * <div class="sub-desc"><ul class="mdetail-params">
41836  * <li><tt><b>{@link Ext.data.DirectProxy#directFn directFn}</b></tt></li>
41837  * <li><tt><b>{@link Ext.data.DirectProxy#paramOrder paramOrder}</b></tt></li>
41838  * <li><tt><b>{@link Ext.data.DirectProxy#paramsAsHash paramsAsHash}</b></tt></li>
41839  * </ul></div>
41840  * </ul></div>
41841  *
41842  * @xtype directstore
41843  *
41844  * @constructor
41845  * @param {Object} config
41846  */
41847 Ext.data.DirectStore = Ext.extend(Ext.data.Store, {
41848     constructor : function(config){
41849         // each transaction upon a singe record will generate a distinct Direct transaction since Direct queues them into one Ajax request.
41850         var c = Ext.apply({}, {
41851             batchTransactions: false
41852         }, config);
41853         Ext.data.DirectStore.superclass.constructor.call(this, Ext.apply(c, {
41854             proxy: Ext.isDefined(c.proxy) ? c.proxy : new Ext.data.DirectProxy(Ext.copyTo({}, c, 'paramOrder,paramsAsHash,directFn,api')),
41855             reader: (!Ext.isDefined(c.reader) && c.fields) ? new Ext.data.JsonReader(Ext.copyTo({}, c, 'totalProperty,root,idProperty'), c.fields) : c.reader
41856         }));
41857     }
41858 });
41859 Ext.reg('directstore', Ext.data.DirectStore);
41860 /**
41861  * @class Ext.Direct
41862  * @extends Ext.util.Observable
41863  * <p><b><u>Overview</u></b></p>
41864  *
41865  * <p>Ext.Direct aims to streamline communication between the client and server
41866  * by providing a single interface that reduces the amount of common code
41867  * typically required to validate data and handle returned data packets
41868  * (reading data, error conditions, etc).</p>
41869  *
41870  * <p>The Ext.direct namespace includes several classes for a closer integration
41871  * with the server-side. The Ext.data namespace also includes classes for working
41872  * with Ext.data.Stores which are backed by data from an Ext.Direct method.</p>
41873  *
41874  * <p><b><u>Specification</u></b></p>
41875  *
41876  * <p>For additional information consult the
41877  * <a href="http://extjs.com/products/extjs/direct.php">Ext.Direct Specification</a>.</p>
41878  *
41879  * <p><b><u>Providers</u></b></p>
41880  *
41881  * <p>Ext.Direct uses a provider architecture, where one or more providers are
41882  * used to transport data to and from the server. There are several providers
41883  * that exist in the core at the moment:</p><div class="mdetail-params"><ul>
41884  *
41885  * <li>{@link Ext.direct.JsonProvider JsonProvider} for simple JSON operations</li>
41886  * <li>{@link Ext.direct.PollingProvider PollingProvider} for repeated requests</li>
41887  * <li>{@link Ext.direct.RemotingProvider RemotingProvider} exposes server side
41888  * on the client.</li>
41889  * </ul></div>
41890  *
41891  * <p>A provider does not need to be invoked directly, providers are added via
41892  * {@link Ext.Direct}.{@link Ext.Direct#add add}.</p>
41893  *
41894  * <p><b><u>Router</u></b></p>
41895  *
41896  * <p>Ext.Direct utilizes a "router" on the server to direct requests from the client
41897  * to the appropriate server-side method. Because the Ext.Direct API is completely
41898  * platform-agnostic, you could completely swap out a Java based server solution
41899  * and replace it with one that uses C# without changing the client side JavaScript
41900  * at all.</p>
41901  *
41902  * <p><b><u>Server side events</u></b></p>
41903  *
41904  * <p>Custom events from the server may be handled by the client by adding
41905  * listeners, for example:</p>
41906  * <pre><code>
41907 {"type":"event","name":"message","data":"Successfully polled at: 11:19:30 am"}
41908
41909 // add a handler for a 'message' event sent by the server
41910 Ext.Direct.on('message', function(e){
41911     out.append(String.format('&lt;p>&lt;i>{0}&lt;/i>&lt;/p>', e.data));
41912             out.el.scrollTo('t', 100000, true);
41913 });
41914  * </code></pre>
41915  * @singleton
41916  */
41917 Ext.Direct = Ext.extend(Ext.util.Observable, {
41918     /**
41919      * Each event type implements a getData() method. The default event types are:
41920      * <div class="mdetail-params"><ul>
41921      * <li><b><tt>event</tt></b> : Ext.Direct.Event</li>
41922      * <li><b><tt>exception</tt></b> : Ext.Direct.ExceptionEvent</li>
41923      * <li><b><tt>rpc</tt></b> : Ext.Direct.RemotingEvent</li>
41924      * </ul></div>
41925      * @property eventTypes
41926      * @type Object
41927      */
41928
41929     /**
41930      * Four types of possible exceptions which can occur:
41931      * <div class="mdetail-params"><ul>
41932      * <li><b><tt>Ext.Direct.exceptions.TRANSPORT</tt></b> : 'xhr'</li>
41933      * <li><b><tt>Ext.Direct.exceptions.PARSE</tt></b> : 'parse'</li>
41934      * <li><b><tt>Ext.Direct.exceptions.LOGIN</tt></b> : 'login'</li>
41935      * <li><b><tt>Ext.Direct.exceptions.SERVER</tt></b> : 'exception'</li>
41936      * </ul></div>
41937      * @property exceptions
41938      * @type Object
41939      */
41940     exceptions: {
41941         TRANSPORT: 'xhr',
41942         PARSE: 'parse',
41943         LOGIN: 'login',
41944         SERVER: 'exception'
41945     },
41946
41947     // private
41948     constructor: function(){
41949         this.addEvents(
41950             /**
41951              * @event event
41952              * Fires after an event.
41953              * @param {event} e The {@link Ext.Direct#eventTypes Ext.Direct.Event type} that occurred.
41954              * @param {Ext.direct.Provider} provider The {@link Ext.direct.Provider Provider}.
41955              */
41956             'event',
41957             /**
41958              * @event exception
41959              * Fires after an event exception.
41960              * @param {event} e The {@link Ext.Direct#eventTypes Ext.Direct.Event type} that occurred.
41961              */
41962             'exception'
41963         );
41964         this.transactions = {};
41965         this.providers = {};
41966     },
41967
41968     /**
41969      * Adds an Ext.Direct Provider and creates the proxy or stub methods to execute server-side methods.
41970      * If the provider is not already connected, it will auto-connect.
41971      * <pre><code>
41972 var pollProv = new Ext.direct.PollingProvider({
41973     url: 'php/poll2.php'
41974 });
41975
41976 Ext.Direct.addProvider(
41977     {
41978         "type":"remoting",       // create a {@link Ext.direct.RemotingProvider}
41979         "url":"php\/router.php", // url to connect to the Ext.Direct server-side router.
41980         "actions":{              // each property within the actions object represents a Class
41981             "TestAction":[       // array of methods within each server side Class
41982             {
41983                 "name":"doEcho", // name of method
41984                 "len":1
41985             },{
41986                 "name":"multiply",
41987                 "len":1
41988             },{
41989                 "name":"doForm",
41990                 "formHandler":true, // handle form on server with Ext.Direct.Transaction
41991                 "len":1
41992             }]
41993         },
41994         "namespace":"myApplication",// namespace to create the Remoting Provider in
41995     },{
41996         type: 'polling', // create a {@link Ext.direct.PollingProvider}
41997         url:  'php/poll.php'
41998     },
41999     pollProv // reference to previously created instance
42000 );
42001      * </code></pre>
42002      * @param {Object/Array} provider Accepts either an Array of Provider descriptions (an instance
42003      * or config object for a Provider) or any number of Provider descriptions as arguments.  Each
42004      * Provider description instructs Ext.Direct how to create client-side stub methods.
42005      */
42006     addProvider : function(provider){
42007         var a = arguments;
42008         if(a.length > 1){
42009             for(var i = 0, len = a.length; i < len; i++){
42010                 this.addProvider(a[i]);
42011             }
42012             return;
42013         }
42014
42015         // if provider has not already been instantiated
42016         if(!provider.events){
42017             provider = new Ext.Direct.PROVIDERS[provider.type](provider);
42018         }
42019         provider.id = provider.id || Ext.id();
42020         this.providers[provider.id] = provider;
42021
42022         provider.on('data', this.onProviderData, this);
42023         provider.on('exception', this.onProviderException, this);
42024
42025
42026         if(!provider.isConnected()){
42027             provider.connect();
42028         }
42029
42030         return provider;
42031     },
42032
42033     /**
42034      * Retrieve a {@link Ext.direct.Provider provider} by the
42035      * <b><tt>{@link Ext.direct.Provider#id id}</tt></b> specified when the provider is
42036      * {@link #addProvider added}.
42037      * @param {String} id Unique identifier assigned to the provider when calling {@link #addProvider}
42038      */
42039     getProvider : function(id){
42040         return this.providers[id];
42041     },
42042
42043     removeProvider : function(id){
42044         var provider = id.id ? id : this.providers[id];
42045         provider.un('data', this.onProviderData, this);
42046         provider.un('exception', this.onProviderException, this);
42047         delete this.providers[provider.id];
42048         return provider;
42049     },
42050
42051     addTransaction: function(t){
42052         this.transactions[t.tid] = t;
42053         return t;
42054     },
42055
42056     removeTransaction: function(t){
42057         delete this.transactions[t.tid || t];
42058         return t;
42059     },
42060
42061     getTransaction: function(tid){
42062         return this.transactions[tid.tid || tid];
42063     },
42064
42065     onProviderData : function(provider, e){
42066         if(Ext.isArray(e)){
42067             for(var i = 0, len = e.length; i < len; i++){
42068                 this.onProviderData(provider, e[i]);
42069             }
42070             return;
42071         }
42072         if(e.name && e.name != 'event' && e.name != 'exception'){
42073             this.fireEvent(e.name, e);
42074         }else if(e.type == 'exception'){
42075             this.fireEvent('exception', e);
42076         }
42077         this.fireEvent('event', e, provider);
42078     },
42079
42080     createEvent : function(response, extraProps){
42081         return new Ext.Direct.eventTypes[response.type](Ext.apply(response, extraProps));
42082     }
42083 });
42084 // overwrite impl. with static instance
42085 Ext.Direct = new Ext.Direct();
42086
42087 Ext.Direct.TID = 1;
42088 Ext.Direct.PROVIDERS = {};/**
42089  * @class Ext.Direct.Transaction
42090  * @extends Object
42091  * <p>Supporting Class for Ext.Direct (not intended to be used directly).</p>
42092  * @constructor
42093  * @param {Object} config
42094  */
42095 Ext.Direct.Transaction = function(config){
42096     Ext.apply(this, config);
42097     this.tid = ++Ext.Direct.TID;
42098     this.retryCount = 0;
42099 };
42100 Ext.Direct.Transaction.prototype = {
42101     send: function(){
42102         this.provider.queueTransaction(this);
42103     },
42104
42105     retry: function(){
42106         this.retryCount++;
42107         this.send();
42108     },
42109
42110     getProvider: function(){
42111         return this.provider;
42112     }
42113 };Ext.Direct.Event = function(config){
42114     Ext.apply(this, config);
42115 };
42116
42117 Ext.Direct.Event.prototype = {
42118     status: true,
42119     getData: function(){
42120         return this.data;
42121     }
42122 };
42123
42124 Ext.Direct.RemotingEvent = Ext.extend(Ext.Direct.Event, {
42125     type: 'rpc',
42126     getTransaction: function(){
42127         return this.transaction || Ext.Direct.getTransaction(this.tid);
42128     }
42129 });
42130
42131 Ext.Direct.ExceptionEvent = Ext.extend(Ext.Direct.RemotingEvent, {
42132     status: false,
42133     type: 'exception'
42134 });
42135
42136 Ext.Direct.eventTypes = {
42137     'rpc':  Ext.Direct.RemotingEvent,
42138     'event':  Ext.Direct.Event,
42139     'exception':  Ext.Direct.ExceptionEvent
42140 };
42141 /**
42142  * @class Ext.direct.Provider
42143  * @extends Ext.util.Observable
42144  * <p>Ext.direct.Provider is an abstract class meant to be extended.</p>
42145  * 
42146  * <p>For example ExtJs implements the following subclasses:</p>
42147  * <pre><code>
42148 Provider
42149 |
42150 +---{@link Ext.direct.JsonProvider JsonProvider} 
42151     |
42152     +---{@link Ext.direct.PollingProvider PollingProvider}   
42153     |
42154     +---{@link Ext.direct.RemotingProvider RemotingProvider}   
42155  * </code></pre>
42156  * @abstract
42157  */
42158 Ext.direct.Provider = Ext.extend(Ext.util.Observable, {    
42159     /**
42160      * @cfg {String} id
42161      * The unique id of the provider (defaults to an {@link Ext#id auto-assigned id}).
42162      * You should assign an id if you need to be able to access the provider later and you do
42163      * not have an object reference available, for example:
42164      * <pre><code>
42165 Ext.Direct.addProvider(
42166     {
42167         type: 'polling',
42168         url:  'php/poll.php',
42169         id:   'poll-provider'
42170     }
42171 );
42172      
42173 var p = {@link Ext.Direct Ext.Direct}.{@link Ext.Direct#getProvider getProvider}('poll-provider');
42174 p.disconnect();
42175      * </code></pre>
42176      */
42177         
42178     /**
42179      * @cfg {Number} priority
42180      * Priority of the request. Lower is higher priority, <tt>0</tt> means "duplex" (always on).
42181      * All Providers default to <tt>1</tt> except for PollingProvider which defaults to <tt>3</tt>.
42182      */    
42183     priority: 1,
42184
42185     /**
42186      * @cfg {String} type
42187      * <b>Required</b>, <tt>undefined</tt> by default.  The <tt>type</tt> of provider specified
42188      * to {@link Ext.Direct Ext.Direct}.{@link Ext.Direct#addProvider addProvider} to create a
42189      * new Provider. Acceptable values by default are:<div class="mdetail-params"><ul>
42190      * <li><b><tt>polling</tt></b> : {@link Ext.direct.PollingProvider PollingProvider}</li>
42191      * <li><b><tt>remoting</tt></b> : {@link Ext.direct.RemotingProvider RemotingProvider}</li>
42192      * </ul></div>
42193      */    
42194  
42195     // private
42196     constructor : function(config){
42197         Ext.apply(this, config);
42198         this.addEvents(
42199             /**
42200              * @event connect
42201              * Fires when the Provider connects to the server-side
42202              * @param {Ext.direct.Provider} provider The {@link Ext.direct.Provider Provider}.
42203              */            
42204             'connect',
42205             /**
42206              * @event disconnect
42207              * Fires when the Provider disconnects from the server-side
42208              * @param {Ext.direct.Provider} provider The {@link Ext.direct.Provider Provider}.
42209              */            
42210             'disconnect',
42211             /**
42212              * @event data
42213              * Fires when the Provider receives data from the server-side
42214              * @param {Ext.direct.Provider} provider The {@link Ext.direct.Provider Provider}.
42215              * @param {event} e The {@link Ext.Direct#eventTypes Ext.Direct.Event type} that occurred.
42216              */            
42217             'data',
42218             /**
42219              * @event exception
42220              * Fires when the Provider receives an exception from the server-side
42221              */                        
42222             'exception'
42223         );
42224         Ext.direct.Provider.superclass.constructor.call(this, config);
42225     },
42226
42227     /**
42228      * Returns whether or not the server-side is currently connected.
42229      * Abstract method for subclasses to implement.
42230      */
42231     isConnected: function(){
42232         return false;
42233     },
42234
42235     /**
42236      * Abstract methods for subclasses to implement.
42237      */
42238     connect: Ext.emptyFn,
42239     
42240     /**
42241      * Abstract methods for subclasses to implement.
42242      */
42243     disconnect: Ext.emptyFn
42244 });
42245 /**
42246  * @class Ext.direct.JsonProvider
42247  * @extends Ext.direct.Provider
42248  */
42249 Ext.direct.JsonProvider = Ext.extend(Ext.direct.Provider, {
42250     parseResponse: function(xhr){
42251         if(!Ext.isEmpty(xhr.responseText)){
42252             if(typeof xhr.responseText == 'object'){
42253                 return xhr.responseText;
42254             }
42255             return Ext.decode(xhr.responseText);
42256         }
42257         return null;
42258     },
42259
42260     getEvents: function(xhr){
42261         var data = null;
42262         try{
42263             data = this.parseResponse(xhr);
42264         }catch(e){
42265             var event = new Ext.Direct.ExceptionEvent({
42266                 data: e,
42267                 xhr: xhr,
42268                 code: Ext.Direct.exceptions.PARSE,
42269                 message: 'Error parsing json response: \n\n ' + data
42270             });
42271             return [event];
42272         }
42273         var events = [];
42274         if(Ext.isArray(data)){
42275             for(var i = 0, len = data.length; i < len; i++){
42276                 events.push(Ext.Direct.createEvent(data[i]));
42277             }
42278         }else{
42279             events.push(Ext.Direct.createEvent(data));
42280         }
42281         return events;
42282     }
42283 });/**
42284  * @class Ext.direct.PollingProvider
42285  * @extends Ext.direct.JsonProvider
42286  *
42287  * <p>Provides for repetitive polling of the server at distinct {@link #interval intervals}.
42288  * The initial request for data originates from the client, and then is responded to by the
42289  * server.</p>
42290  * 
42291  * <p>All configurations for the PollingProvider should be generated by the server-side
42292  * API portion of the Ext.Direct stack.</p>
42293  *
42294  * <p>An instance of PollingProvider may be created directly via the new keyword or by simply
42295  * specifying <tt>type = 'polling'</tt>.  For example:</p>
42296  * <pre><code>
42297 var pollA = new Ext.direct.PollingProvider({
42298     type:'polling',
42299     url: 'php/pollA.php',
42300 });
42301 Ext.Direct.addProvider(pollA);
42302 pollA.disconnect();
42303
42304 Ext.Direct.addProvider(
42305     {
42306         type:'polling',
42307         url: 'php/pollB.php',
42308         id: 'pollB-provider'
42309     }
42310 );
42311 var pollB = Ext.Direct.getProvider('pollB-provider');
42312  * </code></pre>
42313  */
42314 Ext.direct.PollingProvider = Ext.extend(Ext.direct.JsonProvider, {
42315     /**
42316      * @cfg {Number} priority
42317      * Priority of the request (defaults to <tt>3</tt>). See {@link Ext.direct.Provider#priority}.
42318      */
42319     // override default priority
42320     priority: 3,
42321     
42322     /**
42323      * @cfg {Number} interval
42324      * How often to poll the server-side in milliseconds (defaults to <tt>3000</tt> - every
42325      * 3 seconds).
42326      */
42327     interval: 3000,
42328
42329     /**
42330      * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
42331      * on every polling request
42332      */
42333     
42334     /**
42335      * @cfg {String/Function} url
42336      * The url which the PollingProvider should contact with each request. This can also be
42337      * an imported Ext.Direct method which will accept the baseParams as its only argument.
42338      */
42339
42340     // private
42341     constructor : function(config){
42342         Ext.direct.PollingProvider.superclass.constructor.call(this, config);
42343         this.addEvents(
42344             /**
42345              * @event beforepoll
42346              * Fired immediately before a poll takes place, an event handler can return false
42347              * in order to cancel the poll.
42348              * @param {Ext.direct.PollingProvider}
42349              */
42350             'beforepoll',            
42351             /**
42352              * @event poll
42353              * This event has not yet been implemented.
42354              * @param {Ext.direct.PollingProvider}
42355              */
42356             'poll'
42357         );
42358     },
42359
42360     // inherited
42361     isConnected: function(){
42362         return !!this.pollTask;
42363     },
42364
42365     /**
42366      * Connect to the server-side and begin the polling process. To handle each
42367      * response subscribe to the data event.
42368      */
42369     connect: function(){
42370         if(this.url && !this.pollTask){
42371             this.pollTask = Ext.TaskMgr.start({
42372                 run: function(){
42373                     if(this.fireEvent('beforepoll', this) !== false){
42374                         if(typeof this.url == 'function'){
42375                             this.url(this.baseParams);
42376                         }else{
42377                             Ext.Ajax.request({
42378                                 url: this.url,
42379                                 callback: this.onData,
42380                                 scope: this,
42381                                 params: this.baseParams
42382                             });
42383                         }
42384                     }
42385                 },
42386                 interval: this.interval,
42387                 scope: this
42388             });
42389             this.fireEvent('connect', this);
42390         }else if(!this.url){
42391             throw 'Error initializing PollingProvider, no url configured.';
42392         }
42393     },
42394
42395     /**
42396      * Disconnect from the server-side and stop the polling process. The disconnect
42397      * event will be fired on a successful disconnect.
42398      */
42399     disconnect: function(){
42400         if(this.pollTask){
42401             Ext.TaskMgr.stop(this.pollTask);
42402             delete this.pollTask;
42403             this.fireEvent('disconnect', this);
42404         }
42405     },
42406
42407     // private
42408     onData: function(opt, success, xhr){
42409         if(success){
42410             var events = this.getEvents(xhr);
42411             for(var i = 0, len = events.length; i < len; i++){
42412                 var e = events[i];
42413                 this.fireEvent('data', this, e);
42414             }
42415         }else{
42416             var e = new Ext.Direct.ExceptionEvent({
42417                 data: e,
42418                 code: Ext.Direct.exceptions.TRANSPORT,
42419                 message: 'Unable to connect to the server.',
42420                 xhr: xhr
42421             });
42422             this.fireEvent('data', this, e);
42423         }
42424     }
42425 });
42426
42427 Ext.Direct.PROVIDERS['polling'] = Ext.direct.PollingProvider;/**
42428  * @class Ext.direct.RemotingProvider
42429  * @extends Ext.direct.JsonProvider
42430  * 
42431  * <p>The {@link Ext.direct.RemotingProvider RemotingProvider} exposes access to
42432  * server side methods on the client (a remote procedure call (RPC) type of
42433  * connection where the client can initiate a procedure on the server).</p>
42434  * 
42435  * <p>This allows for code to be organized in a fashion that is maintainable,
42436  * while providing a clear path between client and server, something that is
42437  * not always apparent when using URLs.</p>
42438  * 
42439  * <p>To accomplish this the server-side needs to describe what classes and methods
42440  * are available on the client-side. This configuration will typically be
42441  * outputted by the server-side Ext.Direct stack when the API description is built.</p>
42442  */
42443 Ext.direct.RemotingProvider = Ext.extend(Ext.direct.JsonProvider, {       
42444     /**
42445      * @cfg {Object} actions
42446      * Object literal defining the server side actions and methods. For example, if
42447      * the Provider is configured with:
42448      * <pre><code>
42449 "actions":{ // each property within the 'actions' object represents a server side Class 
42450     "TestAction":[ // array of methods within each server side Class to be   
42451     {              // stubbed out on client
42452         "name":"doEcho", 
42453         "len":1            
42454     },{
42455         "name":"multiply",// name of method
42456         "len":2           // The number of parameters that will be used to create an
42457                           // array of data to send to the server side function.
42458                           // Ensure the server sends back a Number, not a String. 
42459     },{
42460         "name":"doForm",
42461         "formHandler":true, // direct the client to use specialized form handling method 
42462         "len":1
42463     }]
42464 }
42465      * </code></pre>
42466      * <p>Note that a Store is not required, a server method can be called at any time.
42467      * In the following example a <b>client side</b> handler is used to call the
42468      * server side method "multiply" in the server-side "TestAction" Class:</p>
42469      * <pre><code>
42470 TestAction.multiply(
42471     2, 4, // pass two arguments to server, so specify len=2
42472     // callback function after the server is called
42473     // result: the result returned by the server
42474     //      e: Ext.Direct.RemotingEvent object
42475     function(result, e){
42476         var t = e.getTransaction();
42477         var action = t.action; // server side Class called
42478         var method = t.method; // server side method called
42479         if(e.status){
42480             var answer = Ext.encode(result); // 8
42481     
42482         }else{
42483             var msg = e.message; // failure message
42484         }
42485     }
42486 );
42487      * </code></pre>
42488      * In the example above, the server side "multiply" function will be passed two
42489      * arguments (2 and 4).  The "multiply" method should return the value 8 which will be
42490      * available as the <tt>result</tt> in the example above. 
42491      */
42492     
42493     /**
42494      * @cfg {String/Object} namespace
42495      * Namespace for the Remoting Provider (defaults to the browser global scope of <i>window</i>).
42496      * Explicitly specify the namespace Object, or specify a String to have a
42497      * {@link Ext#namespace namespace created} implicitly.
42498      */
42499     
42500     /**
42501      * @cfg {String} url
42502      * <b>Required<b>. The url to connect to the {@link Ext.Direct} server-side router. 
42503      */
42504     
42505     /**
42506      * @cfg {String} enableUrlEncode
42507      * Specify which param will hold the arguments for the method.
42508      * Defaults to <tt>'data'</tt>.
42509      */
42510     
42511     /**
42512      * @cfg {Number/Boolean} enableBuffer
42513      * <p><tt>true</tt> or <tt>false</tt> to enable or disable combining of method
42514      * calls. If a number is specified this is the amount of time in milliseconds
42515      * to wait before sending a batched request (defaults to <tt>10</tt>).</p>
42516      * <br><p>Calls which are received within the specified timeframe will be
42517      * concatenated together and sent in a single request, optimizing the
42518      * application by reducing the amount of round trips that have to be made
42519      * to the server.</p>
42520      */
42521     enableBuffer: 10,
42522     
42523     /**
42524      * @cfg {Number} maxRetries
42525      * Number of times to re-attempt delivery on failure of a call. Defaults to <tt>1</tt>.
42526      */
42527     maxRetries: 1,
42528     
42529     /**
42530      * @cfg {Number} timeout
42531      * The timeout to use for each request. Defaults to <tt>undefined</tt>.
42532      */
42533     timeout: undefined,
42534
42535     constructor : function(config){
42536         Ext.direct.RemotingProvider.superclass.constructor.call(this, config);
42537         this.addEvents(
42538             /**
42539              * @event beforecall
42540              * Fires immediately before the client-side sends off the RPC call.
42541              * By returning false from an event handler you can prevent the call from
42542              * executing.
42543              * @param {Ext.direct.RemotingProvider} provider
42544              * @param {Ext.Direct.Transaction} transaction
42545              * @param {Object} meta The meta data
42546              */            
42547             'beforecall',            
42548             /**
42549              * @event call
42550              * Fires immediately after the request to the server-side is sent. This does
42551              * NOT fire after the response has come back from the call.
42552              * @param {Ext.direct.RemotingProvider} provider
42553              * @param {Ext.Direct.Transaction} transaction
42554              * @param {Object} meta The meta data
42555              */            
42556             'call'
42557         );
42558         this.namespace = (Ext.isString(this.namespace)) ? Ext.ns(this.namespace) : this.namespace || window;
42559         this.transactions = {};
42560         this.callBuffer = [];
42561     },
42562
42563     // private
42564     initAPI : function(){
42565         var o = this.actions;
42566         for(var c in o){
42567             var cls = this.namespace[c] || (this.namespace[c] = {}),
42568                 ms = o[c];
42569             for(var i = 0, len = ms.length; i < len; i++){
42570                 var m = ms[i];
42571                 cls[m.name] = this.createMethod(c, m);
42572             }
42573         }
42574     },
42575
42576     // inherited
42577     isConnected: function(){
42578         return !!this.connected;
42579     },
42580
42581     connect: function(){
42582         if(this.url){
42583             this.initAPI();
42584             this.connected = true;
42585             this.fireEvent('connect', this);
42586         }else if(!this.url){
42587             throw 'Error initializing RemotingProvider, no url configured.';
42588         }
42589     },
42590
42591     disconnect: function(){
42592         if(this.connected){
42593             this.connected = false;
42594             this.fireEvent('disconnect', this);
42595         }
42596     },
42597
42598     onData: function(opt, success, xhr){
42599         if(success){
42600             var events = this.getEvents(xhr);
42601             for(var i = 0, len = events.length; i < len; i++){
42602                 var e = events[i],
42603                     t = this.getTransaction(e);
42604                 this.fireEvent('data', this, e);
42605                 if(t){
42606                     this.doCallback(t, e, true);
42607                     Ext.Direct.removeTransaction(t);
42608                 }
42609             }
42610         }else{
42611             var ts = [].concat(opt.ts);
42612             for(var i = 0, len = ts.length; i < len; i++){
42613                 var t = this.getTransaction(ts[i]);
42614                 if(t && t.retryCount < this.maxRetries){
42615                     t.retry();
42616                 }else{
42617                     var e = new Ext.Direct.ExceptionEvent({
42618                         data: e,
42619                         transaction: t,
42620                         code: Ext.Direct.exceptions.TRANSPORT,
42621                         message: 'Unable to connect to the server.',
42622                         xhr: xhr
42623                     });
42624                     this.fireEvent('data', this, e);
42625                     if(t){
42626                         this.doCallback(t, e, false);
42627                         Ext.Direct.removeTransaction(t);
42628                     }
42629                 }
42630             }
42631         }
42632     },
42633
42634     getCallData: function(t){
42635         return {
42636             action: t.action,
42637             method: t.method,
42638             data: t.data,
42639             type: 'rpc',
42640             tid: t.tid
42641         };
42642     },
42643
42644     doSend : function(data){
42645         var o = {
42646             url: this.url,
42647             callback: this.onData,
42648             scope: this,
42649             ts: data,
42650             timeout: this.timeout
42651         }, callData;
42652
42653         if(Ext.isArray(data)){
42654             callData = [];
42655             for(var i = 0, len = data.length; i < len; i++){
42656                 callData.push(this.getCallData(data[i]));
42657             }
42658         }else{
42659             callData = this.getCallData(data);
42660         }
42661
42662         if(this.enableUrlEncode){
42663             var params = {};
42664             params[Ext.isString(this.enableUrlEncode) ? this.enableUrlEncode : 'data'] = Ext.encode(callData);
42665             o.params = params;
42666         }else{
42667             o.jsonData = callData;
42668         }
42669         Ext.Ajax.request(o);
42670     },
42671
42672     combineAndSend : function(){
42673         var len = this.callBuffer.length;
42674         if(len > 0){
42675             this.doSend(len == 1 ? this.callBuffer[0] : this.callBuffer);
42676             this.callBuffer = [];
42677         }
42678     },
42679
42680     queueTransaction: function(t){
42681         if(t.form){
42682             this.processForm(t);
42683             return;
42684         }
42685         this.callBuffer.push(t);
42686         if(this.enableBuffer){
42687             if(!this.callTask){
42688                 this.callTask = new Ext.util.DelayedTask(this.combineAndSend, this);
42689             }
42690             this.callTask.delay(Ext.isNumber(this.enableBuffer) ? this.enableBuffer : 10);
42691         }else{
42692             this.combineAndSend();
42693         }
42694     },
42695
42696     doCall : function(c, m, args){
42697         var data = null, hs = args[m.len], scope = args[m.len+1];
42698
42699         if(m.len !== 0){
42700             data = args.slice(0, m.len);
42701         }
42702
42703         var t = new Ext.Direct.Transaction({
42704             provider: this,
42705             args: args,
42706             action: c,
42707             method: m.name,
42708             data: data,
42709             cb: scope && Ext.isFunction(hs) ? hs.createDelegate(scope) : hs
42710         });
42711
42712         if(this.fireEvent('beforecall', this, t, m) !== false){
42713             Ext.Direct.addTransaction(t);
42714             this.queueTransaction(t);
42715             this.fireEvent('call', this, t, m);
42716         }
42717     },
42718
42719     doForm : function(c, m, form, callback, scope){
42720         var t = new Ext.Direct.Transaction({
42721             provider: this,
42722             action: c,
42723             method: m.name,
42724             args:[form, callback, scope],
42725             cb: scope && Ext.isFunction(callback) ? callback.createDelegate(scope) : callback,
42726             isForm: true
42727         });
42728
42729         if(this.fireEvent('beforecall', this, t, m) !== false){
42730             Ext.Direct.addTransaction(t);
42731             var isUpload = String(form.getAttribute("enctype")).toLowerCase() == 'multipart/form-data',
42732                 params = {
42733                     extTID: t.tid,
42734                     extAction: c,
42735                     extMethod: m.name,
42736                     extType: 'rpc',
42737                     extUpload: String(isUpload)
42738                 };
42739             
42740             // change made from typeof callback check to callback.params
42741             // to support addl param passing in DirectSubmit EAC 6/2
42742             Ext.apply(t, {
42743                 form: Ext.getDom(form),
42744                 isUpload: isUpload,
42745                 params: callback && Ext.isObject(callback.params) ? Ext.apply(params, callback.params) : params
42746             });
42747             this.fireEvent('call', this, t, m);
42748             this.processForm(t);
42749         }
42750     },
42751     
42752     processForm: function(t){
42753         Ext.Ajax.request({
42754             url: this.url,
42755             params: t.params,
42756             callback: this.onData,
42757             scope: this,
42758             form: t.form,
42759             isUpload: t.isUpload,
42760             ts: t
42761         });
42762     },
42763
42764     createMethod : function(c, m){
42765         var f;
42766         if(!m.formHandler){
42767             f = function(){
42768                 this.doCall(c, m, Array.prototype.slice.call(arguments, 0));
42769             }.createDelegate(this);
42770         }else{
42771             f = function(form, callback, scope){
42772                 this.doForm(c, m, form, callback, scope);
42773             }.createDelegate(this);
42774         }
42775         f.directCfg = {
42776             action: c,
42777             method: m
42778         };
42779         return f;
42780     },
42781
42782     getTransaction: function(opt){
42783         return opt && opt.tid ? Ext.Direct.getTransaction(opt.tid) : null;
42784     },
42785
42786     doCallback: function(t, e){
42787         var fn = e.status ? 'success' : 'failure';
42788         if(t && t.cb){
42789             var hs = t.cb,
42790                 result = Ext.isDefined(e.result) ? e.result : e.data;
42791             if(Ext.isFunction(hs)){
42792                 hs(result, e);
42793             } else{
42794                 Ext.callback(hs[fn], hs.scope, [result, e]);
42795                 Ext.callback(hs.callback, hs.scope, [result, e]);
42796             }
42797         }
42798     }
42799 });
42800 Ext.Direct.PROVIDERS['remoting'] = Ext.direct.RemotingProvider;/**
42801  * @class Ext.Resizable
42802  * @extends Ext.util.Observable
42803  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
42804  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
42805  * the textarea in a div and set 'resizeChild' to true (or to the id of the element), <b>or</b> set wrap:true in your config and
42806  * the element will be wrapped for you automatically.</p>
42807  * <p>Here is the list of valid resize handles:</p>
42808  * <pre>
42809 Value   Description
42810 ------  -------------------
42811  'n'     north
42812  's'     south
42813  'e'     east
42814  'w'     west
42815  'nw'    northwest
42816  'sw'    southwest
42817  'se'    southeast
42818  'ne'    northeast
42819  'all'   all
42820 </pre>
42821  * <p>Here's an example showing the creation of a typical Resizable:</p>
42822  * <pre><code>
42823 var resizer = new Ext.Resizable('element-id', {
42824     handles: 'all',
42825     minWidth: 200,
42826     minHeight: 100,
42827     maxWidth: 500,
42828     maxHeight: 400,
42829     pinned: true
42830 });
42831 resizer.on('resize', myHandler);
42832 </code></pre>
42833  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
42834  * resizer.east.setDisplayed(false);</p>
42835  * @constructor
42836  * Create a new resizable component
42837  * @param {Mixed} el The id or element to resize
42838  * @param {Object} config configuration options
42839   */
42840 Ext.Resizable = Ext.extend(Ext.util.Observable, {
42841
42842     constructor: function(el, config){
42843         this.el = Ext.get(el);
42844         if(config && config.wrap){
42845             config.resizeChild = this.el;
42846             this.el = this.el.wrap(typeof config.wrap == 'object' ? config.wrap : {cls:'xresizable-wrap'});
42847             this.el.id = this.el.dom.id = config.resizeChild.id + '-rzwrap';
42848             this.el.setStyle('overflow', 'hidden');
42849             this.el.setPositioning(config.resizeChild.getPositioning());
42850             config.resizeChild.clearPositioning();
42851             if(!config.width || !config.height){
42852                 var csize = config.resizeChild.getSize();
42853                 this.el.setSize(csize.width, csize.height);
42854             }
42855             if(config.pinned && !config.adjustments){
42856                 config.adjustments = 'auto';
42857             }
42858         }
42859
42860         /**
42861          * The proxy Element that is resized in place of the real Element during the resize operation.
42862          * This may be queried using {@link Ext.Element#getBox} to provide the new area to resize to.
42863          * Read only.
42864          * @type Ext.Element.
42865          * @property proxy
42866          */
42867         this.proxy = this.el.createProxy({tag: 'div', cls: 'x-resizable-proxy', id: this.el.id + '-rzproxy'}, Ext.getBody());
42868         this.proxy.unselectable();
42869         this.proxy.enableDisplayMode('block');
42870
42871         Ext.apply(this, config);
42872
42873         if(this.pinned){
42874             this.disableTrackOver = true;
42875             this.el.addClass('x-resizable-pinned');
42876         }
42877         // if the element isn't positioned, make it relative
42878         var position = this.el.getStyle('position');
42879         if(position != 'absolute' && position != 'fixed'){
42880             this.el.setStyle('position', 'relative');
42881         }
42882         if(!this.handles){ // no handles passed, must be legacy style
42883             this.handles = 's,e,se';
42884             if(this.multiDirectional){
42885                 this.handles += ',n,w';
42886             }
42887         }
42888         if(this.handles == 'all'){
42889             this.handles = 'n s e w ne nw se sw';
42890         }
42891         var hs = this.handles.split(/\s*?[,;]\s*?| /);
42892         var ps = Ext.Resizable.positions;
42893         for(var i = 0, len = hs.length; i < len; i++){
42894             if(hs[i] && ps[hs[i]]){
42895                 var pos = ps[hs[i]];
42896                 this[pos] = new Ext.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent, this.handleCls);
42897             }
42898         }
42899         // legacy
42900         this.corner = this.southeast;
42901
42902         if(this.handles.indexOf('n') != -1 || this.handles.indexOf('w') != -1){
42903             this.updateBox = true;
42904         }
42905
42906         this.activeHandle = null;
42907
42908         if(this.resizeChild){
42909             if(typeof this.resizeChild == 'boolean'){
42910                 this.resizeChild = Ext.get(this.el.dom.firstChild, true);
42911             }else{
42912                 this.resizeChild = Ext.get(this.resizeChild, true);
42913             }
42914         }
42915
42916         if(this.adjustments == 'auto'){
42917             var rc = this.resizeChild;
42918             var hw = this.west, he = this.east, hn = this.north, hs = this.south;
42919             if(rc && (hw || hn)){
42920                 rc.position('relative');
42921                 rc.setLeft(hw ? hw.el.getWidth() : 0);
42922                 rc.setTop(hn ? hn.el.getHeight() : 0);
42923             }
42924             this.adjustments = [
42925                 (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
42926                 (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
42927             ];
42928         }
42929
42930         if(this.draggable){
42931             this.dd = this.dynamic ?
42932                 this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
42933             this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
42934             if(this.constrainTo){
42935                 this.dd.constrainTo(this.constrainTo);
42936             }
42937         }
42938
42939         this.addEvents(
42940             /**
42941              * @event beforeresize
42942              * Fired before resize is allowed. Set {@link #enabled} to false to cancel resize.
42943              * @param {Ext.Resizable} this
42944              * @param {Ext.EventObject} e The mousedown event
42945              */
42946             'beforeresize',
42947             /**
42948              * @event resize
42949              * Fired after a resize.
42950              * @param {Ext.Resizable} this
42951              * @param {Number} width The new width
42952              * @param {Number} height The new height
42953              * @param {Ext.EventObject} e The mouseup event
42954              */
42955             'resize'
42956         );
42957
42958         if(this.width !== null && this.height !== null){
42959             this.resizeTo(this.width, this.height);
42960         }else{
42961             this.updateChildSize();
42962         }
42963         if(Ext.isIE){
42964             this.el.dom.style.zoom = 1;
42965         }
42966         Ext.Resizable.superclass.constructor.call(this);
42967     },
42968
42969     /**
42970      * @cfg {Array/String} adjustments String 'auto' or an array [width, height] with values to be <b>added</b> to the
42971      * resize operation's new size (defaults to <tt>[0, 0]</tt>)
42972      */
42973     adjustments : [0, 0],
42974     /**
42975      * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
42976      */
42977     animate : false,
42978     /**
42979      * @cfg {Mixed} constrainTo Constrain the resize to a particular element
42980      */
42981     /**
42982      * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
42983      */
42984     disableTrackOver : false,
42985     /**
42986      * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
42987      */
42988     draggable: false,
42989     /**
42990      * @cfg {Number} duration Animation duration if animate = true (defaults to 0.35)
42991      */
42992     duration : 0.35,
42993     /**
42994      * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
42995      */
42996     dynamic : false,
42997     /**
42998      * @cfg {String} easing Animation easing if animate = true (defaults to <tt>'easingOutStrong'</tt>)
42999      */
43000     easing : 'easeOutStrong',
43001     /**
43002      * @cfg {Boolean} enabled False to disable resizing (defaults to true)
43003      */
43004     enabled : true,
43005     /**
43006      * @property enabled Writable. False if resizing is disabled.
43007      * @type Boolean
43008      */
43009     /**
43010      * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined).
43011      * Specify either <tt>'all'</tt> or any of <tt>'n s e w ne nw se sw'</tt>.
43012      */
43013     handles : false,
43014     /**
43015      * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  Deprecated style of adding multi-direction resize handles.
43016      */
43017     multiDirectional : false,
43018     /**
43019      * @cfg {Number} height The height of the element in pixels (defaults to null)
43020      */
43021     height : null,
43022     /**
43023      * @cfg {Number} width The width of the element in pixels (defaults to null)
43024      */
43025     width : null,
43026     /**
43027      * @cfg {Number} heightIncrement The increment to snap the height resize in pixels
43028      * (only applies if <code>{@link #dynamic}==true</code>). Defaults to <tt>0</tt>.
43029      */
43030     heightIncrement : 0,
43031     /**
43032      * @cfg {Number} widthIncrement The increment to snap the width resize in pixels
43033      * (only applies if <code>{@link #dynamic}==true</code>). Defaults to <tt>0</tt>.
43034      */
43035     widthIncrement : 0,
43036     /**
43037      * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
43038      */
43039     minHeight : 5,
43040     /**
43041      * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
43042      */
43043     minWidth : 5,
43044     /**
43045      * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
43046      */
43047     maxHeight : 10000,
43048     /**
43049      * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
43050      */
43051     maxWidth : 10000,
43052     /**
43053      * @cfg {Number} minX The minimum x for the element (defaults to 0)
43054      */
43055     minX: 0,
43056     /**
43057      * @cfg {Number} minY The minimum x for the element (defaults to 0)
43058      */
43059     minY: 0,
43060     /**
43061      * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
43062      * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
43063      */
43064     pinned : false,
43065     /**
43066      * @cfg {Boolean} preserveRatio True to preserve the original ratio between height
43067      * and width during resize (defaults to false)
43068      */
43069     preserveRatio : false,
43070     /**
43071      * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
43072      */
43073     resizeChild : false,
43074     /**
43075      * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
43076      */
43077     transparent: false,
43078     /**
43079      * @cfg {Ext.lib.Region} resizeRegion Constrain the resize to a particular region
43080      */
43081     /**
43082      * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
43083      * in favor of the handles config option (defaults to false)
43084      */
43085     /**
43086      * @cfg {String} handleCls A css class to add to each handle. Defaults to <tt>''</tt>.
43087      */
43088
43089
43090     /**
43091      * Perform a manual resize and fires the 'resize' event.
43092      * @param {Number} width
43093      * @param {Number} height
43094      */
43095     resizeTo : function(width, height){
43096         this.el.setSize(width, height);
43097         this.updateChildSize();
43098         this.fireEvent('resize', this, width, height, null);
43099     },
43100
43101     // private
43102     startSizing : function(e, handle){
43103         this.fireEvent('beforeresize', this, e);
43104         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
43105
43106             if(!this.overlay){
43107                 this.overlay = this.el.createProxy({tag: 'div', cls: 'x-resizable-overlay', html: '&#160;'}, Ext.getBody());
43108                 this.overlay.unselectable();
43109                 this.overlay.enableDisplayMode('block');
43110                 this.overlay.on({
43111                     scope: this,
43112                     mousemove: this.onMouseMove,
43113                     mouseup: this.onMouseUp
43114                 });
43115             }
43116             this.overlay.setStyle('cursor', handle.el.getStyle('cursor'));
43117
43118             this.resizing = true;
43119             this.startBox = this.el.getBox();
43120             this.startPoint = e.getXY();
43121             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
43122                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
43123
43124             this.overlay.setSize(Ext.lib.Dom.getViewWidth(true), Ext.lib.Dom.getViewHeight(true));
43125             this.overlay.show();
43126
43127             if(this.constrainTo) {
43128                 var ct = Ext.get(this.constrainTo);
43129                 this.resizeRegion = ct.getRegion().adjust(
43130                     ct.getFrameWidth('t'),
43131                     ct.getFrameWidth('l'),
43132                     -ct.getFrameWidth('b'),
43133                     -ct.getFrameWidth('r')
43134                 );
43135             }
43136
43137             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
43138             this.proxy.show();
43139             this.proxy.setBox(this.startBox);
43140             if(!this.dynamic){
43141                 this.proxy.setStyle('visibility', 'visible');
43142             }
43143         }
43144     },
43145
43146     // private
43147     onMouseDown : function(handle, e){
43148         if(this.enabled){
43149             e.stopEvent();
43150             this.activeHandle = handle;
43151             this.startSizing(e, handle);
43152         }
43153     },
43154
43155     // private
43156     onMouseUp : function(e){
43157         this.activeHandle = null;
43158         var size = this.resizeElement();
43159         this.resizing = false;
43160         this.handleOut();
43161         this.overlay.hide();
43162         this.proxy.hide();
43163         this.fireEvent('resize', this, size.width, size.height, e);
43164     },
43165
43166     // private
43167     updateChildSize : function(){
43168         if(this.resizeChild){
43169             var el = this.el;
43170             var child = this.resizeChild;
43171             var adj = this.adjustments;
43172             if(el.dom.offsetWidth){
43173                 var b = el.getSize(true);
43174                 child.setSize(b.width+adj[0], b.height+adj[1]);
43175             }
43176             // Second call here for IE
43177             // The first call enables instant resizing and
43178             // the second call corrects scroll bars if they
43179             // exist
43180             if(Ext.isIE){
43181                 setTimeout(function(){
43182                     if(el.dom.offsetWidth){
43183                         var b = el.getSize(true);
43184                         child.setSize(b.width+adj[0], b.height+adj[1]);
43185                     }
43186                 }, 10);
43187             }
43188         }
43189     },
43190
43191     // private
43192     snap : function(value, inc, min){
43193         if(!inc || !value){
43194             return value;
43195         }
43196         var newValue = value;
43197         var m = value % inc;
43198         if(m > 0){
43199             if(m > (inc/2)){
43200                 newValue = value + (inc-m);
43201             }else{
43202                 newValue = value - m;
43203             }
43204         }
43205         return Math.max(min, newValue);
43206     },
43207
43208     /**
43209      * <p>Performs resizing of the associated Element. This method is called internally by this
43210      * class, and should not be called by user code.</p>
43211      * <p>If a Resizable is being used to resize an Element which encapsulates a more complex UI
43212      * component such as a Panel, this method may be overridden by specifying an implementation
43213      * as a config option to provide appropriate behaviour at the end of the resize operation on
43214      * mouseup, for example resizing the Panel, and relaying the Panel's content.</p>
43215      * <p>The new area to be resized to is available by examining the state of the {@link #proxy}
43216      * Element. Example:
43217 <pre><code>
43218 new Ext.Panel({
43219     title: 'Resize me',
43220     x: 100,
43221     y: 100,
43222     renderTo: Ext.getBody(),
43223     floating: true,
43224     frame: true,
43225     width: 400,
43226     height: 200,
43227     listeners: {
43228         render: function(p) {
43229             new Ext.Resizable(p.getEl(), {
43230                 handles: 'all',
43231                 pinned: true,
43232                 transparent: true,
43233                 resizeElement: function() {
43234                     var box = this.proxy.getBox();
43235                     p.updateBox(box);
43236                     if (p.layout) {
43237                         p.doLayout();
43238                     }
43239                     return box;
43240                 }
43241            });
43242        }
43243     }
43244 }).show();
43245 </code></pre>
43246      */
43247     resizeElement : function(){
43248         var box = this.proxy.getBox();
43249         if(this.updateBox){
43250             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
43251         }else{
43252             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
43253         }
43254         this.updateChildSize();
43255         if(!this.dynamic){
43256             this.proxy.hide();
43257         }
43258         if(this.draggable && this.constrainTo){
43259             this.dd.resetConstraints();
43260             this.dd.constrainTo(this.constrainTo);
43261         }
43262         return box;
43263     },
43264
43265     // private
43266     constrain : function(v, diff, m, mx){
43267         if(v - diff < m){
43268             diff = v - m;
43269         }else if(v - diff > mx){
43270             diff = v - mx;
43271         }
43272         return diff;
43273     },
43274
43275     // private
43276     onMouseMove : function(e){
43277         if(this.enabled && this.activeHandle){
43278             try{// try catch so if something goes wrong the user doesn't get hung
43279
43280             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
43281                 return;
43282             }
43283
43284             //var curXY = this.startPoint;
43285             var curSize = this.curSize || this.startBox,
43286                 x = this.startBox.x, y = this.startBox.y,
43287                 ox = x,
43288                 oy = y,
43289                 w = curSize.width,
43290                 h = curSize.height,
43291                 ow = w,
43292                 oh = h,
43293                 mw = this.minWidth,
43294                 mh = this.minHeight,
43295                 mxw = this.maxWidth,
43296                 mxh = this.maxHeight,
43297                 wi = this.widthIncrement,
43298                 hi = this.heightIncrement,
43299                 eventXY = e.getXY(),
43300                 diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0])),
43301                 diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1])),
43302                 pos = this.activeHandle.position,
43303                 tw,
43304                 th;
43305
43306             switch(pos){
43307                 case 'east':
43308                     w += diffX;
43309                     w = Math.min(Math.max(mw, w), mxw);
43310                     break;
43311                 case 'south':
43312                     h += diffY;
43313                     h = Math.min(Math.max(mh, h), mxh);
43314                     break;
43315                 case 'southeast':
43316                     w += diffX;
43317                     h += diffY;
43318                     w = Math.min(Math.max(mw, w), mxw);
43319                     h = Math.min(Math.max(mh, h), mxh);
43320                     break;
43321                 case 'north':
43322                     diffY = this.constrain(h, diffY, mh, mxh);
43323                     y += diffY;
43324                     h -= diffY;
43325                     break;
43326                 case 'west':
43327                     diffX = this.constrain(w, diffX, mw, mxw);
43328                     x += diffX;
43329                     w -= diffX;
43330                     break;
43331                 case 'northeast':
43332                     w += diffX;
43333                     w = Math.min(Math.max(mw, w), mxw);
43334                     diffY = this.constrain(h, diffY, mh, mxh);
43335                     y += diffY;
43336                     h -= diffY;
43337                     break;
43338                 case 'northwest':
43339                     diffX = this.constrain(w, diffX, mw, mxw);
43340                     diffY = this.constrain(h, diffY, mh, mxh);
43341                     y += diffY;
43342                     h -= diffY;
43343                     x += diffX;
43344                     w -= diffX;
43345                     break;
43346                case 'southwest':
43347                     diffX = this.constrain(w, diffX, mw, mxw);
43348                     h += diffY;
43349                     h = Math.min(Math.max(mh, h), mxh);
43350                     x += diffX;
43351                     w -= diffX;
43352                     break;
43353             }
43354
43355             var sw = this.snap(w, wi, mw);
43356             var sh = this.snap(h, hi, mh);
43357             if(sw != w || sh != h){
43358                 switch(pos){
43359                     case 'northeast':
43360                         y -= sh - h;
43361                     break;
43362                     case 'north':
43363                         y -= sh - h;
43364                         break;
43365                     case 'southwest':
43366                         x -= sw - w;
43367                     break;
43368                     case 'west':
43369                         x -= sw - w;
43370                         break;
43371                     case 'northwest':
43372                         x -= sw - w;
43373                         y -= sh - h;
43374                     break;
43375                 }
43376                 w = sw;
43377                 h = sh;
43378             }
43379
43380             if(this.preserveRatio){
43381                 switch(pos){
43382                     case 'southeast':
43383                     case 'east':
43384                         h = oh * (w/ow);
43385                         h = Math.min(Math.max(mh, h), mxh);
43386                         w = ow * (h/oh);
43387                        break;
43388                     case 'south':
43389                         w = ow * (h/oh);
43390                         w = Math.min(Math.max(mw, w), mxw);
43391                         h = oh * (w/ow);
43392                         break;
43393                     case 'northeast':
43394                         w = ow * (h/oh);
43395                         w = Math.min(Math.max(mw, w), mxw);
43396                         h = oh * (w/ow);
43397                     break;
43398                     case 'north':
43399                         tw = w;
43400                         w = ow * (h/oh);
43401                         w = Math.min(Math.max(mw, w), mxw);
43402                         h = oh * (w/ow);
43403                         x += (tw - w) / 2;
43404                         break;
43405                     case 'southwest':
43406                         h = oh * (w/ow);
43407                         h = Math.min(Math.max(mh, h), mxh);
43408                         tw = w;
43409                         w = ow * (h/oh);
43410                         x += tw - w;
43411                         break;
43412                     case 'west':
43413                         th = h;
43414                         h = oh * (w/ow);
43415                         h = Math.min(Math.max(mh, h), mxh);
43416                         y += (th - h) / 2;
43417                         tw = w;
43418                         w = ow * (h/oh);
43419                         x += tw - w;
43420                        break;
43421                     case 'northwest':
43422                         tw = w;
43423                         th = h;
43424                         h = oh * (w/ow);
43425                         h = Math.min(Math.max(mh, h), mxh);
43426                         w = ow * (h/oh);
43427                         y += th - h;
43428                         x += tw - w;
43429                         break;
43430
43431                 }
43432             }
43433             this.proxy.setBounds(x, y, w, h);
43434             if(this.dynamic){
43435                 this.resizeElement();
43436             }
43437             }catch(ex){}
43438         }
43439     },
43440
43441     // private
43442     handleOver : function(){
43443         if(this.enabled){
43444             this.el.addClass('x-resizable-over');
43445         }
43446     },
43447
43448     // private
43449     handleOut : function(){
43450         if(!this.resizing){
43451             this.el.removeClass('x-resizable-over');
43452         }
43453     },
43454
43455     /**
43456      * Returns the element this component is bound to.
43457      * @return {Ext.Element}
43458      */
43459     getEl : function(){
43460         return this.el;
43461     },
43462
43463     /**
43464      * Returns the resizeChild element (or null).
43465      * @return {Ext.Element}
43466      */
43467     getResizeChild : function(){
43468         return this.resizeChild;
43469     },
43470
43471     /**
43472      * Destroys this resizable. If the element was wrapped and
43473      * removeEl is not true then the element remains.
43474      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
43475      */
43476     destroy : function(removeEl){
43477         Ext.destroy(this.dd, this.overlay, this.proxy);
43478         this.overlay = null;
43479         this.proxy = null;
43480
43481         var ps = Ext.Resizable.positions;
43482         for(var k in ps){
43483             if(typeof ps[k] != 'function' && this[ps[k]]){
43484                 this[ps[k]].destroy();
43485             }
43486         }
43487         if(removeEl){
43488             this.el.update('');
43489             Ext.destroy(this.el);
43490             this.el = null;
43491         }
43492         this.purgeListeners();
43493     },
43494
43495     syncHandleHeight : function(){
43496         var h = this.el.getHeight(true);
43497         if(this.west){
43498             this.west.el.setHeight(h);
43499         }
43500         if(this.east){
43501             this.east.el.setHeight(h);
43502         }
43503     }
43504 });
43505
43506 // private
43507 // hash to map config positions to true positions
43508 Ext.Resizable.positions = {
43509     n: 'north', s: 'south', e: 'east', w: 'west', se: 'southeast', sw: 'southwest', nw: 'northwest', ne: 'northeast'
43510 };
43511
43512 Ext.Resizable.Handle = Ext.extend(Object, {
43513     constructor : function(rz, pos, disableTrackOver, transparent, cls){
43514        if(!this.tpl){
43515             // only initialize the template if resizable is used
43516             var tpl = Ext.DomHelper.createTemplate(
43517                 {tag: 'div', cls: 'x-resizable-handle x-resizable-handle-{0}'}
43518             );
43519             tpl.compile();
43520             Ext.Resizable.Handle.prototype.tpl = tpl;
43521         }
43522         this.position = pos;
43523         this.rz = rz;
43524         this.el = this.tpl.append(rz.el.dom, [this.position], true);
43525         this.el.unselectable();
43526         if(transparent){
43527             this.el.setOpacity(0);
43528         }
43529         if(!Ext.isEmpty(cls)){
43530             this.el.addClass(cls);
43531         }
43532         this.el.on('mousedown', this.onMouseDown, this);
43533         if(!disableTrackOver){
43534             this.el.on({
43535                 scope: this,
43536                 mouseover: this.onMouseOver,
43537                 mouseout: this.onMouseOut
43538             });
43539         }
43540     },
43541
43542     // private
43543     afterResize : function(rz){
43544         // do nothing
43545     },
43546     // private
43547     onMouseDown : function(e){
43548         this.rz.onMouseDown(this, e);
43549     },
43550     // private
43551     onMouseOver : function(e){
43552         this.rz.handleOver(this, e);
43553     },
43554     // private
43555     onMouseOut : function(e){
43556         this.rz.handleOut(this, e);
43557     },
43558     // private
43559     destroy : function(){
43560         Ext.destroy(this.el);
43561         this.el = null;
43562     }
43563 });
43564 /**
43565  * @class Ext.Window
43566  * @extends Ext.Panel
43567  * <p>A specialized panel intended for use as an application window.  Windows are floated, {@link #resizable}, and
43568  * {@link #draggable} by default.  Windows can be {@link #maximizable maximized} to fill the viewport,
43569  * restored to their prior size, and can be {@link #minimize}d.</p>
43570  * <p>Windows can also be linked to a {@link Ext.WindowGroup} or managed by the {@link Ext.WindowMgr} to provide
43571  * grouping, activation, to front, to back and other application-specific behavior.</p>
43572  * <p>By default, Windows will be rendered to document.body. To {@link #constrain} a Window to another element
43573  * specify {@link Ext.Component#renderTo renderTo}.</p>
43574  * <p><b>Note:</b> By default, the <code>{@link #closable close}</code> header tool <i>destroys</i> the Window resulting in
43575  * destruction of any child Components. This makes the Window object, and all its descendants <b>unusable</b>. To enable
43576  * re-use of a Window, use <b><code>{@link #closeAction closeAction: 'hide'}</code></b>.</p>
43577  * @constructor
43578  * @param {Object} config The config object
43579  * @xtype window
43580  */
43581 Ext.Window = Ext.extend(Ext.Panel, {
43582     /**
43583      * @cfg {Number} x
43584      * The X position of the left edge of the window on initial showing. Defaults to centering the Window within
43585      * the width of the Window's container {@link Ext.Element Element) (The Element that the Window is rendered to).
43586      */
43587     /**
43588      * @cfg {Number} y
43589      * The Y position of the top edge of the window on initial showing. Defaults to centering the Window within
43590      * the height of the Window's container {@link Ext.Element Element) (The Element that the Window is rendered to).
43591      */
43592     /**
43593      * @cfg {Boolean} modal
43594      * True to make the window modal and mask everything behind it when displayed, false to display it without
43595      * restricting access to other UI elements (defaults to false).
43596      */
43597     /**
43598      * @cfg {String/Element} animateTarget
43599      * Id or element from which the window should animate while opening (defaults to null with no animation).
43600      */
43601     /**
43602      * @cfg {String} resizeHandles
43603      * A valid {@link Ext.Resizable} handles config string (defaults to 'all').  Only applies when resizable = true.
43604      */
43605     /**
43606      * @cfg {Ext.WindowGroup} manager
43607      * A reference to the WindowGroup that should manage this window (defaults to {@link Ext.WindowMgr}).
43608      */
43609     /**
43610     * @cfg {String/Number/Component} defaultButton
43611     * <p>Specifies a Component to receive focus when this Window is focussed.</p>
43612     * <p>This may be one of:</p><div class="mdetail-params"><ul>
43613     * <li>The index of a footer Button.</li>
43614     * <li>The id of a Component.</li>
43615     * <li>A Component.</li>
43616     * </ul></div>
43617     */
43618     /**
43619     * @cfg {Function} onEsc
43620     * Allows override of the built-in processing for the escape key. Default action
43621     * is to close the Window (performing whatever action is specified in {@link #closeAction}.
43622     * To prevent the Window closing when the escape key is pressed, specify this as
43623     * Ext.emptyFn (See {@link Ext#emptyFn}).
43624     */
43625     /**
43626      * @cfg {Boolean} collapsed
43627      * True to render the window collapsed, false to render it expanded (defaults to false). Note that if
43628      * {@link #expandOnShow} is true (the default) it will override the <tt>collapsed</tt> config and the window
43629      * will always be expanded when shown.
43630      */
43631     /**
43632      * @cfg {Boolean} maximized
43633      * True to initially display the window in a maximized state. (Defaults to false).
43634      */
43635
43636     /**
43637     * @cfg {String} baseCls
43638     * The base CSS class to apply to this panel's element (defaults to 'x-window').
43639     */
43640     baseCls : 'x-window',
43641     /**
43642      * @cfg {Boolean} resizable
43643      * True to allow user resizing at each edge and corner of the window, false to disable resizing (defaults to true).
43644      */
43645     resizable : true,
43646     /**
43647      * @cfg {Boolean} draggable
43648      * True to allow the window to be dragged by the header bar, false to disable dragging (defaults to true).  Note
43649      * that by default the window will be centered in the viewport, so if dragging is disabled the window may need
43650      * to be positioned programmatically after render (e.g., myWindow.setPosition(100, 100);).
43651      */
43652     draggable : true,
43653     /**
43654      * @cfg {Boolean} closable
43655      * <p>True to display the 'close' tool button and allow the user to close the window, false to
43656      * hide the button and disallow closing the window (defaults to true).</p>
43657      * <p>By default, when close is requested by either clicking the close button in the header
43658      * or pressing ESC when the Window has focus, the {@link #close} method will be called. This
43659      * will <i>{@link Ext.Component#destroy destroy}</i> the Window and its content meaning that
43660      * it may not be reused.</p>
43661      * <p>To make closing a Window <i>hide</i> the Window so that it may be reused, set
43662      * {@link #closeAction} to 'hide'.
43663      */
43664     closable : true,
43665     /**
43666      * @cfg {String} closeAction
43667      * <p>The action to take when the close header tool is clicked:
43668      * <div class="mdetail-params"><ul>
43669      * <li><b><code>'{@link #close}'</code></b> : <b>Default</b><div class="sub-desc">
43670      * {@link #close remove} the window from the DOM and {@link Ext.Component#destroy destroy}
43671      * it and all descendant Components. The window will <b>not</b> be available to be
43672      * redisplayed via the {@link #show} method.
43673      * </div></li>
43674      * <li><b><code>'{@link #hide}'</code></b> : <div class="sub-desc">
43675      * {@link #hide} the window by setting visibility to hidden and applying negative offsets.
43676      * The window will be available to be redisplayed via the {@link #show} method.
43677      * </div></li>
43678      * </ul></div>
43679      * <p><b>Note:</b> This setting does not affect the {@link #close} method
43680      * which will always {@link Ext.Component#destroy destroy} the window. To
43681      * programatically <i>hide</i> a window, call {@link #hide}.</p>
43682      */
43683     closeAction : 'close',
43684     /**
43685      * @cfg {Boolean} constrain
43686      * True to constrain the window within its containing element, false to allow it to fall outside of its
43687      * containing element. By default the window will be rendered to document.body.  To render and constrain the
43688      * window within another element specify {@link #renderTo}.
43689      * (defaults to false).  Optionally the header only can be constrained using {@link #constrainHeader}.
43690      */
43691     constrain : false,
43692     /**
43693      * @cfg {Boolean} constrainHeader
43694      * True to constrain the window header within its containing element (allowing the window body to fall outside
43695      * of its containing element) or false to allow the header to fall outside its containing element (defaults to
43696      * false). Optionally the entire window can be constrained using {@link #constrain}.
43697      */
43698     constrainHeader : false,
43699     /**
43700      * @cfg {Boolean} plain
43701      * True to render the window body with a transparent background so that it will blend into the framing
43702      * elements, false to add a lighter background color to visually highlight the body element and separate it
43703      * more distinctly from the surrounding frame (defaults to false).
43704      */
43705     plain : false,
43706     /**
43707      * @cfg {Boolean} minimizable
43708      * True to display the 'minimize' tool button and allow the user to minimize the window, false to hide the button
43709      * and disallow minimizing the window (defaults to false).  Note that this button provides no implementation --
43710      * the behavior of minimizing a window is implementation-specific, so the minimize event must be handled and a
43711      * custom minimize behavior implemented for this option to be useful.
43712      */
43713     minimizable : false,
43714     /**
43715      * @cfg {Boolean} maximizable
43716      * True to display the 'maximize' tool button and allow the user to maximize the window, false to hide the button
43717      * and disallow maximizing the window (defaults to false).  Note that when a window is maximized, the tool button
43718      * will automatically change to a 'restore' button with the appropriate behavior already built-in that will
43719      * restore the window to its previous size.
43720      */
43721     maximizable : false,
43722     /**
43723      * @cfg {Number} minHeight
43724      * The minimum height in pixels allowed for this window (defaults to 100).  Only applies when resizable = true.
43725      */
43726     minHeight : 100,
43727     /**
43728      * @cfg {Number} minWidth
43729      * The minimum width in pixels allowed for this window (defaults to 200).  Only applies when resizable = true.
43730      */
43731     minWidth : 200,
43732     /**
43733      * @cfg {Boolean} expandOnShow
43734      * True to always expand the window when it is displayed, false to keep it in its current state (which may be
43735      * {@link #collapsed}) when displayed (defaults to true).
43736      */
43737     expandOnShow : true,
43738     
43739     /**
43740      * @cfg {Number} showAnimDuration The number of seconds that the window show animation takes if enabled.
43741      * Defaults to 0.25
43742      */
43743     showAnimDuration: 0.25,
43744     
43745     /**
43746      * @cfg {Number} hideAnimDuration The number of seconds that the window hide animation takes if enabled.
43747      * Defaults to 0.25
43748      */
43749     hideAnimDuration: 0.25,
43750
43751     // inherited docs, same default
43752     collapsible : false,
43753
43754     /**
43755      * @cfg {Boolean} initHidden
43756      * True to hide the window until show() is explicitly called (defaults to true).
43757      * @deprecated
43758      */
43759     initHidden : undefined,
43760
43761     /**
43762      * @cfg {Boolean} hidden
43763      * Render this component hidden (default is <tt>true</tt>). If <tt>true</tt>, the
43764      * {@link #hide} method will be called internally.
43765      */
43766     hidden : true,
43767
43768     // The following configs are set to provide the basic functionality of a window.
43769     // Changing them would require additional code to handle correctly and should
43770     // usually only be done in subclasses that can provide custom behavior.  Changing them
43771     // may have unexpected or undesirable results.
43772     /** @cfg {String} elements @hide */
43773     elements : 'header,body',
43774     /** @cfg {Boolean} frame @hide */
43775     frame : true,
43776     /** @cfg {Boolean} floating @hide */
43777     floating : true,
43778
43779     // private
43780     initComponent : function(){
43781         this.initTools();
43782         Ext.Window.superclass.initComponent.call(this);
43783         this.addEvents(
43784             /**
43785              * @event activate
43786              * Fires after the window has been visually activated via {@link #setActive}.
43787              * @param {Ext.Window} this
43788              */
43789             /**
43790              * @event deactivate
43791              * Fires after the window has been visually deactivated via {@link #setActive}.
43792              * @param {Ext.Window} this
43793              */
43794             /**
43795              * @event resize
43796              * Fires after the window has been resized.
43797              * @param {Ext.Window} this
43798              * @param {Number} width The window's new width
43799              * @param {Number} height The window's new height
43800              */
43801             'resize',
43802             /**
43803              * @event maximize
43804              * Fires after the window has been maximized.
43805              * @param {Ext.Window} this
43806              */
43807             'maximize',
43808             /**
43809              * @event minimize
43810              * Fires after the window has been minimized.
43811              * @param {Ext.Window} this
43812              */
43813             'minimize',
43814             /**
43815              * @event restore
43816              * Fires after the window has been restored to its original size after being maximized.
43817              * @param {Ext.Window} this
43818              */
43819             'restore'
43820         );
43821         // for backwards compat, this should be removed at some point
43822         if(Ext.isDefined(this.initHidden)){
43823             this.hidden = this.initHidden;
43824         }
43825         if(this.hidden === false){
43826             this.hidden = true;
43827             this.show();
43828         }
43829     },
43830
43831     // private
43832     getState : function(){
43833         return Ext.apply(Ext.Window.superclass.getState.call(this) || {}, this.getBox(true));
43834     },
43835
43836     // private
43837     onRender : function(ct, position){
43838         Ext.Window.superclass.onRender.call(this, ct, position);
43839
43840         if(this.plain){
43841             this.el.addClass('x-window-plain');
43842         }
43843
43844         // this element allows the Window to be focused for keyboard events
43845         this.focusEl = this.el.createChild({
43846                     tag: 'a', href:'#', cls:'x-dlg-focus',
43847                     tabIndex:'-1', html: '&#160;'});
43848         this.focusEl.swallowEvent('click', true);
43849
43850         this.proxy = this.el.createProxy('x-window-proxy');
43851         this.proxy.enableDisplayMode('block');
43852
43853         if(this.modal){
43854             this.mask = this.container.createChild({cls:'ext-el-mask'}, this.el.dom);
43855             this.mask.enableDisplayMode('block');
43856             this.mask.hide();
43857             this.mon(this.mask, 'click', this.focus, this);
43858         }
43859         if(this.maximizable){
43860             this.mon(this.header, 'dblclick', this.toggleMaximize, this);
43861         }
43862     },
43863
43864     // private
43865     initEvents : function(){
43866         Ext.Window.superclass.initEvents.call(this);
43867         if(this.animateTarget){
43868             this.setAnimateTarget(this.animateTarget);
43869         }
43870
43871         if(this.resizable){
43872             this.resizer = new Ext.Resizable(this.el, {
43873                 minWidth: this.minWidth,
43874                 minHeight:this.minHeight,
43875                 handles: this.resizeHandles || 'all',
43876                 pinned: true,
43877                 resizeElement : this.resizerAction,
43878                 handleCls: 'x-window-handle'
43879             });
43880             this.resizer.window = this;
43881             this.mon(this.resizer, 'beforeresize', this.beforeResize, this);
43882         }
43883
43884         if(this.draggable){
43885             this.header.addClass('x-window-draggable');
43886         }
43887         this.mon(this.el, 'mousedown', this.toFront, this);
43888         this.manager = this.manager || Ext.WindowMgr;
43889         this.manager.register(this);
43890         if(this.maximized){
43891             this.maximized = false;
43892             this.maximize();
43893         }
43894         if(this.closable){
43895             var km = this.getKeyMap();
43896             km.on(27, this.onEsc, this);
43897             km.disable();
43898         }
43899     },
43900
43901     initDraggable : function(){
43902         /**
43903          * <p>If this Window is configured {@link #draggable}, this property will contain
43904          * an instance of {@link Ext.dd.DD} which handles dragging the Window's DOM Element.</p>
43905          * <p>This has implementations of <code>startDrag</code>, <code>onDrag</code> and <code>endDrag</code>
43906          * which perform the dragging action. If extra logic is needed at these points, use
43907          * {@link Function#createInterceptor createInterceptor} or {@link Function#createSequence createSequence} to
43908          * augment the existing implementations.</p>
43909          * @type Ext.dd.DD
43910          * @property dd
43911          */
43912         this.dd = new Ext.Window.DD(this);
43913     },
43914
43915    // private
43916     onEsc : function(k, e){
43917         e.stopEvent();
43918         this[this.closeAction]();
43919     },
43920
43921     // private
43922     beforeDestroy : function(){
43923         if(this.rendered){
43924             this.hide();
43925             this.clearAnchor();
43926             Ext.destroy(
43927                 this.focusEl,
43928                 this.resizer,
43929                 this.dd,
43930                 this.proxy,
43931                 this.mask
43932             );
43933         }
43934         Ext.Window.superclass.beforeDestroy.call(this);
43935     },
43936
43937     // private
43938     onDestroy : function(){
43939         if(this.manager){
43940             this.manager.unregister(this);
43941         }
43942         Ext.Window.superclass.onDestroy.call(this);
43943     },
43944
43945     // private
43946     initTools : function(){
43947         if(this.minimizable){
43948             this.addTool({
43949                 id: 'minimize',
43950                 handler: this.minimize.createDelegate(this, [])
43951             });
43952         }
43953         if(this.maximizable){
43954             this.addTool({
43955                 id: 'maximize',
43956                 handler: this.maximize.createDelegate(this, [])
43957             });
43958             this.addTool({
43959                 id: 'restore',
43960                 handler: this.restore.createDelegate(this, []),
43961                 hidden:true
43962             });
43963         }
43964         if(this.closable){
43965             this.addTool({
43966                 id: 'close',
43967                 handler: this[this.closeAction].createDelegate(this, [])
43968             });
43969         }
43970     },
43971
43972     // private
43973     resizerAction : function(){
43974         var box = this.proxy.getBox();
43975         this.proxy.hide();
43976         this.window.handleResize(box);
43977         return box;
43978     },
43979
43980     // private
43981     beforeResize : function(){
43982         this.resizer.minHeight = Math.max(this.minHeight, this.getFrameHeight() + 40); // 40 is a magic minimum content size?
43983         this.resizer.minWidth = Math.max(this.minWidth, this.getFrameWidth() + 40);
43984         this.resizeBox = this.el.getBox();
43985     },
43986
43987     // private
43988     updateHandles : function(){
43989         if(Ext.isIE && this.resizer){
43990             this.resizer.syncHandleHeight();
43991             this.el.repaint();
43992         }
43993     },
43994
43995     // private
43996     handleResize : function(box){
43997         var rz = this.resizeBox;
43998         if(rz.x != box.x || rz.y != box.y){
43999             this.updateBox(box);
44000         }else{
44001             this.setSize(box);
44002             if (Ext.isIE6 && Ext.isStrict) {
44003                 this.doLayout();
44004             }
44005         }
44006         this.focus();
44007         this.updateHandles();
44008         this.saveState();
44009     },
44010
44011     /**
44012      * Focuses the window.  If a defaultButton is set, it will receive focus, otherwise the
44013      * window itself will receive focus.
44014      */
44015     focus : function(){
44016         var f = this.focusEl,
44017             db = this.defaultButton,
44018             t = typeof db,
44019             el,
44020             ct;
44021         if(Ext.isDefined(db)){
44022             if(Ext.isNumber(db) && this.fbar){
44023                 f = this.fbar.items.get(db);
44024             }else if(Ext.isString(db)){
44025                 f = Ext.getCmp(db);
44026             }else{
44027                 f = db;
44028             }
44029             el = f.getEl();
44030             ct = Ext.getDom(this.container);
44031             if (el && ct) {
44032                 if (ct != document.body && !Ext.lib.Region.getRegion(ct).contains(Ext.lib.Region.getRegion(el.dom))){
44033                     return;
44034                 }
44035             }
44036         }
44037         f = f || this.focusEl;
44038         f.focus.defer(10, f);
44039     },
44040
44041     /**
44042      * Sets the target element from which the window should animate while opening.
44043      * @param {String/Element} el The target element or id
44044      */
44045     setAnimateTarget : function(el){
44046         el = Ext.get(el);
44047         this.animateTarget = el;
44048     },
44049
44050     // private
44051     beforeShow : function(){
44052         delete this.el.lastXY;
44053         delete this.el.lastLT;
44054         if(this.x === undefined || this.y === undefined){
44055             var xy = this.el.getAlignToXY(this.container, 'c-c');
44056             var pos = this.el.translatePoints(xy[0], xy[1]);
44057             this.x = this.x === undefined? pos.left : this.x;
44058             this.y = this.y === undefined? pos.top : this.y;
44059         }
44060         this.el.setLeftTop(this.x, this.y);
44061
44062         if(this.expandOnShow){
44063             this.expand(false);
44064         }
44065
44066         if(this.modal){
44067             Ext.getBody().addClass('x-body-masked');
44068             this.mask.setSize(Ext.lib.Dom.getViewWidth(true), Ext.lib.Dom.getViewHeight(true));
44069             this.mask.show();
44070         }
44071     },
44072
44073     /**
44074      * Shows the window, rendering it first if necessary, or activates it and brings it to front if hidden.
44075      * @param {String/Element} animateTarget (optional) The target element or id from which the window should
44076      * animate while opening (defaults to null with no animation)
44077      * @param {Function} callback (optional) A callback function to call after the window is displayed
44078      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to this Window.
44079      * @return {Ext.Window} this
44080      */
44081     show : function(animateTarget, cb, scope){
44082         if(!this.rendered){
44083             this.render(Ext.getBody());
44084         }
44085         if(this.hidden === false){
44086             this.toFront();
44087             return this;
44088         }
44089         if(this.fireEvent('beforeshow', this) === false){
44090             return this;
44091         }
44092         if(cb){
44093             this.on('show', cb, scope, {single:true});
44094         }
44095         this.hidden = false;
44096         if(Ext.isDefined(animateTarget)){
44097             this.setAnimateTarget(animateTarget);
44098         }
44099         this.beforeShow();
44100         if(this.animateTarget){
44101             this.animShow();
44102         }else{
44103             this.afterShow();
44104         }
44105         return this;
44106     },
44107
44108     // private
44109     afterShow : function(isAnim){
44110         if (this.isDestroyed){
44111             return false;
44112         }
44113         this.proxy.hide();
44114         this.el.setStyle('display', 'block');
44115         this.el.show();
44116         if(this.maximized){
44117             this.fitContainer();
44118         }
44119         if(Ext.isMac && Ext.isGecko2){ // work around stupid FF 2.0/Mac scroll bar bug
44120             this.cascade(this.setAutoScroll);
44121         }
44122
44123         if(this.monitorResize || this.modal || this.constrain || this.constrainHeader){
44124             Ext.EventManager.onWindowResize(this.onWindowResize, this);
44125         }
44126         this.doConstrain();
44127         this.doLayout();
44128         if(this.keyMap){
44129             this.keyMap.enable();
44130         }
44131         this.toFront();
44132         this.updateHandles();
44133         if(isAnim && (Ext.isIE || Ext.isWebKit)){
44134             var sz = this.getSize();
44135             this.onResize(sz.width, sz.height);
44136         }
44137         this.onShow();
44138         this.fireEvent('show', this);
44139     },
44140
44141     // private
44142     animShow : function(){
44143         this.proxy.show();
44144         this.proxy.setBox(this.animateTarget.getBox());
44145         this.proxy.setOpacity(0);
44146         var b = this.getBox();
44147         this.el.setStyle('display', 'none');
44148         this.proxy.shift(Ext.apply(b, {
44149             callback: this.afterShow.createDelegate(this, [true], false),
44150             scope: this,
44151             easing: 'easeNone',
44152             duration: this.showAnimDuration,
44153             opacity: 0.5
44154         }));
44155     },
44156
44157     /**
44158      * Hides the window, setting it to invisible and applying negative offsets.
44159      * @param {String/Element} animateTarget (optional) The target element or id to which the window should
44160      * animate while hiding (defaults to null with no animation)
44161      * @param {Function} callback (optional) A callback function to call after the window is hidden
44162      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to this Window.
44163      * @return {Ext.Window} this
44164      */
44165     hide : function(animateTarget, cb, scope){
44166         if(this.hidden || this.fireEvent('beforehide', this) === false){
44167             return this;
44168         }
44169         if(cb){
44170             this.on('hide', cb, scope, {single:true});
44171         }
44172         this.hidden = true;
44173         if(animateTarget !== undefined){
44174             this.setAnimateTarget(animateTarget);
44175         }
44176         if(this.modal){
44177             this.mask.hide();
44178             Ext.getBody().removeClass('x-body-masked');
44179         }
44180         if(this.animateTarget){
44181             this.animHide();
44182         }else{
44183             this.el.hide();
44184             this.afterHide();
44185         }
44186         return this;
44187     },
44188
44189     // private
44190     afterHide : function(){
44191         this.proxy.hide();
44192         if(this.monitorResize || this.modal || this.constrain || this.constrainHeader){
44193             Ext.EventManager.removeResizeListener(this.onWindowResize, this);
44194         }
44195         if(this.keyMap){
44196             this.keyMap.disable();
44197         }
44198         this.onHide();
44199         this.fireEvent('hide', this);
44200     },
44201
44202     // private
44203     animHide : function(){
44204         this.proxy.setOpacity(0.5);
44205         this.proxy.show();
44206         var tb = this.getBox(false);
44207         this.proxy.setBox(tb);
44208         this.el.hide();
44209         this.proxy.shift(Ext.apply(this.animateTarget.getBox(), {
44210             callback: this.afterHide,
44211             scope: this,
44212             duration: this.hideAnimDuration,
44213             easing: 'easeNone',
44214             opacity: 0
44215         }));
44216     },
44217
44218     /**
44219      * Method that is called immediately before the <code>show</code> event is fired.
44220      * Defaults to <code>Ext.emptyFn</code>.
44221      */
44222     onShow : Ext.emptyFn,
44223
44224     /**
44225      * Method that is called immediately before the <code>hide</code> event is fired.
44226      * Defaults to <code>Ext.emptyFn</code>.
44227      */
44228     onHide : Ext.emptyFn,
44229
44230     // private
44231     onWindowResize : function(){
44232         if(this.maximized){
44233             this.fitContainer();
44234         }
44235         if(this.modal){
44236             this.mask.setSize('100%', '100%');
44237             var force = this.mask.dom.offsetHeight;
44238             this.mask.setSize(Ext.lib.Dom.getViewWidth(true), Ext.lib.Dom.getViewHeight(true));
44239         }
44240         this.doConstrain();
44241     },
44242
44243     // private
44244     doConstrain : function(){
44245         if(this.constrain || this.constrainHeader){
44246             var offsets;
44247             if(this.constrain){
44248                 offsets = {
44249                     right:this.el.shadowOffset,
44250                     left:this.el.shadowOffset,
44251                     bottom:this.el.shadowOffset
44252                 };
44253             }else {
44254                 var s = this.getSize();
44255                 offsets = {
44256                     right:-(s.width - 100),
44257                     bottom:-(s.height - 25)
44258                 };
44259             }
44260
44261             var xy = this.el.getConstrainToXY(this.container, true, offsets);
44262             if(xy){
44263                 this.setPosition(xy[0], xy[1]);
44264             }
44265         }
44266     },
44267
44268     // private - used for dragging
44269     ghost : function(cls){
44270         var ghost = this.createGhost(cls);
44271         var box = this.getBox(true);
44272         ghost.setLeftTop(box.x, box.y);
44273         ghost.setWidth(box.width);
44274         this.el.hide();
44275         this.activeGhost = ghost;
44276         return ghost;
44277     },
44278
44279     // private
44280     unghost : function(show, matchPosition){
44281         if(!this.activeGhost) {
44282             return;
44283         }
44284         if(show !== false){
44285             this.el.show();
44286             this.focus.defer(10, this);
44287             if(Ext.isMac && Ext.isGecko2){ // work around stupid FF 2.0/Mac scroll bar bug
44288                 this.cascade(this.setAutoScroll);
44289             }
44290         }
44291         if(matchPosition !== false){
44292             this.setPosition(this.activeGhost.getLeft(true), this.activeGhost.getTop(true));
44293         }
44294         this.activeGhost.hide();
44295         this.activeGhost.remove();
44296         delete this.activeGhost;
44297     },
44298
44299     /**
44300      * Placeholder method for minimizing the window.  By default, this method simply fires the {@link #minimize} event
44301      * since the behavior of minimizing a window is application-specific.  To implement custom minimize behavior,
44302      * either the minimize event can be handled or this method can be overridden.
44303      * @return {Ext.Window} this
44304      */
44305     minimize : function(){
44306         this.fireEvent('minimize', this);
44307         return this;
44308     },
44309
44310     /**
44311      * <p>Closes the Window, removes it from the DOM, {@link Ext.Component#destroy destroy}s
44312      * the Window object and all its descendant Components. The {@link Ext.Panel#beforeclose beforeclose}
44313      * event is fired before the close happens and will cancel the close action if it returns false.<p>
44314      * <p><b>Note:</b> This method is not affected by the {@link #closeAction} setting which
44315      * only affects the action triggered when clicking the {@link #closable 'close' tool in the header}.
44316      * To hide the Window without destroying it, call {@link #hide}.</p>
44317      */
44318     close : function(){
44319         if(this.fireEvent('beforeclose', this) !== false){
44320             if(this.hidden){
44321                 this.doClose();
44322             }else{
44323                 this.hide(null, this.doClose, this);
44324             }
44325         }
44326     },
44327
44328     // private
44329     doClose : function(){
44330         this.fireEvent('close', this);
44331         this.destroy();
44332     },
44333
44334     /**
44335      * Fits the window within its current container and automatically replaces
44336      * the {@link #maximizable 'maximize' tool button} with the 'restore' tool button.
44337      * Also see {@link #toggleMaximize}.
44338      * @return {Ext.Window} this
44339      */
44340     maximize : function(){
44341         if(!this.maximized){
44342             this.expand(false);
44343             this.restoreSize = this.getSize();
44344             this.restorePos = this.getPosition(true);
44345             if (this.maximizable){
44346                 this.tools.maximize.hide();
44347                 this.tools.restore.show();
44348             }
44349             this.maximized = true;
44350             this.el.disableShadow();
44351
44352             if(this.dd){
44353                 this.dd.lock();
44354             }
44355             if(this.collapsible){
44356                 this.tools.toggle.hide();
44357             }
44358             this.el.addClass('x-window-maximized');
44359             this.container.addClass('x-window-maximized-ct');
44360
44361             this.setPosition(0, 0);
44362             this.fitContainer();
44363             this.fireEvent('maximize', this);
44364         }
44365         return this;
44366     },
44367
44368     /**
44369      * Restores a {@link #maximizable maximized}  window back to its original
44370      * size and position prior to being maximized and also replaces
44371      * the 'restore' tool button with the 'maximize' tool button.
44372      * Also see {@link #toggleMaximize}.
44373      * @return {Ext.Window} this
44374      */
44375     restore : function(){
44376         if(this.maximized){
44377             var t = this.tools;
44378             this.el.removeClass('x-window-maximized');
44379             if(t.restore){
44380                 t.restore.hide();
44381             }
44382             if(t.maximize){
44383                 t.maximize.show();
44384             }
44385             this.setPosition(this.restorePos[0], this.restorePos[1]);
44386             this.setSize(this.restoreSize.width, this.restoreSize.height);
44387             delete this.restorePos;
44388             delete this.restoreSize;
44389             this.maximized = false;
44390             this.el.enableShadow(true);
44391
44392             if(this.dd){
44393                 this.dd.unlock();
44394             }
44395             if(this.collapsible && t.toggle){
44396                 t.toggle.show();
44397             }
44398             this.container.removeClass('x-window-maximized-ct');
44399
44400             this.doConstrain();
44401             this.fireEvent('restore', this);
44402         }
44403         return this;
44404     },
44405
44406     /**
44407      * A shortcut method for toggling between {@link #maximize} and {@link #restore} based on the current maximized
44408      * state of the window.
44409      * @return {Ext.Window} this
44410      */
44411     toggleMaximize : function(){
44412         return this[this.maximized ? 'restore' : 'maximize']();
44413     },
44414
44415     // private
44416     fitContainer : function(){
44417         var vs = this.container.getViewSize(false);
44418         this.setSize(vs.width, vs.height);
44419     },
44420
44421     // private
44422     // z-index is managed by the WindowManager and may be overwritten at any time
44423     setZIndex : function(index){
44424         if(this.modal){
44425             this.mask.setStyle('z-index', index);
44426         }
44427         this.el.setZIndex(++index);
44428         index += 5;
44429
44430         if(this.resizer){
44431             this.resizer.proxy.setStyle('z-index', ++index);
44432         }
44433
44434         this.lastZIndex = index;
44435     },
44436
44437     /**
44438      * Aligns the window to the specified element
44439      * @param {Mixed} element The element to align to.
44440      * @param {String} position (optional, defaults to "tl-bl?") The position to align to (see {@link Ext.Element#alignTo} for more details).
44441      * @param {Array} offsets (optional) Offset the positioning by [x, y]
44442      * @return {Ext.Window} this
44443      */
44444     alignTo : function(element, position, offsets){
44445         var xy = this.el.getAlignToXY(element, position, offsets);
44446         this.setPagePosition(xy[0], xy[1]);
44447         return this;
44448     },
44449
44450     /**
44451      * Anchors this window to another element and realigns it when the window is resized or scrolled.
44452      * @param {Mixed} element The element to align to.
44453      * @param {String} position The position to align to (see {@link Ext.Element#alignTo} for more details)
44454      * @param {Array} offsets (optional) Offset the positioning by [x, y]
44455      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
44456      * is a number, it is used as the buffer delay (defaults to 50ms).
44457      * @return {Ext.Window} this
44458      */
44459     anchorTo : function(el, alignment, offsets, monitorScroll){
44460         this.clearAnchor();
44461         this.anchorTarget = {
44462             el: el,
44463             alignment: alignment,
44464             offsets: offsets
44465         };
44466
44467         Ext.EventManager.onWindowResize(this.doAnchor, this);
44468         var tm = typeof monitorScroll;
44469         if(tm != 'undefined'){
44470             Ext.EventManager.on(window, 'scroll', this.doAnchor, this,
44471                 {buffer: tm == 'number' ? monitorScroll : 50});
44472         }
44473         return this.doAnchor();
44474     },
44475
44476     /**
44477      * Performs the anchor, using the saved anchorTarget property.
44478      * @return {Ext.Window} this
44479      * @private
44480      */
44481     doAnchor : function(){
44482         var o = this.anchorTarget;
44483         this.alignTo(o.el, o.alignment, o.offsets);
44484         return this;
44485     },
44486
44487     /**
44488      * Removes any existing anchor from this window. See {@link #anchorTo}.
44489      * @return {Ext.Window} this
44490      */
44491     clearAnchor : function(){
44492         if(this.anchorTarget){
44493             Ext.EventManager.removeResizeListener(this.doAnchor, this);
44494             Ext.EventManager.un(window, 'scroll', this.doAnchor, this);
44495             delete this.anchorTarget;
44496         }
44497         return this;
44498     },
44499
44500     /**
44501      * Brings this window to the front of any other visible windows
44502      * @param {Boolean} e (optional) Specify <tt>false</tt> to prevent the window from being focused.
44503      * @return {Ext.Window} this
44504      */
44505     toFront : function(e){
44506         if(this.manager.bringToFront(this)){
44507             if(!e || !e.getTarget().focus){
44508                 this.focus();
44509             }
44510         }
44511         return this;
44512     },
44513
44514     /**
44515      * Makes this the active window by showing its shadow, or deactivates it by hiding its shadow.  This method also
44516      * fires the {@link #activate} or {@link #deactivate} event depending on which action occurred. This method is
44517      * called internally by {@link Ext.WindowMgr}.
44518      * @param {Boolean} active True to activate the window, false to deactivate it (defaults to false)
44519      */
44520     setActive : function(active){
44521         if(active){
44522             if(!this.maximized){
44523                 this.el.enableShadow(true);
44524             }
44525             this.fireEvent('activate', this);
44526         }else{
44527             this.el.disableShadow();
44528             this.fireEvent('deactivate', this);
44529         }
44530     },
44531
44532     /**
44533      * Sends this window to the back of (lower z-index than) any other visible windows
44534      * @return {Ext.Window} this
44535      */
44536     toBack : function(){
44537         this.manager.sendToBack(this);
44538         return this;
44539     },
44540
44541     /**
44542      * Centers this window in the viewport
44543      * @return {Ext.Window} this
44544      */
44545     center : function(){
44546         var xy = this.el.getAlignToXY(this.container, 'c-c');
44547         this.setPagePosition(xy[0], xy[1]);
44548         return this;
44549     }
44550
44551     /**
44552      * @cfg {Boolean} autoWidth @hide
44553      **/
44554 });
44555 Ext.reg('window', Ext.Window);
44556
44557 // private - custom Window DD implementation
44558 Ext.Window.DD = Ext.extend(Ext.dd.DD, {
44559     
44560     constructor : function(win){
44561         this.win = win;
44562         Ext.Window.DD.superclass.constructor.call(this, win.el.id, 'WindowDD-'+win.id);
44563         this.setHandleElId(win.header.id);
44564         this.scroll = false;        
44565     },
44566     
44567     moveOnly:true,
44568     headerOffsets:[100, 25],
44569     startDrag : function(){
44570         var w = this.win;
44571         this.proxy = w.ghost(w.initialConfig.cls);
44572         if(w.constrain !== false){
44573             var so = w.el.shadowOffset;
44574             this.constrainTo(w.container, {right: so, left: so, bottom: so});
44575         }else if(w.constrainHeader !== false){
44576             var s = this.proxy.getSize();
44577             this.constrainTo(w.container, {right: -(s.width-this.headerOffsets[0]), bottom: -(s.height-this.headerOffsets[1])});
44578         }
44579     },
44580     b4Drag : Ext.emptyFn,
44581
44582     onDrag : function(e){
44583         this.alignElWithMouse(this.proxy, e.getPageX(), e.getPageY());
44584     },
44585
44586     endDrag : function(e){
44587         this.win.unghost();
44588         this.win.saveState();
44589     }
44590 });
44591 /**
44592  * @class Ext.WindowGroup
44593  * An object that manages a group of {@link Ext.Window} instances and provides z-order management
44594  * and window activation behavior.
44595  * @constructor
44596  */
44597 Ext.WindowGroup = function(){
44598     var list = {};
44599     var accessList = [];
44600     var front = null;
44601
44602     // private
44603     var sortWindows = function(d1, d2){
44604         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
44605     };
44606
44607     // private
44608     var orderWindows = function(){
44609         var a = accessList, len = a.length;
44610         if(len > 0){
44611             a.sort(sortWindows);
44612             var seed = a[0].manager.zseed;
44613             for(var i = 0; i < len; i++){
44614                 var win = a[i];
44615                 if(win && !win.hidden){
44616                     win.setZIndex(seed + (i*10));
44617                 }
44618             }
44619         }
44620         activateLast();
44621     };
44622
44623     // private
44624     var setActiveWin = function(win){
44625         if(win != front){
44626             if(front){
44627                 front.setActive(false);
44628             }
44629             front = win;
44630             if(win){
44631                 win.setActive(true);
44632             }
44633         }
44634     };
44635
44636     // private
44637     var activateLast = function(){
44638         for(var i = accessList.length-1; i >=0; --i) {
44639             if(!accessList[i].hidden){
44640                 setActiveWin(accessList[i]);
44641                 return;
44642             }
44643         }
44644         // none to activate
44645         setActiveWin(null);
44646     };
44647
44648     return {
44649         /**
44650          * The starting z-index for windows in this WindowGroup (defaults to 9000)
44651          * @type Number The z-index value
44652          */
44653         zseed : 9000,
44654
44655         /**
44656          * <p>Registers a {@link Ext.Window Window} with this WindowManager. This should not
44657          * need to be called under normal circumstances. Windows are automatically registered
44658          * with a {@link Ext.Window#manager manager} at construction time.</p>
44659          * <p>Where this may be useful is moving Windows between two WindowManagers. For example,
44660          * to bring the Ext.MessageBox dialog under the same manager as the Desktop's
44661          * WindowManager in the desktop sample app:</p><code><pre>
44662 var msgWin = Ext.MessageBox.getDialog();
44663 MyDesktop.getDesktop().getManager().register(msgWin);
44664 </pre></code>
44665          * @param {Window} win The Window to register.
44666          */
44667         register : function(win){
44668             if(win.manager){
44669                 win.manager.unregister(win);
44670             }
44671             win.manager = this;
44672
44673             list[win.id] = win;
44674             accessList.push(win);
44675             win.on('hide', activateLast);
44676         },
44677
44678         /**
44679          * <p>Unregisters a {@link Ext.Window Window} from this WindowManager. This should not
44680          * need to be called. Windows are automatically unregistered upon destruction.
44681          * See {@link #register}.</p>
44682          * @param {Window} win The Window to unregister.
44683          */
44684         unregister : function(win){
44685             delete win.manager;
44686             delete list[win.id];
44687             win.un('hide', activateLast);
44688             accessList.remove(win);
44689         },
44690
44691         /**
44692          * Gets a registered window by id.
44693          * @param {String/Object} id The id of the window or a {@link Ext.Window} instance
44694          * @return {Ext.Window}
44695          */
44696         get : function(id){
44697             return typeof id == "object" ? id : list[id];
44698         },
44699
44700         /**
44701          * Brings the specified window to the front of any other active windows in this WindowGroup.
44702          * @param {String/Object} win The id of the window or a {@link Ext.Window} instance
44703          * @return {Boolean} True if the dialog was brought to the front, else false
44704          * if it was already in front
44705          */
44706         bringToFront : function(win){
44707             win = this.get(win);
44708             if(win != front){
44709                 win._lastAccess = new Date().getTime();
44710                 orderWindows();
44711                 return true;
44712             }
44713             return false;
44714         },
44715
44716         /**
44717          * Sends the specified window to the back of other active windows in this WindowGroup.
44718          * @param {String/Object} win The id of the window or a {@link Ext.Window} instance
44719          * @return {Ext.Window} The window
44720          */
44721         sendToBack : function(win){
44722             win = this.get(win);
44723             win._lastAccess = -(new Date().getTime());
44724             orderWindows();
44725             return win;
44726         },
44727
44728         /**
44729          * Hides all windows in this WindowGroup.
44730          */
44731         hideAll : function(){
44732             for(var id in list){
44733                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
44734                     list[id].hide();
44735                 }
44736             }
44737         },
44738
44739         /**
44740          * Gets the currently-active window in this WindowGroup.
44741          * @return {Ext.Window} The active window
44742          */
44743         getActive : function(){
44744             return front;
44745         },
44746
44747         /**
44748          * Returns zero or more windows in this WindowGroup using the custom search function passed to this method.
44749          * The function should accept a single {@link Ext.Window} reference as its only argument and should
44750          * return true if the window matches the search criteria, otherwise it should return false.
44751          * @param {Function} fn The search function
44752          * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the Window being tested.
44753          * that gets passed to the function if not specified)
44754          * @return {Array} An array of zero or more matching windows
44755          */
44756         getBy : function(fn, scope){
44757             var r = [];
44758             for(var i = accessList.length-1; i >=0; --i) {
44759                 var win = accessList[i];
44760                 if(fn.call(scope||win, win) !== false){
44761                     r.push(win);
44762                 }
44763             }
44764             return r;
44765         },
44766
44767         /**
44768          * Executes the specified function once for every window in this WindowGroup, passing each
44769          * window as the only parameter. Returning false from the function will stop the iteration.
44770          * @param {Function} fn The function to execute for each item
44771          * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the current Window in the iteration.
44772          */
44773         each : function(fn, scope){
44774             for(var id in list){
44775                 if(list[id] && typeof list[id] != "function"){
44776                     if(fn.call(scope || list[id], list[id]) === false){
44777                         return;
44778                     }
44779                 }
44780             }
44781         }
44782     };
44783 };
44784
44785
44786 /**
44787  * @class Ext.WindowMgr
44788  * @extends Ext.WindowGroup
44789  * The default global window group that is available automatically.  To have more than one group of windows
44790  * with separate z-order stacks, create additional instances of {@link Ext.WindowGroup} as needed.
44791  * @singleton
44792  */
44793 Ext.WindowMgr = new Ext.WindowGroup();/**
44794  * @class Ext.MessageBox
44795  * <p>Utility class for generating different styles of message boxes.  The alias Ext.Msg can also be used.<p/>
44796  * <p>Note that the MessageBox is asynchronous.  Unlike a regular JavaScript <code>alert</code> (which will halt
44797  * browser execution), showing a MessageBox will not cause the code to stop.  For this reason, if you have code
44798  * that should only run <em>after</em> some user feedback from the MessageBox, you must use a callback function
44799  * (see the <code>function</code> parameter for {@link #show} for more details).</p>
44800  * <p>Example usage:</p>
44801  *<pre><code>
44802 // Basic alert:
44803 Ext.Msg.alert('Status', 'Changes saved successfully.');
44804
44805 // Prompt for user data and process the result using a callback:
44806 Ext.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
44807     if (btn == 'ok'){
44808         // process text value and close...
44809     }
44810 });
44811
44812 // Show a dialog using config options:
44813 Ext.Msg.show({
44814    title:'Save Changes?',
44815    msg: 'You are closing a tab that has unsaved changes. Would you like to save your changes?',
44816    buttons: Ext.Msg.YESNOCANCEL,
44817    fn: processResult,
44818    animEl: 'elId',
44819    icon: Ext.MessageBox.QUESTION
44820 });
44821 </code></pre>
44822  * @singleton
44823  */
44824 Ext.MessageBox = function(){
44825     var dlg, opt, mask, waitTimer,
44826         bodyEl, msgEl, textboxEl, textareaEl, progressBar, pp, iconEl, spacerEl,
44827         buttons, activeTextEl, bwidth, bufferIcon = '', iconCls = '',
44828         buttonNames = ['ok', 'yes', 'no', 'cancel'];
44829
44830     // private
44831     var handleButton = function(button){
44832         buttons[button].blur();
44833         if(dlg.isVisible()){
44834             dlg.hide();
44835             handleHide();
44836             Ext.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value, opt], 1);
44837         }
44838     };
44839
44840     // private
44841     var handleHide = function(){
44842         if(opt && opt.cls){
44843             dlg.el.removeClass(opt.cls);
44844         }
44845         progressBar.reset();        
44846     };
44847
44848     // private
44849     var handleEsc = function(d, k, e){
44850         if(opt && opt.closable !== false){
44851             dlg.hide();
44852             handleHide();
44853         }
44854         if(e){
44855             e.stopEvent();
44856         }
44857     };
44858
44859     // private
44860     var updateButtons = function(b){
44861         var width = 0,
44862             cfg;
44863         if(!b){
44864             Ext.each(buttonNames, function(name){
44865                 buttons[name].hide();
44866             });
44867             return width;
44868         }
44869         dlg.footer.dom.style.display = '';
44870         Ext.iterate(buttons, function(name, btn){
44871             cfg = b[name];
44872             if(cfg){
44873                 btn.show();
44874                 btn.setText(Ext.isString(cfg) ? cfg : Ext.MessageBox.buttonText[name]);
44875                 width += btn.getEl().getWidth() + 15;
44876             }else{
44877                 btn.hide();
44878             }
44879         });
44880         return width;
44881     };
44882
44883     return {
44884         /**
44885          * Returns a reference to the underlying {@link Ext.Window} element
44886          * @return {Ext.Window} The window
44887          */
44888         getDialog : function(titleText){
44889            if(!dlg){
44890                 var btns = [];
44891                 
44892                 buttons = {};
44893                 Ext.each(buttonNames, function(name){
44894                     btns.push(buttons[name] = new Ext.Button({
44895                         text: this.buttonText[name],
44896                         handler: handleButton.createCallback(name),
44897                         hideMode: 'offsets'
44898                     }));
44899                 }, this);
44900                 dlg = new Ext.Window({
44901                     autoCreate : true,
44902                     title:titleText,
44903                     resizable:false,
44904                     constrain:true,
44905                     constrainHeader:true,
44906                     minimizable : false,
44907                     maximizable : false,
44908                     stateful: false,
44909                     modal: true,
44910                     shim:true,
44911                     buttonAlign:"center",
44912                     width:400,
44913                     height:100,
44914                     minHeight: 80,
44915                     plain:true,
44916                     footer:true,
44917                     closable:true,
44918                     close : function(){
44919                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
44920                             handleButton("no");
44921                         }else{
44922                             handleButton("cancel");
44923                         }
44924                     },
44925                     fbar: new Ext.Toolbar({
44926                         items: btns,
44927                         enableOverflow: false
44928                     })
44929                 });
44930                 dlg.render(document.body);
44931                 dlg.getEl().addClass('x-window-dlg');
44932                 mask = dlg.mask;
44933                 bodyEl = dlg.body.createChild({
44934                     html:'<div class="ext-mb-icon"></div><div class="ext-mb-content"><span class="ext-mb-text"></span><br /><div class="ext-mb-fix-cursor"><input type="text" class="ext-mb-input" /><textarea class="ext-mb-textarea"></textarea></div></div>'
44935                 });
44936                 iconEl = Ext.get(bodyEl.dom.firstChild);
44937                 var contentEl = bodyEl.dom.childNodes[1];
44938                 msgEl = Ext.get(contentEl.firstChild);
44939                 textboxEl = Ext.get(contentEl.childNodes[2].firstChild);
44940                 textboxEl.enableDisplayMode();
44941                 textboxEl.addKeyListener([10,13], function(){
44942                     if(dlg.isVisible() && opt && opt.buttons){
44943                         if(opt.buttons.ok){
44944                             handleButton("ok");
44945                         }else if(opt.buttons.yes){
44946                             handleButton("yes");
44947                         }
44948                     }
44949                 });
44950                 textareaEl = Ext.get(contentEl.childNodes[2].childNodes[1]);
44951                 textareaEl.enableDisplayMode();
44952                 progressBar = new Ext.ProgressBar({
44953                     renderTo:bodyEl
44954                 });
44955                bodyEl.createChild({cls:'x-clear'});
44956             }
44957             return dlg;
44958         },
44959
44960         /**
44961          * Updates the message box body text
44962          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
44963          * the XHTML-compliant non-breaking space character '&amp;#160;')
44964          * @return {Ext.MessageBox} this
44965          */
44966         updateText : function(text){
44967             if(!dlg.isVisible() && !opt.width){
44968                 dlg.setSize(this.maxWidth, 100); // resize first so content is never clipped from previous shows
44969             }
44970             // Append a space here for sizing. In IE, for some reason, it wraps text incorrectly without one in some cases
44971             msgEl.update(text ? text + ' ' : '&#160;');
44972
44973             var iw = iconCls != '' ? (iconEl.getWidth() + iconEl.getMargins('lr')) : 0,
44974                 mw = msgEl.getWidth() + msgEl.getMargins('lr'),
44975                 fw = dlg.getFrameWidth('lr'),
44976                 bw = dlg.body.getFrameWidth('lr'),
44977                 w;
44978                 
44979             w = Math.max(Math.min(opt.width || iw+mw+fw+bw, opt.maxWidth || this.maxWidth),
44980                     Math.max(opt.minWidth || this.minWidth, bwidth || 0));
44981
44982             if(opt.prompt === true){
44983                 activeTextEl.setWidth(w-iw-fw-bw);
44984             }
44985             if(opt.progress === true || opt.wait === true){
44986                 progressBar.setSize(w-iw-fw-bw);
44987             }
44988             if(Ext.isIE && w == bwidth){
44989                 w += 4; //Add offset when the content width is smaller than the buttons.    
44990             }
44991             msgEl.update(text || '&#160;');
44992             dlg.setSize(w, 'auto').center();
44993             return this;
44994         },
44995
44996         /**
44997          * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
44998          * initiated via {@link Ext.MessageBox#progress} or {@link Ext.MessageBox#wait},
44999          * or by calling {@link Ext.MessageBox#show} with progress: true.
45000          * @param {Number} value Any number between 0 and 1 (e.g., .5, defaults to 0)
45001          * @param {String} progressText The progress text to display inside the progress bar (defaults to '')
45002          * @param {String} msg The message box's body text is replaced with the specified string (defaults to undefined
45003          * so that any existing body text will not get overwritten by default unless a new value is passed in)
45004          * @return {Ext.MessageBox} this
45005          */
45006         updateProgress : function(value, progressText, msg){
45007             progressBar.updateProgress(value, progressText);
45008             if(msg){
45009                 this.updateText(msg);
45010             }
45011             return this;
45012         },
45013
45014         /**
45015          * Returns true if the message box is currently displayed
45016          * @return {Boolean} True if the message box is visible, else false
45017          */
45018         isVisible : function(){
45019             return dlg && dlg.isVisible();
45020         },
45021
45022         /**
45023          * Hides the message box if it is displayed
45024          * @return {Ext.MessageBox} this
45025          */
45026         hide : function(){
45027             var proxy = dlg ? dlg.activeGhost : null;
45028             if(this.isVisible() || proxy){
45029                 dlg.hide();
45030                 handleHide();
45031                 if (proxy){
45032                     // unghost is a private function, but i saw no better solution
45033                     // to fix the locking problem when dragging while it closes
45034                     dlg.unghost(false, false);
45035                 } 
45036             }
45037             return this;
45038         },
45039
45040         /**
45041          * Displays a new message box, or reinitializes an existing message box, based on the config options
45042          * passed in. All display functions (e.g. prompt, alert, etc.) on MessageBox call this function internally,
45043          * although those calls are basic shortcuts and do not support all of the config options allowed here.
45044          * @param {Object} config The following config options are supported: <ul>
45045          * <li><b>animEl</b> : String/Element<div class="sub-desc">An id or Element from which the message box should animate as it
45046          * opens and closes (defaults to undefined)</div></li>
45047          * <li><b>buttons</b> : Object/Boolean<div class="sub-desc">A button config object (e.g., Ext.MessageBox.OKCANCEL or {ok:'Foo',
45048          * cancel:'Bar'}), or false to not show any buttons (defaults to false)</div></li>
45049          * <li><b>closable</b> : Boolean<div class="sub-desc">False to hide the top-right close button (defaults to true). Note that
45050          * progress and wait dialogs will ignore this property and always hide the close button as they can only
45051          * be closed programmatically.</div></li>
45052          * <li><b>cls</b> : String<div class="sub-desc">A custom CSS class to apply to the message box's container element</div></li>
45053          * <li><b>defaultTextHeight</b> : Number<div class="sub-desc">The default height in pixels of the message box's multiline textarea
45054          * if displayed (defaults to 75)</div></li>
45055          * <li><b>fn</b> : Function<div class="sub-desc">A callback function which is called when the dialog is dismissed either
45056          * by clicking on the configured buttons, or on the dialog close button, or by pressing
45057          * the return button to enter input.
45058          * <p>Progress and wait dialogs will ignore this option since they do not respond to user
45059          * actions and can only be closed programmatically, so any required function should be called
45060          * by the same code after it closes the dialog. Parameters passed:<ul>
45061          * <li><b>buttonId</b> : String<div class="sub-desc">The ID of the button pressed, one of:<div class="sub-desc"><ul>
45062          * <li><tt>ok</tt></li>
45063          * <li><tt>yes</tt></li>
45064          * <li><tt>no</tt></li>
45065          * <li><tt>cancel</tt></li>
45066          * </ul></div></div></li>
45067          * <li><b>text</b> : String<div class="sub-desc">Value of the input field if either <tt><a href="#show-option-prompt" ext:member="show-option-prompt" ext:cls="Ext.MessageBox">prompt</a></tt>
45068          * or <tt><a href="#show-option-multiline" ext:member="show-option-multiline" ext:cls="Ext.MessageBox">multiline</a></tt> is true</div></li>
45069          * <li><b>opt</b> : Object<div class="sub-desc">The config object passed to show.</div></li>
45070          * </ul></p></div></li>
45071          * <li><b>scope</b> : Object<div class="sub-desc">The scope of the callback function</div></li>
45072          * <li><b>icon</b> : String<div class="sub-desc">A CSS class that provides a background image to be used as the body icon for the
45073          * dialog (e.g. Ext.MessageBox.WARNING or 'custom-class') (defaults to '')</div></li>
45074          * <li><b>iconCls</b> : String<div class="sub-desc">The standard {@link Ext.Window#iconCls} to
45075          * add an optional header icon (defaults to '')</div></li>
45076          * <li><b>maxWidth</b> : Number<div class="sub-desc">The maximum width in pixels of the message box (defaults to 600)</div></li>
45077          * <li><b>minWidth</b> : Number<div class="sub-desc">The minimum width in pixels of the message box (defaults to 100)</div></li>
45078          * <li><b>modal</b> : Boolean<div class="sub-desc">False to allow user interaction with the page while the message box is
45079          * displayed (defaults to true)</div></li>
45080          * <li><b>msg</b> : String<div class="sub-desc">A string that will replace the existing message box body text (defaults to the
45081          * XHTML-compliant non-breaking space character '&amp;#160;')</div></li>
45082          * <li><a id="show-option-multiline"></a><b>multiline</b> : Boolean<div class="sub-desc">
45083          * True to prompt the user to enter multi-line text (defaults to false)</div></li>
45084          * <li><b>progress</b> : Boolean<div class="sub-desc">True to display a progress bar (defaults to false)</div></li>
45085          * <li><b>progressText</b> : String<div class="sub-desc">The text to display inside the progress bar if progress = true (defaults to '')</div></li>
45086          * <li><a id="show-option-prompt"></a><b>prompt</b> : Boolean<div class="sub-desc">True to prompt the user to enter single-line text (defaults to false)</div></li>
45087          * <li><b>proxyDrag</b> : Boolean<div class="sub-desc">True to display a lightweight proxy while dragging (defaults to false)</div></li>
45088          * <li><b>title</b> : String<div class="sub-desc">The title text</div></li>
45089          * <li><b>value</b> : String<div class="sub-desc">The string value to set into the active textbox element if displayed</div></li>
45090          * <li><b>wait</b> : Boolean<div class="sub-desc">True to display a progress bar (defaults to false)</div></li>
45091          * <li><b>waitConfig</b> : Object<div class="sub-desc">A {@link Ext.ProgressBar#waitConfig} object (applies only if wait = true)</div></li>
45092          * <li><b>width</b> : Number<div class="sub-desc">The width of the dialog in pixels</div></li>
45093          * </ul>
45094          * Example usage:
45095          * <pre><code>
45096 Ext.Msg.show({
45097    title: 'Address',
45098    msg: 'Please enter your address:',
45099    width: 300,
45100    buttons: Ext.MessageBox.OKCANCEL,
45101    multiline: true,
45102    fn: saveAddress,
45103    animEl: 'addAddressBtn',
45104    icon: Ext.MessageBox.INFO
45105 });
45106 </code></pre>
45107          * @return {Ext.MessageBox} this
45108          */
45109         show : function(options){
45110             if(this.isVisible()){
45111                 this.hide();
45112             }
45113             opt = options;
45114             var d = this.getDialog(opt.title || "&#160;");
45115
45116             d.setTitle(opt.title || "&#160;");
45117             var allowClose = (opt.closable !== false && opt.progress !== true && opt.wait !== true);
45118             d.tools.close.setDisplayed(allowClose);
45119             activeTextEl = textboxEl;
45120             opt.prompt = opt.prompt || (opt.multiline ? true : false);
45121             if(opt.prompt){
45122                 if(opt.multiline){
45123                     textboxEl.hide();
45124                     textareaEl.show();
45125                     textareaEl.setHeight(Ext.isNumber(opt.multiline) ? opt.multiline : this.defaultTextHeight);
45126                     activeTextEl = textareaEl;
45127                 }else{
45128                     textboxEl.show();
45129                     textareaEl.hide();
45130                 }
45131             }else{
45132                 textboxEl.hide();
45133                 textareaEl.hide();
45134             }
45135             activeTextEl.dom.value = opt.value || "";
45136             if(opt.prompt){
45137                 d.focusEl = activeTextEl;
45138             }else{
45139                 var bs = opt.buttons;
45140                 var db = null;
45141                 if(bs && bs.ok){
45142                     db = buttons["ok"];
45143                 }else if(bs && bs.yes){
45144                     db = buttons["yes"];
45145                 }
45146                 if (db){
45147                     d.focusEl = db;
45148                 }
45149             }
45150             if(Ext.isDefined(opt.iconCls)){
45151               d.setIconClass(opt.iconCls);
45152             }
45153             this.setIcon(Ext.isDefined(opt.icon) ? opt.icon : bufferIcon);
45154             bwidth = updateButtons(opt.buttons);
45155             progressBar.setVisible(opt.progress === true || opt.wait === true);
45156             this.updateProgress(0, opt.progressText);
45157             this.updateText(opt.msg);
45158             if(opt.cls){
45159                 d.el.addClass(opt.cls);
45160             }
45161             d.proxyDrag = opt.proxyDrag === true;
45162             d.modal = opt.modal !== false;
45163             d.mask = opt.modal !== false ? mask : false;
45164             if(!d.isVisible()){
45165                 // force it to the end of the z-index stack so it gets a cursor in FF
45166                 document.body.appendChild(dlg.el.dom);
45167                 d.setAnimateTarget(opt.animEl);
45168                 //workaround for window internally enabling keymap in afterShow
45169                 d.on('show', function(){
45170                     if(allowClose === true){
45171                         d.keyMap.enable();
45172                     }else{
45173                         d.keyMap.disable();
45174                     }
45175                 }, this, {single:true});
45176                 d.show(opt.animEl);
45177             }
45178             if(opt.wait === true){
45179                 progressBar.wait(opt.waitConfig);
45180             }
45181             return this;
45182         },
45183
45184         /**
45185          * Adds the specified icon to the dialog.  By default, the class 'ext-mb-icon' is applied for default
45186          * styling, and the class passed in is expected to supply the background image url. Pass in empty string ('')
45187          * to clear any existing icon. This method must be called before the MessageBox is shown.
45188          * The following built-in icon classes are supported, but you can also pass in a custom class name:
45189          * <pre>
45190 Ext.MessageBox.INFO
45191 Ext.MessageBox.WARNING
45192 Ext.MessageBox.QUESTION
45193 Ext.MessageBox.ERROR
45194          *</pre>
45195          * @param {String} icon A CSS classname specifying the icon's background image url, or empty string to clear the icon
45196          * @return {Ext.MessageBox} this
45197          */
45198         setIcon : function(icon){
45199             if(!dlg){
45200                 bufferIcon = icon;
45201                 return;
45202             }
45203             bufferIcon = undefined;
45204             if(icon && icon != ''){
45205                 iconEl.removeClass('x-hidden');
45206                 iconEl.replaceClass(iconCls, icon);
45207                 bodyEl.addClass('x-dlg-icon');
45208                 iconCls = icon;
45209             }else{
45210                 iconEl.replaceClass(iconCls, 'x-hidden');
45211                 bodyEl.removeClass('x-dlg-icon');
45212                 iconCls = '';
45213             }
45214             return this;
45215         },
45216
45217         /**
45218          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
45219          * the user.  You are responsible for updating the progress bar as needed via {@link Ext.MessageBox#updateProgress}
45220          * and closing the message box when the process is complete.
45221          * @param {String} title The title bar text
45222          * @param {String} msg The message box body text
45223          * @param {String} progressText (optional) The text to display inside the progress bar (defaults to '')
45224          * @return {Ext.MessageBox} this
45225          */
45226         progress : function(title, msg, progressText){
45227             this.show({
45228                 title : title,
45229                 msg : msg,
45230                 buttons: false,
45231                 progress:true,
45232                 closable:false,
45233                 minWidth: this.minProgressWidth,
45234                 progressText: progressText
45235             });
45236             return this;
45237         },
45238
45239         /**
45240          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
45241          * interaction while waiting for a long-running process to complete that does not have defined intervals.
45242          * You are responsible for closing the message box when the process is complete.
45243          * @param {String} msg The message box body text
45244          * @param {String} title (optional) The title bar text
45245          * @param {Object} config (optional) A {@link Ext.ProgressBar#waitConfig} object
45246          * @return {Ext.MessageBox} this
45247          */
45248         wait : function(msg, title, config){
45249             this.show({
45250                 title : title,
45251                 msg : msg,
45252                 buttons: false,
45253                 closable:false,
45254                 wait:true,
45255                 modal:true,
45256                 minWidth: this.minProgressWidth,
45257                 waitConfig: config
45258             });
45259             return this;
45260         },
45261
45262         /**
45263          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript alert prompt).
45264          * If a callback function is passed it will be called after the user clicks the button, and the
45265          * id of the button that was clicked will be passed as the only parameter to the callback
45266          * (could also be the top-right close button).
45267          * @param {String} title The title bar text
45268          * @param {String} msg The message box body text
45269          * @param {Function} fn (optional) The callback function invoked after the message box is closed
45270          * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to the browser wnidow.
45271          * @return {Ext.MessageBox} this
45272          */
45273         alert : function(title, msg, fn, scope){
45274             this.show({
45275                 title : title,
45276                 msg : msg,
45277                 buttons: this.OK,
45278                 fn: fn,
45279                 scope : scope,
45280                 minWidth: this.minWidth
45281             });
45282             return this;
45283         },
45284
45285         /**
45286          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's confirm).
45287          * If a callback function is passed it will be called after the user clicks either button,
45288          * and the id of the button that was clicked will be passed as the only parameter to the callback
45289          * (could also be the top-right close button).
45290          * @param {String} title The title bar text
45291          * @param {String} msg The message box body text
45292          * @param {Function} fn (optional) The callback function invoked after the message box is closed
45293          * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to the browser wnidow.
45294          * @return {Ext.MessageBox} this
45295          */
45296         confirm : function(title, msg, fn, scope){
45297             this.show({
45298                 title : title,
45299                 msg : msg,
45300                 buttons: this.YESNO,
45301                 fn: fn,
45302                 scope : scope,
45303                 icon: this.QUESTION,
45304                 minWidth: this.minWidth
45305             });
45306             return this;
45307         },
45308
45309         /**
45310          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to JavaScript's prompt).
45311          * The prompt can be a single-line or multi-line textbox.  If a callback function is passed it will be called after the user
45312          * clicks either button, and the id of the button that was clicked (could also be the top-right
45313          * close button) and the text that was entered will be passed as the two parameters to the callback.
45314          * @param {String} title The title bar text
45315          * @param {String} msg The message box body text
45316          * @param {Function} fn (optional) The callback function invoked after the message box is closed
45317          * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to the browser wnidow.
45318          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
45319          * property, or the height in pixels to create the textbox (defaults to false / single-line)
45320          * @param {String} value (optional) Default value of the text input element (defaults to '')
45321          * @return {Ext.MessageBox} this
45322          */
45323         prompt : function(title, msg, fn, scope, multiline, value){
45324             this.show({
45325                 title : title,
45326                 msg : msg,
45327                 buttons: this.OKCANCEL,
45328                 fn: fn,
45329                 minWidth: this.minPromptWidth,
45330                 scope : scope,
45331                 prompt:true,
45332                 multiline: multiline,
45333                 value: value
45334             });
45335             return this;
45336         },
45337
45338         /**
45339          * Button config that displays a single OK button
45340          * @type Object
45341          */
45342         OK : {ok:true},
45343         /**
45344          * Button config that displays a single Cancel button
45345          * @type Object
45346          */
45347         CANCEL : {cancel:true},
45348         /**
45349          * Button config that displays OK and Cancel buttons
45350          * @type Object
45351          */
45352         OKCANCEL : {ok:true, cancel:true},
45353         /**
45354          * Button config that displays Yes and No buttons
45355          * @type Object
45356          */
45357         YESNO : {yes:true, no:true},
45358         /**
45359          * Button config that displays Yes, No and Cancel buttons
45360          * @type Object
45361          */
45362         YESNOCANCEL : {yes:true, no:true, cancel:true},
45363         /**
45364          * The CSS class that provides the INFO icon image
45365          * @type String
45366          */
45367         INFO : 'ext-mb-info',
45368         /**
45369          * The CSS class that provides the WARNING icon image
45370          * @type String
45371          */
45372         WARNING : 'ext-mb-warning',
45373         /**
45374          * The CSS class that provides the QUESTION icon image
45375          * @type String
45376          */
45377         QUESTION : 'ext-mb-question',
45378         /**
45379          * The CSS class that provides the ERROR icon image
45380          * @type String
45381          */
45382         ERROR : 'ext-mb-error',
45383
45384         /**
45385          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
45386          * @type Number
45387          */
45388         defaultTextHeight : 75,
45389         /**
45390          * The maximum width in pixels of the message box (defaults to 600)
45391          * @type Number
45392          */
45393         maxWidth : 600,
45394         /**
45395          * The minimum width in pixels of the message box (defaults to 100)
45396          * @type Number
45397          */
45398         minWidth : 100,
45399         /**
45400          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
45401          * for setting a different minimum width than text-only dialogs may need (defaults to 250).
45402          * @type Number
45403          */
45404         minProgressWidth : 250,
45405         /**
45406          * The minimum width in pixels of the message box if it is a prompt dialog.  This is useful
45407          * for setting a different minimum width than text-only dialogs may need (defaults to 250).
45408          * @type Number
45409          */
45410         minPromptWidth: 250,
45411         /**
45412          * An object containing the default button text strings that can be overriden for localized language support.
45413          * Supported properties are: ok, cancel, yes and no.  Generally you should include a locale-specific
45414          * resource file for handling language support across the framework.
45415          * Customize the default text like so: Ext.MessageBox.buttonText.yes = "oui"; //french
45416          * @type Object
45417          */
45418         buttonText : {
45419             ok : "OK",
45420             cancel : "Cancel",
45421             yes : "Yes",
45422             no : "No"
45423         }
45424     };
45425 }();
45426
45427 /**
45428  * Shorthand for {@link Ext.MessageBox}
45429  */
45430 Ext.Msg = Ext.MessageBox;/**
45431  * @class Ext.dd.PanelProxy
45432  * A custom drag proxy implementation specific to {@link Ext.Panel}s. This class is primarily used internally
45433  * for the Panel's drag drop implementation, and should never need to be created directly.
45434  * @constructor
45435  * @param panel The {@link Ext.Panel} to proxy for
45436  * @param config Configuration options
45437  */
45438 Ext.dd.PanelProxy  = Ext.extend(Object, {
45439     
45440     constructor : function(panel, config){
45441         this.panel = panel;
45442         this.id = this.panel.id +'-ddproxy';
45443         Ext.apply(this, config);        
45444     },
45445     
45446     /**
45447      * @cfg {Boolean} insertProxy True to insert a placeholder proxy element while dragging the panel,
45448      * false to drag with no proxy (defaults to true).
45449      */
45450     insertProxy : true,
45451
45452     // private overrides
45453     setStatus : Ext.emptyFn,
45454     reset : Ext.emptyFn,
45455     update : Ext.emptyFn,
45456     stop : Ext.emptyFn,
45457     sync: Ext.emptyFn,
45458
45459     /**
45460      * Gets the proxy's element
45461      * @return {Element} The proxy's element
45462      */
45463     getEl : function(){
45464         return this.ghost;
45465     },
45466
45467     /**
45468      * Gets the proxy's ghost element
45469      * @return {Element} The proxy's ghost element
45470      */
45471     getGhost : function(){
45472         return this.ghost;
45473     },
45474
45475     /**
45476      * Gets the proxy's element
45477      * @return {Element} The proxy's element
45478      */
45479     getProxy : function(){
45480         return this.proxy;
45481     },
45482
45483     /**
45484      * Hides the proxy
45485      */
45486     hide : function(){
45487         if(this.ghost){
45488             if(this.proxy){
45489                 this.proxy.remove();
45490                 delete this.proxy;
45491             }
45492             this.panel.el.dom.style.display = '';
45493             this.ghost.remove();
45494             delete this.ghost;
45495         }
45496     },
45497
45498     /**
45499      * Shows the proxy
45500      */
45501     show : function(){
45502         if(!this.ghost){
45503             this.ghost = this.panel.createGhost(this.panel.initialConfig.cls, undefined, Ext.getBody());
45504             this.ghost.setXY(this.panel.el.getXY());
45505             if(this.insertProxy){
45506                 this.proxy = this.panel.el.insertSibling({cls:'x-panel-dd-spacer'});
45507                 this.proxy.setSize(this.panel.getSize());
45508             }
45509             this.panel.el.dom.style.display = 'none';
45510         }
45511     },
45512
45513     // private
45514     repair : function(xy, callback, scope){
45515         this.hide();
45516         if(typeof callback == "function"){
45517             callback.call(scope || this);
45518         }
45519     },
45520
45521     /**
45522      * Moves the proxy to a different position in the DOM.  This is typically called while dragging the Panel
45523      * to keep the proxy sync'd to the Panel's location.
45524      * @param {HTMLElement} parentNode The proxy's parent DOM node
45525      * @param {HTMLElement} before (optional) The sibling node before which the proxy should be inserted (defaults
45526      * to the parent's last child if not specified)
45527      */
45528     moveProxy : function(parentNode, before){
45529         if(this.proxy){
45530             parentNode.insertBefore(this.proxy.dom, before);
45531         }
45532     }
45533 });
45534
45535 // private - DD implementation for Panels
45536 Ext.Panel.DD = Ext.extend(Ext.dd.DragSource, {
45537     
45538     constructor : function(panel, cfg){
45539         this.panel = panel;
45540         this.dragData = {panel: panel};
45541         this.proxy = new Ext.dd.PanelProxy(panel, cfg);
45542         Ext.Panel.DD.superclass.constructor.call(this, panel.el, cfg);
45543         var h = panel.header,
45544             el = panel.body;
45545         if(h){
45546             this.setHandleElId(h.id);
45547             el = panel.header;
45548         }
45549         el.setStyle('cursor', 'move');
45550         this.scroll = false;        
45551     },
45552     
45553     showFrame: Ext.emptyFn,
45554     startDrag: Ext.emptyFn,
45555     b4StartDrag: function(x, y) {
45556         this.proxy.show();
45557     },
45558     b4MouseDown: function(e) {
45559         var x = e.getPageX(),
45560             y = e.getPageY();
45561         this.autoOffset(x, y);
45562     },
45563     onInitDrag : function(x, y){
45564         this.onStartDrag(x, y);
45565         return true;
45566     },
45567     createFrame : Ext.emptyFn,
45568     getDragEl : function(e){
45569         return this.proxy.ghost.dom;
45570     },
45571     endDrag : function(e){
45572         this.proxy.hide();
45573         this.panel.saveState();
45574     },
45575
45576     autoOffset : function(x, y) {
45577         x -= this.startPageX;
45578         y -= this.startPageY;
45579         this.setDelta(x, y);
45580     }
45581 });/**
45582  * @class Ext.state.Provider
45583  * Abstract base class for state provider implementations. This class provides methods
45584  * for encoding and decoding <b>typed</b> variables including dates and defines the
45585  * Provider interface.
45586  */
45587 Ext.state.Provider = Ext.extend(Ext.util.Observable, {
45588     
45589     constructor : function(){
45590         /**
45591          * @event statechange
45592          * Fires when a state change occurs.
45593          * @param {Provider} this This state provider
45594          * @param {String} key The state key which was changed
45595          * @param {String} value The encoded value for the state
45596          */
45597         this.addEvents("statechange");
45598         this.state = {};
45599         Ext.state.Provider.superclass.constructor.call(this);
45600     },
45601     
45602     /**
45603      * Returns the current value for a key
45604      * @param {String} name The key name
45605      * @param {Mixed} defaultValue A default value to return if the key's value is not found
45606      * @return {Mixed} The state data
45607      */
45608     get : function(name, defaultValue){
45609         return typeof this.state[name] == "undefined" ?
45610             defaultValue : this.state[name];
45611     },
45612
45613     /**
45614      * Clears a value from the state
45615      * @param {String} name The key name
45616      */
45617     clear : function(name){
45618         delete this.state[name];
45619         this.fireEvent("statechange", this, name, null);
45620     },
45621
45622     /**
45623      * Sets the value for a key
45624      * @param {String} name The key name
45625      * @param {Mixed} value The value to set
45626      */
45627     set : function(name, value){
45628         this.state[name] = value;
45629         this.fireEvent("statechange", this, name, value);
45630     },
45631
45632     /**
45633      * Decodes a string previously encoded with {@link #encodeValue}.
45634      * @param {String} value The value to decode
45635      * @return {Mixed} The decoded value
45636      */
45637     decodeValue : function(cookie){
45638         /**
45639          * a -> Array
45640          * n -> Number
45641          * d -> Date
45642          * b -> Boolean
45643          * s -> String
45644          * o -> Object
45645          * -> Empty (null)
45646          */
45647         var re = /^(a|n|d|b|s|o|e)\:(.*)$/,
45648             matches = re.exec(unescape(cookie)),
45649             all,
45650             type,
45651             v,
45652             kv;
45653         if(!matches || !matches[1]){
45654             return; // non state cookie
45655         }
45656         type = matches[1];
45657         v = matches[2];
45658         switch(type){
45659             case 'e':
45660                 return null;
45661             case 'n':
45662                 return parseFloat(v);
45663             case 'd':
45664                 return new Date(Date.parse(v));
45665             case 'b':
45666                 return (v == '1');
45667             case 'a':
45668                 all = [];
45669                 if(v != ''){
45670                     Ext.each(v.split('^'), function(val){
45671                         all.push(this.decodeValue(val));
45672                     }, this);
45673                 }
45674                 return all;
45675            case 'o':
45676                 all = {};
45677                 if(v != ''){
45678                     Ext.each(v.split('^'), function(val){
45679                         kv = val.split('=');
45680                         all[kv[0]] = this.decodeValue(kv[1]);
45681                     }, this);
45682                 }
45683                 return all;
45684            default:
45685                 return v;
45686         }
45687     },
45688
45689     /**
45690      * Encodes a value including type information.  Decode with {@link #decodeValue}.
45691      * @param {Mixed} value The value to encode
45692      * @return {String} The encoded value
45693      */
45694     encodeValue : function(v){
45695         var enc,
45696             flat = '',
45697             i = 0,
45698             len,
45699             key;
45700         if(v == null){
45701             return 'e:1';    
45702         }else if(typeof v == 'number'){
45703             enc = 'n:' + v;
45704         }else if(typeof v == 'boolean'){
45705             enc = 'b:' + (v ? '1' : '0');
45706         }else if(Ext.isDate(v)){
45707             enc = 'd:' + v.toGMTString();
45708         }else if(Ext.isArray(v)){
45709             for(len = v.length; i < len; i++){
45710                 flat += this.encodeValue(v[i]);
45711                 if(i != len - 1){
45712                     flat += '^';
45713                 }
45714             }
45715             enc = 'a:' + flat;
45716         }else if(typeof v == 'object'){
45717             for(key in v){
45718                 if(typeof v[key] != 'function' && v[key] !== undefined){
45719                     flat += key + '=' + this.encodeValue(v[key]) + '^';
45720                 }
45721             }
45722             enc = 'o:' + flat.substring(0, flat.length-1);
45723         }else{
45724             enc = 's:' + v;
45725         }
45726         return escape(enc);
45727     }
45728 });
45729 /**
45730  * @class Ext.state.Manager
45731  * This is the global state manager. By default all components that are "state aware" check this class
45732  * for state information if you don't pass them a custom state provider. In order for this class
45733  * to be useful, it must be initialized with a provider when your application initializes. Example usage:
45734  <pre><code>
45735 // in your initialization function
45736 init : function(){
45737    Ext.state.Manager.setProvider(new Ext.state.CookieProvider());
45738    var win = new Window(...);
45739    win.restoreState();
45740 }
45741  </code></pre>
45742  * @singleton
45743  */
45744 Ext.state.Manager = function(){
45745     var provider = new Ext.state.Provider();
45746
45747     return {
45748         /**
45749          * Configures the default state provider for your application
45750          * @param {Provider} stateProvider The state provider to set
45751          */
45752         setProvider : function(stateProvider){
45753             provider = stateProvider;
45754         },
45755
45756         /**
45757          * Returns the current value for a key
45758          * @param {String} name The key name
45759          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
45760          * @return {Mixed} The state data
45761          */
45762         get : function(key, defaultValue){
45763             return provider.get(key, defaultValue);
45764         },
45765
45766         /**
45767          * Sets the value for a key
45768          * @param {String} name The key name
45769          * @param {Mixed} value The state data
45770          */
45771          set : function(key, value){
45772             provider.set(key, value);
45773         },
45774
45775         /**
45776          * Clears a value from the state
45777          * @param {String} name The key name
45778          */
45779         clear : function(key){
45780             provider.clear(key);
45781         },
45782
45783         /**
45784          * Gets the currently configured state provider
45785          * @return {Provider} The state provider
45786          */
45787         getProvider : function(){
45788             return provider;
45789         }
45790     };
45791 }();
45792 /**
45793  * @class Ext.state.CookieProvider
45794  * @extends Ext.state.Provider
45795  * The default Provider implementation which saves state via cookies.
45796  * <br />Usage:
45797  <pre><code>
45798    var cp = new Ext.state.CookieProvider({
45799        path: "/cgi-bin/",
45800        expires: new Date(new Date().getTime()+(1000*60*60*24*30)), //30 days
45801        domain: "extjs.com"
45802    });
45803    Ext.state.Manager.setProvider(cp);
45804  </code></pre>
45805  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
45806  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
45807  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
45808  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'extjs.com' to include
45809  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
45810  * domain the page is running on including the 'www' like 'www.extjs.com')
45811  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
45812  * @constructor
45813  * Create a new CookieProvider
45814  * @param {Object} config The configuration object
45815  */
45816 Ext.state.CookieProvider = Ext.extend(Ext.state.Provider, {
45817     
45818     constructor : function(config){
45819         Ext.state.CookieProvider.superclass.constructor.call(this);
45820         this.path = "/";
45821         this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
45822         this.domain = null;
45823         this.secure = false;
45824         Ext.apply(this, config);
45825         this.state = this.readCookies();
45826     },
45827     
45828     // private
45829     set : function(name, value){
45830         if(typeof value == "undefined" || value === null){
45831             this.clear(name);
45832             return;
45833         }
45834         this.setCookie(name, value);
45835         Ext.state.CookieProvider.superclass.set.call(this, name, value);
45836     },
45837
45838     // private
45839     clear : function(name){
45840         this.clearCookie(name);
45841         Ext.state.CookieProvider.superclass.clear.call(this, name);
45842     },
45843
45844     // private
45845     readCookies : function(){
45846         var cookies = {},
45847             c = document.cookie + ";",
45848             re = /\s?(.*?)=(.*?);/g,
45849             matches,
45850             name,
45851             value;
45852         while((matches = re.exec(c)) != null){
45853             name = matches[1];
45854             value = matches[2];
45855             if(name && name.substring(0,3) == "ys-"){
45856                 cookies[name.substr(3)] = this.decodeValue(value);
45857             }
45858         }
45859         return cookies;
45860     },
45861
45862     // private
45863     setCookie : function(name, value){
45864         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
45865            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
45866            ((this.path == null) ? "" : ("; path=" + this.path)) +
45867            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
45868            ((this.secure == true) ? "; secure" : "");
45869     },
45870
45871     // private
45872     clearCookie : function(name){
45873         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
45874            ((this.path == null) ? "" : ("; path=" + this.path)) +
45875            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
45876            ((this.secure == true) ? "; secure" : "");
45877     }
45878 });/**
45879  * @class Ext.DataView
45880  * @extends Ext.BoxComponent
45881  * A mechanism for displaying data using custom layout templates and formatting. DataView uses an {@link Ext.XTemplate}
45882  * as its internal templating mechanism, and is bound to an {@link Ext.data.Store}
45883  * so that as the data in the store changes the view is automatically updated to reflect the changes.  The view also
45884  * provides built-in behavior for many common events that can occur for its contained items including click, doubleclick,
45885  * mouseover, mouseout, etc. as well as a built-in selection model. <b>In order to use these features, an {@link #itemSelector}
45886  * config must be provided for the DataView to determine what nodes it will be working with.</b>
45887  *
45888  * <p>The example below binds a DataView to a {@link Ext.data.Store} and renders it into an {@link Ext.Panel}.</p>
45889  * <pre><code>
45890 var store = new Ext.data.JsonStore({
45891     url: 'get-images.php',
45892     root: 'images',
45893     fields: [
45894         'name', 'url',
45895         {name:'size', type: 'float'},
45896         {name:'lastmod', type:'date', dateFormat:'timestamp'}
45897     ]
45898 });
45899 store.load();
45900
45901 var tpl = new Ext.XTemplate(
45902     '&lt;tpl for="."&gt;',
45903         '&lt;div class="thumb-wrap" id="{name}"&gt;',
45904         '&lt;div class="thumb"&gt;&lt;img src="{url}" title="{name}"&gt;&lt;/div&gt;',
45905         '&lt;span class="x-editable"&gt;{shortName}&lt;/span&gt;&lt;/div&gt;',
45906     '&lt;/tpl&gt;',
45907     '&lt;div class="x-clear"&gt;&lt;/div&gt;'
45908 );
45909
45910 var panel = new Ext.Panel({
45911     id:'images-view',
45912     frame:true,
45913     width:535,
45914     autoHeight:true,
45915     collapsible:true,
45916     layout:'fit',
45917     title:'Simple DataView',
45918
45919     items: new Ext.DataView({
45920         store: store,
45921         tpl: tpl,
45922         autoHeight:true,
45923         multiSelect: true,
45924         overClass:'x-view-over',
45925         itemSelector:'div.thumb-wrap',
45926         emptyText: 'No images to display'
45927     })
45928 });
45929 panel.render(document.body);
45930 </code></pre>
45931  * @constructor
45932  * Create a new DataView
45933  * @param {Object} config The config object
45934  * @xtype dataview
45935  */
45936 Ext.DataView = Ext.extend(Ext.BoxComponent, {
45937     /**
45938      * @cfg {String/Array} tpl
45939      * The HTML fragment or an array of fragments that will make up the template used by this DataView.  This should
45940      * be specified in the same format expected by the constructor of {@link Ext.XTemplate}.
45941      */
45942     /**
45943      * @cfg {Ext.data.Store} store
45944      * The {@link Ext.data.Store} to bind this DataView to.
45945      */
45946     /**
45947      * @cfg {String} itemSelector
45948      * <b>This is a required setting</b>. A simple CSS selector (e.g. <tt>div.some-class</tt> or 
45949      * <tt>span:first-child</tt>) that will be used to determine what nodes this DataView will be
45950      * working with.
45951      */
45952     /**
45953      * @cfg {Boolean} multiSelect
45954      * True to allow selection of more than one item at a time, false to allow selection of only a single item
45955      * at a time or no selection at all, depending on the value of {@link #singleSelect} (defaults to false).
45956      */
45957     /**
45958      * @cfg {Boolean} singleSelect
45959      * True to allow selection of exactly one item at a time, false to allow no selection at all (defaults to false).
45960      * Note that if {@link #multiSelect} = true, this value will be ignored.
45961      */
45962     /**
45963      * @cfg {Boolean} simpleSelect
45964      * True to enable multiselection by clicking on multiple items without requiring the user to hold Shift or Ctrl,
45965      * false to force the user to hold Ctrl or Shift to select more than on item (defaults to false).
45966      */
45967     /**
45968      * @cfg {String} overClass
45969      * A CSS class to apply to each item in the view on mouseover (defaults to undefined).
45970      */
45971     /**
45972      * @cfg {String} loadingText
45973      * A string to display during data load operations (defaults to undefined).  If specified, this text will be
45974      * displayed in a loading div and the view's contents will be cleared while loading, otherwise the view's
45975      * contents will continue to display normally until the new data is loaded and the contents are replaced.
45976      */
45977     /**
45978      * @cfg {String} selectedClass
45979      * A CSS class to apply to each selected item in the view (defaults to 'x-view-selected').
45980      */
45981     selectedClass : "x-view-selected",
45982     /**
45983      * @cfg {String} emptyText
45984      * The text to display in the view when there is no data to display (defaults to '').
45985      */
45986     emptyText : "",
45987
45988     /**
45989      * @cfg {Boolean} deferEmptyText True to defer emptyText being applied until the store's first load
45990      */
45991     deferEmptyText: true,
45992     /**
45993      * @cfg {Boolean} trackOver True to enable mouseenter and mouseleave events
45994      */
45995     trackOver: false,
45996     
45997     /**
45998      * @cfg {Boolean} blockRefresh Set this to true to ignore datachanged events on the bound store. This is useful if
45999      * you wish to provide custom transition animations via a plugin (defaults to false)
46000      */
46001     blockRefresh: false,
46002
46003     //private
46004     last: false,
46005
46006     // private
46007     initComponent : function(){
46008         Ext.DataView.superclass.initComponent.call(this);
46009         if(Ext.isString(this.tpl) || Ext.isArray(this.tpl)){
46010             this.tpl = new Ext.XTemplate(this.tpl);
46011         }
46012
46013         this.addEvents(
46014             /**
46015              * @event beforeclick
46016              * Fires before a click is processed. Returns false to cancel the default action.
46017              * @param {Ext.DataView} this
46018              * @param {Number} index The index of the target node
46019              * @param {HTMLElement} node The target node
46020              * @param {Ext.EventObject} e The raw event object
46021              */
46022             "beforeclick",
46023             /**
46024              * @event click
46025              * Fires when a template node is clicked.
46026              * @param {Ext.DataView} this
46027              * @param {Number} index The index of the target node
46028              * @param {HTMLElement} node The target node
46029              * @param {Ext.EventObject} e The raw event object
46030              */
46031             "click",
46032             /**
46033              * @event mouseenter
46034              * Fires when the mouse enters a template node. trackOver:true or an overClass must be set to enable this event.
46035              * @param {Ext.DataView} this
46036              * @param {Number} index The index of the target node
46037              * @param {HTMLElement} node The target node
46038              * @param {Ext.EventObject} e The raw event object
46039              */
46040             "mouseenter",
46041             /**
46042              * @event mouseleave
46043              * Fires when the mouse leaves a template node. trackOver:true or an overClass must be set to enable this event.
46044              * @param {Ext.DataView} this
46045              * @param {Number} index The index of the target node
46046              * @param {HTMLElement} node The target node
46047              * @param {Ext.EventObject} e The raw event object
46048              */
46049             "mouseleave",
46050             /**
46051              * @event containerclick
46052              * Fires when a click occurs and it is not on a template node.
46053              * @param {Ext.DataView} this
46054              * @param {Ext.EventObject} e The raw event object
46055              */
46056             "containerclick",
46057             /**
46058              * @event dblclick
46059              * Fires when a template node is double clicked.
46060              * @param {Ext.DataView} this
46061              * @param {Number} index The index of the target node
46062              * @param {HTMLElement} node The target node
46063              * @param {Ext.EventObject} e The raw event object
46064              */
46065             "dblclick",
46066             /**
46067              * @event contextmenu
46068              * Fires when a template node is right clicked.
46069              * @param {Ext.DataView} this
46070              * @param {Number} index The index of the target node
46071              * @param {HTMLElement} node The target node
46072              * @param {Ext.EventObject} e The raw event object
46073              */
46074             "contextmenu",
46075             /**
46076              * @event containercontextmenu
46077              * Fires when a right click occurs that is not on a template node.
46078              * @param {Ext.DataView} this
46079              * @param {Ext.EventObject} e The raw event object
46080              */
46081             "containercontextmenu",
46082             /**
46083              * @event selectionchange
46084              * Fires when the selected nodes change.
46085              * @param {Ext.DataView} this
46086              * @param {Array} selections Array of the selected nodes
46087              */
46088             "selectionchange",
46089
46090             /**
46091              * @event beforeselect
46092              * Fires before a selection is made. If any handlers return false, the selection is cancelled.
46093              * @param {Ext.DataView} this
46094              * @param {HTMLElement} node The node to be selected
46095              * @param {Array} selections Array of currently selected nodes
46096              */
46097             "beforeselect"
46098         );
46099
46100         this.store = Ext.StoreMgr.lookup(this.store);
46101         this.all = new Ext.CompositeElementLite();
46102         this.selected = new Ext.CompositeElementLite();
46103     },
46104
46105     // private
46106     afterRender : function(){
46107         Ext.DataView.superclass.afterRender.call(this);
46108
46109                 this.mon(this.getTemplateTarget(), {
46110             "click": this.onClick,
46111             "dblclick": this.onDblClick,
46112             "contextmenu": this.onContextMenu,
46113             scope:this
46114         });
46115
46116         if(this.overClass || this.trackOver){
46117             this.mon(this.getTemplateTarget(), {
46118                 "mouseover": this.onMouseOver,
46119                 "mouseout": this.onMouseOut,
46120                 scope:this
46121             });
46122         }
46123
46124         if(this.store){
46125             this.bindStore(this.store, true);
46126         }
46127     },
46128
46129     /**
46130      * Refreshes the view by reloading the data from the store and re-rendering the template.
46131      */
46132     refresh : function() {
46133         this.clearSelections(false, true);
46134         var el = this.getTemplateTarget(),
46135             records = this.store.getRange();
46136             
46137         el.update('');
46138         if(records.length < 1){
46139             if(!this.deferEmptyText || this.hasSkippedEmptyText){
46140                 el.update(this.emptyText);
46141             }
46142             this.all.clear();
46143         }else{
46144             this.tpl.overwrite(el, this.collectData(records, 0));
46145             this.all.fill(Ext.query(this.itemSelector, el.dom));
46146             this.updateIndexes(0);
46147         }
46148         this.hasSkippedEmptyText = true;
46149     },
46150
46151     getTemplateTarget: function(){
46152         return this.el;
46153     },
46154
46155     /**
46156      * Function which can be overridden to provide custom formatting for each Record that is used by this
46157      * DataView's {@link #tpl template} to render each node.
46158      * @param {Array/Object} data The raw data object that was used to create the Record.
46159      * @param {Number} recordIndex the index number of the Record being prepared for rendering.
46160      * @param {Record} record The Record being prepared for rendering.
46161      * @return {Array/Object} The formatted data in a format expected by the internal {@link #tpl template}'s overwrite() method.
46162      * (either an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'}))
46163      */
46164     prepareData : function(data){
46165         return data;
46166     },
46167
46168     /**
46169      * <p>Function which can be overridden which returns the data object passed to this
46170      * DataView's {@link #tpl template} to render the whole DataView.</p>
46171      * <p>This is usually an Array of data objects, each element of which is processed by an
46172      * {@link Ext.XTemplate XTemplate} which uses <tt>'&lt;tpl for="."&gt;'</tt> to iterate over its supplied
46173      * data object as an Array. However, <i>named</i> properties may be placed into the data object to
46174      * provide non-repeating data such as headings, totals etc.</p>
46175      * @param {Array} records An Array of {@link Ext.data.Record}s to be rendered into the DataView.
46176      * @param {Number} startIndex the index number of the Record being prepared for rendering.
46177      * @return {Array} An Array of data objects to be processed by a repeating XTemplate. May also
46178      * contain <i>named</i> properties.
46179      */
46180     collectData : function(records, startIndex){
46181         var r = [],
46182             i = 0,
46183             len = records.length;
46184         for(; i < len; i++){
46185             r[r.length] = this.prepareData(records[i].data, startIndex + i, records[i]);
46186         }
46187         return r;
46188     },
46189
46190     // private
46191     bufferRender : function(records, index){
46192         var div = document.createElement('div');
46193         this.tpl.overwrite(div, this.collectData(records, index));
46194         return Ext.query(this.itemSelector, div);
46195     },
46196
46197     // private
46198     onUpdate : function(ds, record){
46199         var index = this.store.indexOf(record);
46200         if(index > -1){
46201             var sel = this.isSelected(index),
46202                 original = this.all.elements[index],
46203                 node = this.bufferRender([record], index)[0];
46204
46205             this.all.replaceElement(index, node, true);
46206             if(sel){
46207                 this.selected.replaceElement(original, node);
46208                 this.all.item(index).addClass(this.selectedClass);
46209             }
46210             this.updateIndexes(index, index);
46211         }
46212     },
46213
46214     // private
46215     onAdd : function(ds, records, index){
46216         if(this.all.getCount() === 0){
46217             this.refresh();
46218             return;
46219         }
46220         var nodes = this.bufferRender(records, index), n, a = this.all.elements;
46221         if(index < this.all.getCount()){
46222             n = this.all.item(index).insertSibling(nodes, 'before', true);
46223             a.splice.apply(a, [index, 0].concat(nodes));
46224         }else{
46225             n = this.all.last().insertSibling(nodes, 'after', true);
46226             a.push.apply(a, nodes);
46227         }
46228         this.updateIndexes(index);
46229     },
46230
46231     // private
46232     onRemove : function(ds, record, index){
46233         this.deselect(index);
46234         this.all.removeElement(index, true);
46235         this.updateIndexes(index);
46236         if (this.store.getCount() === 0){
46237             this.refresh();
46238         }
46239     },
46240
46241     /**
46242      * Refreshes an individual node's data from the store.
46243      * @param {Number} index The item's data index in the store
46244      */
46245     refreshNode : function(index){
46246         this.onUpdate(this.store, this.store.getAt(index));
46247     },
46248
46249     // private
46250     updateIndexes : function(startIndex, endIndex){
46251         var ns = this.all.elements;
46252         startIndex = startIndex || 0;
46253         endIndex = endIndex || ((endIndex === 0) ? 0 : (ns.length - 1));
46254         for(var i = startIndex; i <= endIndex; i++){
46255             ns[i].viewIndex = i;
46256         }
46257     },
46258     
46259     /**
46260      * Returns the store associated with this DataView.
46261      * @return {Ext.data.Store} The store
46262      */
46263     getStore : function(){
46264         return this.store;
46265     },
46266
46267     /**
46268      * Changes the data store bound to this view and refreshes it.
46269      * @param {Store} store The store to bind to this view
46270      */
46271     bindStore : function(store, initial){
46272         if(!initial && this.store){
46273             if(store !== this.store && this.store.autoDestroy){
46274                 this.store.destroy();
46275             }else{
46276                 this.store.un("beforeload", this.onBeforeLoad, this);
46277                 this.store.un("datachanged", this.onDataChanged, this);
46278                 this.store.un("add", this.onAdd, this);
46279                 this.store.un("remove", this.onRemove, this);
46280                 this.store.un("update", this.onUpdate, this);
46281                 this.store.un("clear", this.refresh, this);
46282             }
46283             if(!store){
46284                 this.store = null;
46285             }
46286         }
46287         if(store){
46288             store = Ext.StoreMgr.lookup(store);
46289             store.on({
46290                 scope: this,
46291                 beforeload: this.onBeforeLoad,
46292                 datachanged: this.onDataChanged,
46293                 add: this.onAdd,
46294                 remove: this.onRemove,
46295                 update: this.onUpdate,
46296                 clear: this.refresh
46297             });
46298         }
46299         this.store = store;
46300         if(store){
46301             this.refresh();
46302         }
46303     },
46304     
46305     /**
46306      * @private
46307      * Calls this.refresh if this.blockRefresh is not true
46308      */
46309     onDataChanged: function() {
46310         if (this.blockRefresh !== true) {
46311             this.refresh.apply(this, arguments);
46312         }
46313     },
46314
46315     /**
46316      * Returns the template node the passed child belongs to, or null if it doesn't belong to one.
46317      * @param {HTMLElement} node
46318      * @return {HTMLElement} The template node
46319      */
46320     findItemFromChild : function(node){
46321         return Ext.fly(node).findParent(this.itemSelector, this.getTemplateTarget());
46322     },
46323
46324     // private
46325     onClick : function(e){
46326         var item = e.getTarget(this.itemSelector, this.getTemplateTarget()),
46327             index;
46328         if(item){
46329             index = this.indexOf(item);
46330             if(this.onItemClick(item, index, e) !== false){
46331                 this.fireEvent("click", this, index, item, e);
46332             }
46333         }else{
46334             if(this.fireEvent("containerclick", this, e) !== false){
46335                 this.onContainerClick(e);
46336             }
46337         }
46338     },
46339
46340     onContainerClick : function(e){
46341         this.clearSelections();
46342     },
46343
46344     // private
46345     onContextMenu : function(e){
46346         var item = e.getTarget(this.itemSelector, this.getTemplateTarget());
46347         if(item){
46348             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
46349         }else{
46350             this.fireEvent("containercontextmenu", this, e);
46351         }
46352     },
46353
46354     // private
46355     onDblClick : function(e){
46356         var item = e.getTarget(this.itemSelector, this.getTemplateTarget());
46357         if(item){
46358             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
46359         }
46360     },
46361
46362     // private
46363     onMouseOver : function(e){
46364         var item = e.getTarget(this.itemSelector, this.getTemplateTarget());
46365         if(item && item !== this.lastItem){
46366             this.lastItem = item;
46367             Ext.fly(item).addClass(this.overClass);
46368             this.fireEvent("mouseenter", this, this.indexOf(item), item, e);
46369         }
46370     },
46371
46372     // private
46373     onMouseOut : function(e){
46374         if(this.lastItem){
46375             if(!e.within(this.lastItem, true, true)){
46376                 Ext.fly(this.lastItem).removeClass(this.overClass);
46377                 this.fireEvent("mouseleave", this, this.indexOf(this.lastItem), this.lastItem, e);
46378                 delete this.lastItem;
46379             }
46380         }
46381     },
46382
46383     // private
46384     onItemClick : function(item, index, e){
46385         if(this.fireEvent("beforeclick", this, index, item, e) === false){
46386             return false;
46387         }
46388         if(this.multiSelect){
46389             this.doMultiSelection(item, index, e);
46390             e.preventDefault();
46391         }else if(this.singleSelect){
46392             this.doSingleSelection(item, index, e);
46393             e.preventDefault();
46394         }
46395         return true;
46396     },
46397
46398     // private
46399     doSingleSelection : function(item, index, e){
46400         if(e.ctrlKey && this.isSelected(index)){
46401             this.deselect(index);
46402         }else{
46403             this.select(index, false);
46404         }
46405     },
46406
46407     // private
46408     doMultiSelection : function(item, index, e){
46409         if(e.shiftKey && this.last !== false){
46410             var last = this.last;
46411             this.selectRange(last, index, e.ctrlKey);
46412             this.last = last; // reset the last
46413         }else{
46414             if((e.ctrlKey||this.simpleSelect) && this.isSelected(index)){
46415                 this.deselect(index);
46416             }else{
46417                 this.select(index, e.ctrlKey || e.shiftKey || this.simpleSelect);
46418             }
46419         }
46420     },
46421
46422     /**
46423      * Gets the number of selected nodes.
46424      * @return {Number} The node count
46425      */
46426     getSelectionCount : function(){
46427         return this.selected.getCount();
46428     },
46429
46430     /**
46431      * Gets the currently selected nodes.
46432      * @return {Array} An array of HTMLElements
46433      */
46434     getSelectedNodes : function(){
46435         return this.selected.elements;
46436     },
46437
46438     /**
46439      * Gets the indexes of the selected nodes.
46440      * @return {Array} An array of numeric indexes
46441      */
46442     getSelectedIndexes : function(){
46443         var indexes = [], 
46444             selected = this.selected.elements,
46445             i = 0,
46446             len = selected.length;
46447             
46448         for(; i < len; i++){
46449             indexes.push(selected[i].viewIndex);
46450         }
46451         return indexes;
46452     },
46453
46454     /**
46455      * Gets an array of the selected records
46456      * @return {Array} An array of {@link Ext.data.Record} objects
46457      */
46458     getSelectedRecords : function(){
46459         return this.getRecords(this.selected.elements);
46460     },
46461
46462     /**
46463      * Gets an array of the records from an array of nodes
46464      * @param {Array} nodes The nodes to evaluate
46465      * @return {Array} records The {@link Ext.data.Record} objects
46466      */
46467     getRecords : function(nodes){
46468         var records = [], 
46469             i = 0,
46470             len = nodes.length;
46471             
46472         for(; i < len; i++){
46473             records[records.length] = this.store.getAt(nodes[i].viewIndex);
46474         }
46475         return records;
46476     },
46477
46478     /**
46479      * Gets a record from a node
46480      * @param {HTMLElement} node The node to evaluate
46481      * @return {Record} record The {@link Ext.data.Record} object
46482      */
46483     getRecord : function(node){
46484         return this.store.getAt(node.viewIndex);
46485     },
46486
46487     /**
46488      * Clears all selections.
46489      * @param {Boolean} suppressEvent (optional) True to skip firing of the selectionchange event
46490      */
46491     clearSelections : function(suppressEvent, skipUpdate){
46492         if((this.multiSelect || this.singleSelect) && this.selected.getCount() > 0){
46493             if(!skipUpdate){
46494                 this.selected.removeClass(this.selectedClass);
46495             }
46496             this.selected.clear();
46497             this.last = false;
46498             if(!suppressEvent){
46499                 this.fireEvent("selectionchange", this, this.selected.elements);
46500             }
46501         }
46502     },
46503
46504     /**
46505      * Returns true if the passed node is selected, else false.
46506      * @param {HTMLElement/Number/Ext.data.Record} node The node, node index or record to check
46507      * @return {Boolean} True if selected, else false
46508      */
46509     isSelected : function(node){
46510         return this.selected.contains(this.getNode(node));
46511     },
46512
46513     /**
46514      * Deselects a node.
46515      * @param {HTMLElement/Number/Record} node The node, node index or record to deselect
46516      */
46517     deselect : function(node){
46518         if(this.isSelected(node)){
46519             node = this.getNode(node);
46520             this.selected.removeElement(node);
46521             if(this.last == node.viewIndex){
46522                 this.last = false;
46523             }
46524             Ext.fly(node).removeClass(this.selectedClass);
46525             this.fireEvent("selectionchange", this, this.selected.elements);
46526         }
46527     },
46528
46529     /**
46530      * Selects a set of nodes.
46531      * @param {Array/HTMLElement/String/Number/Ext.data.Record} nodeInfo An HTMLElement template node, index of a template node,
46532      * id of a template node, record associated with a node or an array of any of those to select
46533      * @param {Boolean} keepExisting (optional) true to keep existing selections
46534      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
46535      */
46536     select : function(nodeInfo, keepExisting, suppressEvent){
46537         if(Ext.isArray(nodeInfo)){
46538             if(!keepExisting){
46539                 this.clearSelections(true);
46540             }
46541             for(var i = 0, len = nodeInfo.length; i < len; i++){
46542                 this.select(nodeInfo[i], true, true);
46543             }
46544             if(!suppressEvent){
46545                 this.fireEvent("selectionchange", this, this.selected.elements);
46546             }
46547         } else{
46548             var node = this.getNode(nodeInfo);
46549             if(!keepExisting){
46550                 this.clearSelections(true);
46551             }
46552             if(node && !this.isSelected(node)){
46553                 if(this.fireEvent("beforeselect", this, node, this.selected.elements) !== false){
46554                     Ext.fly(node).addClass(this.selectedClass);
46555                     this.selected.add(node);
46556                     this.last = node.viewIndex;
46557                     if(!suppressEvent){
46558                         this.fireEvent("selectionchange", this, this.selected.elements);
46559                     }
46560                 }
46561             }
46562         }
46563     },
46564
46565     /**
46566      * Selects a range of nodes. All nodes between start and end are selected.
46567      * @param {Number} start The index of the first node in the range
46568      * @param {Number} end The index of the last node in the range
46569      * @param {Boolean} keepExisting (optional) True to retain existing selections
46570      */
46571     selectRange : function(start, end, keepExisting){
46572         if(!keepExisting){
46573             this.clearSelections(true);
46574         }
46575         this.select(this.getNodes(start, end), true);
46576     },
46577
46578     /**
46579      * Gets a template node.
46580      * @param {HTMLElement/String/Number/Ext.data.Record} nodeInfo An HTMLElement template node, index of a template node, 
46581      * the id of a template node or the record associated with the node.
46582      * @return {HTMLElement} The node or null if it wasn't found
46583      */
46584     getNode : function(nodeInfo){
46585         if(Ext.isString(nodeInfo)){
46586             return document.getElementById(nodeInfo);
46587         }else if(Ext.isNumber(nodeInfo)){
46588             return this.all.elements[nodeInfo];
46589         }else if(nodeInfo instanceof Ext.data.Record){
46590             var idx = this.store.indexOf(nodeInfo);
46591             return this.all.elements[idx];
46592         }
46593         return nodeInfo;
46594     },
46595
46596     /**
46597      * Gets a range nodes.
46598      * @param {Number} start (optional) The index of the first node in the range
46599      * @param {Number} end (optional) The index of the last node in the range
46600      * @return {Array} An array of nodes
46601      */
46602     getNodes : function(start, end){
46603         var ns = this.all.elements,
46604             nodes = [],
46605             i;
46606             
46607         start = start || 0;
46608         end = !Ext.isDefined(end) ? Math.max(ns.length - 1, 0) : end;
46609         if(start <= end){
46610             for(i = start; i <= end && ns[i]; i++){
46611                 nodes.push(ns[i]);
46612             }
46613         } else{
46614             for(i = start; i >= end && ns[i]; i--){
46615                 nodes.push(ns[i]);
46616             }
46617         }
46618         return nodes;
46619     },
46620
46621     /**
46622      * Finds the index of the passed node.
46623      * @param {HTMLElement/String/Number/Record} nodeInfo An HTMLElement template node, index of a template node, the id of a template node
46624      * or a record associated with a node.
46625      * @return {Number} The index of the node or -1
46626      */
46627     indexOf : function(node){
46628         node = this.getNode(node);
46629         if(Ext.isNumber(node.viewIndex)){
46630             return node.viewIndex;
46631         }
46632         return this.all.indexOf(node);
46633     },
46634
46635     // private
46636     onBeforeLoad : function(){
46637         if(this.loadingText){
46638             this.clearSelections(false, true);
46639             this.getTemplateTarget().update('<div class="loading-indicator">'+this.loadingText+'</div>');
46640             this.all.clear();
46641         }
46642     },
46643
46644     onDestroy : function(){
46645         this.all.clear();
46646         this.selected.clear();
46647         Ext.DataView.superclass.onDestroy.call(this);
46648         this.bindStore(null);
46649     }
46650 });
46651
46652 /**
46653  * Changes the data store bound to this view and refreshes it. (deprecated in favor of bindStore)
46654  * @param {Store} store The store to bind to this view
46655  */
46656 Ext.DataView.prototype.setStore = Ext.DataView.prototype.bindStore;
46657
46658 Ext.reg('dataview', Ext.DataView);
46659 /**
46660  * @class Ext.list.ListView
46661  * @extends Ext.DataView
46662  * <p>Ext.list.ListView is a fast and light-weight implentation of a
46663  * {@link Ext.grid.GridPanel Grid} like view with the following characteristics:</p>
46664  * <div class="mdetail-params"><ul>
46665  * <li>resizable columns</li>
46666  * <li>selectable</li>
46667  * <li>column widths are initially proportioned by percentage based on the container
46668  * width and number of columns</li>
46669  * <li>uses templates to render the data in any required format</li>
46670  * <li>no horizontal scrolling</li>
46671  * <li>no editing</li>
46672  * </ul></div>
46673  * <p>Example usage:</p>
46674  * <pre><code>
46675 // consume JSON of this form:
46676 {
46677    "images":[
46678       {
46679          "name":"dance_fever.jpg",
46680          "size":2067,
46681          "lastmod":1236974993000,
46682          "url":"images\/thumbs\/dance_fever.jpg"
46683       },
46684       {
46685          "name":"zack_sink.jpg",
46686          "size":2303,
46687          "lastmod":1236974993000,
46688          "url":"images\/thumbs\/zack_sink.jpg"
46689       }
46690    ]
46691 }
46692 var store = new Ext.data.JsonStore({
46693     url: 'get-images.php',
46694     root: 'images',
46695     fields: [
46696         'name', 'url',
46697         {name:'size', type: 'float'},
46698         {name:'lastmod', type:'date', dateFormat:'timestamp'}
46699     ]
46700 });
46701 store.load();
46702
46703 var listView = new Ext.list.ListView({
46704     store: store,
46705     multiSelect: true,
46706     emptyText: 'No images to display',
46707     reserveScrollOffset: true,
46708     columns: [{
46709         header: 'File',
46710         width: .5,
46711         dataIndex: 'name'
46712     },{
46713         header: 'Last Modified',
46714         width: .35,
46715         dataIndex: 'lastmod',
46716         tpl: '{lastmod:date("m-d h:i a")}'
46717     },{
46718         header: 'Size',
46719         dataIndex: 'size',
46720         tpl: '{size:fileSize}', // format using Ext.util.Format.fileSize()
46721         align: 'right'
46722     }]
46723 });
46724
46725 // put it in a Panel so it looks pretty
46726 var panel = new Ext.Panel({
46727     id:'images-view',
46728     width:425,
46729     height:250,
46730     collapsible:true,
46731     layout:'fit',
46732     title:'Simple ListView <i>(0 items selected)</i>',
46733     items: listView
46734 });
46735 panel.render(document.body);
46736
46737 // little bit of feedback
46738 listView.on('selectionchange', function(view, nodes){
46739     var l = nodes.length;
46740     var s = l != 1 ? 's' : '';
46741     panel.setTitle('Simple ListView <i>('+l+' item'+s+' selected)</i>');
46742 });
46743  * </code></pre>
46744  * @constructor
46745  * @param {Object} config
46746  * @xtype listview
46747  */
46748 Ext.list.ListView = Ext.extend(Ext.DataView, {
46749     /**
46750      * Set this property to <tt>true</tt> to disable the header click handler disabling sort
46751      * (defaults to <tt>false</tt>).
46752      * @type Boolean
46753      * @property disableHeaders
46754      */
46755     /**
46756      * @cfg {Boolean} hideHeaders
46757      * <tt>true</tt> to hide the {@link #internalTpl header row} (defaults to <tt>false</tt> so
46758      * the {@link #internalTpl header row} will be shown).
46759      */
46760     /**
46761      * @cfg {String} itemSelector
46762      * Defaults to <tt>'dl'</tt> to work with the preconfigured <b><tt>{@link Ext.DataView#tpl tpl}</tt></b>.
46763      * This setting specifies the CSS selector (e.g. <tt>div.some-class</tt> or <tt>span:first-child</tt>)
46764      * that will be used to determine what nodes the ListView will be working with.
46765      */
46766     itemSelector: 'dl',
46767     /**
46768      * @cfg {String} selectedClass The CSS class applied to a selected row (defaults to
46769      * <tt>'x-list-selected'</tt>). An example overriding the default styling:
46770     <pre><code>
46771     .x-list-selected {background-color: yellow;}
46772     </code></pre>
46773      * @type String
46774      */
46775     selectedClass:'x-list-selected',
46776     /**
46777      * @cfg {String} overClass The CSS class applied when over a row (defaults to
46778      * <tt>'x-list-over'</tt>). An example overriding the default styling:
46779     <pre><code>
46780     .x-list-over {background-color: orange;}
46781     </code></pre>
46782      * @type String
46783      */
46784     overClass:'x-list-over',
46785     /**
46786      * @cfg {Boolean} reserveScrollOffset
46787      * By default will defer accounting for the configured <b><tt>{@link #scrollOffset}</tt></b>
46788      * for 10 milliseconds.  Specify <tt>true</tt> to account for the configured
46789      * <b><tt>{@link #scrollOffset}</tt></b> immediately.
46790      */
46791     /**
46792      * @cfg {Number} scrollOffset The amount of space to reserve for the scrollbar (defaults to
46793      * <tt>undefined</tt>). If an explicit value isn't specified, this will be automatically
46794      * calculated.
46795      */
46796     scrollOffset : undefined,
46797     /**
46798      * @cfg {Boolean/Object} columnResize
46799      * Specify <tt>true</tt> or specify a configuration object for {@link Ext.list.ListView.ColumnResizer}
46800      * to enable the columns to be resizable (defaults to <tt>true</tt>).
46801      */
46802     columnResize: true,
46803     /**
46804      * @cfg {Array} columns An array of column configuration objects, for example:
46805      * <pre><code>
46806 {
46807     align: 'right',
46808     dataIndex: 'size',
46809     header: 'Size',
46810     tpl: '{size:fileSize}',
46811     width: .35
46812 }
46813      * </code></pre>
46814      * Acceptable properties for each column configuration object are:
46815      * <div class="mdetail-params"><ul>
46816      * <li><b><tt>align</tt></b> : String<div class="sub-desc">Set the CSS text-align property
46817      * of the column. Defaults to <tt>'left'</tt>.</div></li>
46818      * <li><b><tt>dataIndex</tt></b> : String<div class="sub-desc">See {@link Ext.grid.Column}.
46819      * {@link Ext.grid.Column#dataIndex dataIndex} for details.</div></li>
46820      * <li><b><tt>header</tt></b> : String<div class="sub-desc">See {@link Ext.grid.Column}.
46821      * {@link Ext.grid.Column#header header} for details.</div></li>
46822      * <li><b><tt>tpl</tt></b> : String<div class="sub-desc">Specify a string to pass as the
46823      * configuration string for {@link Ext.XTemplate}.  By default an {@link Ext.XTemplate}
46824      * will be implicitly created using the <tt>dataIndex</tt>.</div></li>
46825      * <li><b><tt>width</tt></b> : Number<div class="sub-desc">Percentage of the container width
46826      * this column should be allocated.  Columns that have no width specified will be
46827      * allocated with an equal percentage to fill 100% of the container width.  To easily take
46828      * advantage of the full container width, leave the width of at least one column undefined.
46829      * Note that if you do not want to take up the full width of the container, the width of
46830      * every column needs to be explicitly defined.</div></li>
46831      * </ul></div>
46832      */
46833     /**
46834      * @cfg {Boolean/Object} columnSort
46835      * Specify <tt>true</tt> or specify a configuration object for {@link Ext.list.ListView.Sorter}
46836      * to enable the columns to be sortable (defaults to <tt>true</tt>).
46837      */
46838     columnSort: true,
46839     /**
46840      * @cfg {String/Array} internalTpl
46841      * The template to be used for the header row.  See {@link #tpl} for more details.
46842      */
46843
46844     /*
46845      * IE has issues when setting percentage based widths to 100%. Default to 99.
46846      */
46847     maxColumnWidth: Ext.isIE ? 99 : 100,
46848
46849     initComponent : function(){
46850         if(this.columnResize){
46851             this.colResizer = new Ext.list.ColumnResizer(this.colResizer);
46852             this.colResizer.init(this);
46853         }
46854         if(this.columnSort){
46855             this.colSorter = new Ext.list.Sorter(this.columnSort);
46856             this.colSorter.init(this);
46857         }
46858         if(!this.internalTpl){
46859             this.internalTpl = new Ext.XTemplate(
46860                 '<div class="x-list-header"><div class="x-list-header-inner">',
46861                     '<tpl for="columns">',
46862                     '<div style="width:{[values.width*100]}%;text-align:{align};"><em unselectable="on" id="',this.id, '-xlhd-{#}">',
46863                         '{header}',
46864                     '</em></div>',
46865                     '</tpl>',
46866                     '<div class="x-clear"></div>',
46867                 '</div></div>',
46868                 '<div class="x-list-body"><div class="x-list-body-inner">',
46869                 '</div></div>'
46870             );
46871         }
46872         if(!this.tpl){
46873             this.tpl = new Ext.XTemplate(
46874                 '<tpl for="rows">',
46875                     '<dl>',
46876                         '<tpl for="parent.columns">',
46877                         '<dt style="width:{[values.width*100]}%;text-align:{align};">',
46878                         '<em unselectable="on"<tpl if="cls"> class="{cls}</tpl>">',
46879                             '{[values.tpl.apply(parent)]}',
46880                         '</em></dt>',
46881                         '</tpl>',
46882                         '<div class="x-clear"></div>',
46883                     '</dl>',
46884                 '</tpl>'
46885             );
46886         };
46887
46888         var cs = this.columns,
46889             allocatedWidth = 0,
46890             colsWithWidth = 0,
46891             len = cs.length,
46892             columns = [];
46893
46894         for(var i = 0; i < len; i++){
46895             var c = cs[i];
46896             if(!c.isColumn) {
46897                 c.xtype = c.xtype ? (/^lv/.test(c.xtype) ? c.xtype : 'lv' + c.xtype) : 'lvcolumn';
46898                 c = Ext.create(c);
46899             }
46900             if(c.width) {
46901                 allocatedWidth += c.width*100;
46902                 if(allocatedWidth > this.maxColumnWidth){
46903                     c.width -= (allocatedWidth - this.maxColumnWidth) / 100;
46904                 }
46905                 colsWithWidth++;
46906             }
46907             columns.push(c);
46908         }
46909
46910         cs = this.columns = columns;
46911
46912         // auto calculate missing column widths
46913         if(colsWithWidth < len){
46914             var remaining = len - colsWithWidth;
46915             if(allocatedWidth < this.maxColumnWidth){
46916                 var perCol = ((this.maxColumnWidth-allocatedWidth) / remaining)/100;
46917                 for(var j = 0; j < len; j++){
46918                     var c = cs[j];
46919                     if(!c.width){
46920                         c.width = perCol;
46921                     }
46922                 }
46923             }
46924         }
46925         Ext.list.ListView.superclass.initComponent.call(this);
46926     },
46927
46928     onRender : function(){
46929         this.autoEl = {
46930             cls: 'x-list-wrap'
46931         };
46932         Ext.list.ListView.superclass.onRender.apply(this, arguments);
46933
46934         this.internalTpl.overwrite(this.el, {columns: this.columns});
46935
46936         this.innerBody = Ext.get(this.el.dom.childNodes[1].firstChild);
46937         this.innerHd = Ext.get(this.el.dom.firstChild.firstChild);
46938
46939         if(this.hideHeaders){
46940             this.el.dom.firstChild.style.display = 'none';
46941         }
46942     },
46943
46944     getTemplateTarget : function(){
46945         return this.innerBody;
46946     },
46947
46948     /**
46949      * <p>Function which can be overridden which returns the data object passed to this
46950      * view's {@link #tpl template} to render the whole ListView. The returned object
46951      * shall contain the following properties:</p>
46952      * <div class="mdetail-params"><ul>
46953      * <li><b>columns</b> : String<div class="sub-desc">See <tt>{@link #columns}</tt></div></li>
46954      * <li><b>rows</b> : String<div class="sub-desc">See
46955      * <tt>{@link Ext.DataView}.{@link Ext.DataView#collectData collectData}</div></li>
46956      * </ul></div>
46957      * @param {Array} records An Array of {@link Ext.data.Record}s to be rendered into the DataView.
46958      * @param {Number} startIndex the index number of the Record being prepared for rendering.
46959      * @return {Object} A data object containing properties to be processed by a repeating
46960      * XTemplate as described above.
46961      */
46962     collectData : function(){
46963         var rs = Ext.list.ListView.superclass.collectData.apply(this, arguments);
46964         return {
46965             columns: this.columns,
46966             rows: rs
46967         };
46968     },
46969
46970     verifyInternalSize : function(){
46971         if(this.lastSize){
46972             this.onResize(this.lastSize.width, this.lastSize.height);
46973         }
46974     },
46975
46976     // private
46977     onResize : function(w, h){
46978         var body = this.innerBody.dom,
46979             header = this.innerHd.dom,
46980             scrollWidth = w - Ext.num(this.scrollOffset, Ext.getScrollBarWidth()) + 'px',
46981             parentNode;
46982             
46983         if(!body){
46984             return;
46985         }
46986         parentNode = body.parentNode;
46987         if(Ext.isNumber(w)){
46988             if(this.reserveScrollOffset || ((parentNode.offsetWidth - parentNode.clientWidth) > 10)){
46989                 body.style.width = scrollWidth;
46990                 header.style.width = scrollWidth;
46991             }else{
46992                 body.style.width = w + 'px';
46993                 header.style.width = w + 'px';
46994                 setTimeout(function(){
46995                     if((parentNode.offsetWidth - parentNode.clientWidth) > 10){
46996                         body.style.width = scrollWidth;
46997                         header.style.width = scrollWidth;
46998                     }
46999                 }, 10);
47000             }
47001         }
47002         if(Ext.isNumber(h)){
47003             parentNode.style.height = Math.max(0, h - header.parentNode.offsetHeight) + 'px';
47004         }
47005     },
47006
47007     updateIndexes : function(){
47008         Ext.list.ListView.superclass.updateIndexes.apply(this, arguments);
47009         this.verifyInternalSize();
47010     },
47011
47012     findHeaderIndex : function(header){
47013         header = header.dom || header;
47014         var parentNode = header.parentNode, 
47015             children = parentNode.parentNode.childNodes,
47016             i = 0,
47017             c;
47018         for(; c = children[i]; i++){
47019             if(c == parentNode){
47020                 return i;
47021             }
47022         }
47023         return -1;
47024     },
47025
47026     setHdWidths : function(){
47027         var els = this.innerHd.dom.getElementsByTagName('div'),
47028             i = 0,
47029             columns = this.columns,
47030             len = columns.length;
47031             
47032         for(; i < len; i++){
47033             els[i].style.width = (columns[i].width*100) + '%';
47034         }
47035     }
47036 });
47037
47038 Ext.reg('listview', Ext.list.ListView);
47039
47040 // Backwards compatibility alias
47041 Ext.ListView = Ext.list.ListView;/**
47042  * @class Ext.list.Column
47043  * <p>This class encapsulates column configuration data to be used in the initialization of a
47044  * {@link Ext.list.ListView ListView}.</p>
47045  * <p>While subclasses are provided to render data in different ways, this class renders a passed
47046  * data field unchanged and is usually used for textual columns.</p>
47047  */
47048 Ext.list.Column = Ext.extend(Object, {
47049     /**
47050      * @private
47051      * @cfg {Boolean} isColumn
47052      * Used by ListView constructor method to avoid reprocessing a Column
47053      * if <code>isColumn</code> is not set ListView will recreate a new Ext.list.Column
47054      * Defaults to true.
47055      */
47056     isColumn: true,
47057     
47058     /**
47059      * @cfg {String} align
47060      * Set the CSS text-align property of the column. Defaults to <tt>'left'</tt>.
47061      */        
47062     align: 'left',
47063     /**
47064      * @cfg {String} header Optional. The header text to be used as innerHTML
47065      * (html tags are accepted) to display in the ListView.  <b>Note</b>: to
47066      * have a clickable header with no text displayed use <tt>'&#160;'</tt>.
47067      */    
47068     header: '',
47069     
47070     /**
47071      * @cfg {Number} width Optional. Percentage of the container width
47072      * this column should be allocated.  Columns that have no width specified will be
47073      * allocated with an equal percentage to fill 100% of the container width.  To easily take
47074      * advantage of the full container width, leave the width of at least one column undefined.
47075      * Note that if you do not want to take up the full width of the container, the width of
47076      * every column needs to be explicitly defined.
47077      */    
47078     width: null,
47079
47080     /**
47081      * @cfg {String} cls Optional. This option can be used to add a CSS class to the cell of each
47082      * row for this column.
47083      */
47084     cls: '',
47085     
47086     /**
47087      * @cfg {String} tpl Optional. Specify a string to pass as the
47088      * configuration string for {@link Ext.XTemplate}.  By default an {@link Ext.XTemplate}
47089      * will be implicitly created using the <tt>dataIndex</tt>.
47090      */
47091
47092     /**
47093      * @cfg {String} dataIndex <p><b>Required</b>. The name of the field in the
47094      * ListViews's {@link Ext.data.Store}'s {@link Ext.data.Record} definition from
47095      * which to draw the column's value.</p>
47096      */
47097     
47098     constructor : function(c){
47099         if(!c.tpl){
47100             c.tpl = new Ext.XTemplate('{' + c.dataIndex + '}');
47101         }
47102         else if(Ext.isString(c.tpl)){
47103             c.tpl = new Ext.XTemplate(c.tpl);
47104         }
47105         
47106         Ext.apply(this, c);
47107     }
47108 });
47109
47110 Ext.reg('lvcolumn', Ext.list.Column);
47111
47112 /**
47113  * @class Ext.list.NumberColumn
47114  * @extends Ext.list.Column
47115  * <p>A Column definition class which renders a numeric data field according to a {@link #format} string.  See the
47116  * {@link Ext.list.Column#xtype xtype} config option of {@link Ext.list.Column} for more details.</p>
47117  */
47118 Ext.list.NumberColumn = Ext.extend(Ext.list.Column, {
47119     /**
47120      * @cfg {String} format
47121      * A formatting string as used by {@link Ext.util.Format#number} to format a numeric value for this Column
47122      * (defaults to <tt>'0,000.00'</tt>).
47123      */    
47124     format: '0,000.00',
47125     
47126     constructor : function(c) {
47127         c.tpl = c.tpl || new Ext.XTemplate('{' + c.dataIndex + ':number("' + (c.format || this.format) + '")}');       
47128         Ext.list.NumberColumn.superclass.constructor.call(this, c);
47129     }
47130 });
47131
47132 Ext.reg('lvnumbercolumn', Ext.list.NumberColumn);
47133
47134 /**
47135  * @class Ext.list.DateColumn
47136  * @extends Ext.list.Column
47137  * <p>A Column definition class which renders a passed date according to the default locale, or a configured
47138  * {@link #format}. See the {@link Ext.list.Column#xtype xtype} config option of {@link Ext.list.Column}
47139  * for more details.</p>
47140  */
47141 Ext.list.DateColumn = Ext.extend(Ext.list.Column, {
47142     format: 'm/d/Y',
47143     constructor : function(c) {
47144         c.tpl = c.tpl || new Ext.XTemplate('{' + c.dataIndex + ':date("' + (c.format || this.format) + '")}');      
47145         Ext.list.DateColumn.superclass.constructor.call(this, c);
47146     }
47147 });
47148 Ext.reg('lvdatecolumn', Ext.list.DateColumn);
47149
47150 /**
47151  * @class Ext.list.BooleanColumn
47152  * @extends Ext.list.Column
47153  * <p>A Column definition class which renders boolean data fields.  See the {@link Ext.list.Column#xtype xtype}
47154  * config option of {@link Ext.list.Column} for more details.</p>
47155  */
47156 Ext.list.BooleanColumn = Ext.extend(Ext.list.Column, {
47157     /**
47158      * @cfg {String} trueText
47159      * The string returned by the renderer when the column value is not falsey (defaults to <tt>'true'</tt>).
47160      */
47161     trueText: 'true',
47162     /**
47163      * @cfg {String} falseText
47164      * The string returned by the renderer when the column value is falsey (but not undefined) (defaults to
47165      * <tt>'false'</tt>).
47166      */
47167     falseText: 'false',
47168     /**
47169      * @cfg {String} undefinedText
47170      * The string returned by the renderer when the column value is undefined (defaults to <tt>'&#160;'</tt>).
47171      */
47172     undefinedText: '&#160;',
47173     
47174     constructor : function(c) {
47175         c.tpl = c.tpl || new Ext.XTemplate('{' + c.dataIndex + ':this.format}');
47176         
47177         var t = this.trueText, f = this.falseText, u = this.undefinedText;
47178         c.tpl.format = function(v){
47179             if(v === undefined){
47180                 return u;
47181             }
47182             if(!v || v === 'false'){
47183                 return f;
47184             }
47185             return t;
47186         };
47187         
47188         Ext.list.DateColumn.superclass.constructor.call(this, c);
47189     }
47190 });
47191
47192 Ext.reg('lvbooleancolumn', Ext.list.BooleanColumn);/**
47193  * @class Ext.list.ColumnResizer
47194  * @extends Ext.util.Observable
47195  * <p>Supporting Class for Ext.list.ListView</p>
47196  * @constructor
47197  * @param {Object} config
47198  */
47199 Ext.list.ColumnResizer = Ext.extend(Ext.util.Observable, {
47200     /**
47201      * @cfg {Number} minPct The minimum percentage to allot for any column (defaults to <tt>.05</tt>)
47202      */
47203     minPct: .05,
47204
47205     constructor: function(config){
47206         Ext.apply(this, config);
47207         Ext.list.ColumnResizer.superclass.constructor.call(this);
47208     },
47209     init : function(listView){
47210         this.view = listView;
47211         listView.on('render', this.initEvents, this);
47212     },
47213
47214     initEvents : function(view){
47215         view.mon(view.innerHd, 'mousemove', this.handleHdMove, this);
47216         this.tracker = new Ext.dd.DragTracker({
47217             onBeforeStart: this.onBeforeStart.createDelegate(this),
47218             onStart: this.onStart.createDelegate(this),
47219             onDrag: this.onDrag.createDelegate(this),
47220             onEnd: this.onEnd.createDelegate(this),
47221             tolerance: 3,
47222             autoStart: 300
47223         });
47224         this.tracker.initEl(view.innerHd);
47225         view.on('beforedestroy', this.tracker.destroy, this.tracker);
47226     },
47227
47228     handleHdMove : function(e, t){
47229         var handleWidth = 5,
47230             x = e.getPageX(),
47231             header = e.getTarget('em', 3, true);
47232         if(header){
47233             var region = header.getRegion(),
47234                 style = header.dom.style,
47235                 parentNode = header.dom.parentNode;
47236
47237             if(x - region.left <= handleWidth && parentNode != parentNode.parentNode.firstChild){
47238                 this.activeHd = Ext.get(parentNode.previousSibling.firstChild);
47239                 style.cursor = Ext.isWebKit ? 'e-resize' : 'col-resize';
47240             } else if(region.right - x <= handleWidth && parentNode != parentNode.parentNode.lastChild.previousSibling){
47241                 this.activeHd = header;
47242                 style.cursor = Ext.isWebKit ? 'w-resize' : 'col-resize';
47243             } else{
47244                 delete this.activeHd;
47245                 style.cursor = '';
47246             }
47247         }
47248     },
47249
47250     onBeforeStart : function(e){
47251         this.dragHd = this.activeHd;
47252         return !!this.dragHd;
47253     },
47254
47255     onStart: function(e){
47256         
47257         var me = this,
47258             view = me.view,
47259             dragHeader = me.dragHd,
47260             x = me.tracker.getXY()[0];            
47261         
47262         me.proxy = view.el.createChild({cls:'x-list-resizer'});
47263         me.dragX = dragHeader.getX();
47264         me.headerIndex = view.findHeaderIndex(dragHeader);
47265         
47266         me.headersDisabled = view.disableHeaders;
47267         view.disableHeaders = true;
47268         
47269         me.proxy.setHeight(view.el.getHeight());
47270         me.proxy.setX(me.dragX);
47271         me.proxy.setWidth(x - me.dragX);
47272         
47273         this.setBoundaries();
47274         
47275     },
47276     
47277     // Sets up the boundaries for the drag/drop operation
47278     setBoundaries: function(relativeX){
47279         var view = this.view,
47280             headerIndex = this.headerIndex,
47281             width = view.innerHd.getWidth(),
47282             relativeX = view.innerHd.getX(),
47283             minWidth = Math.ceil(width * this.minPct),
47284             maxWidth = width - minWidth,
47285             numColumns = view.columns.length,
47286             headers = view.innerHd.select('em', true),
47287             minX = minWidth + relativeX,
47288             maxX = maxWidth + relativeX,
47289             header;
47290           
47291         if (numColumns == 2) {
47292             this.minX = minX;
47293             this.maxX = maxX;
47294         }else{
47295             header = headers.item(headerIndex + 2);
47296             this.minX = headers.item(headerIndex).getX() + minWidth;
47297             this.maxX = header ? header.getX() - minWidth : maxX;
47298             if (headerIndex == 0) {
47299                 // First
47300                 this.minX = minX;
47301             } else if (headerIndex == numColumns - 2) {
47302                 // Last
47303                 this.maxX = maxX;
47304             }
47305         }
47306     },
47307
47308     onDrag: function(e){
47309         var me = this,
47310             cursorX = me.tracker.getXY()[0].constrain(me.minX, me.maxX);
47311             
47312         me.proxy.setWidth(cursorX - this.dragX);
47313     },
47314
47315     onEnd: function(e){
47316         /* calculate desired width by measuring proxy and then remove it */
47317         var newWidth = this.proxy.getWidth(),
47318             index = this.headerIndex,
47319             view = this.view,
47320             columns = view.columns,
47321             width = view.innerHd.getWidth(),
47322             newPercent = Math.ceil(newWidth * view.maxColumnWidth / width) / 100,
47323             disabled = this.headersDisabled,
47324             headerCol = columns[index],
47325             otherCol = columns[index + 1],
47326             totalPercent = headerCol.width + otherCol.width;
47327
47328         this.proxy.remove();
47329
47330         headerCol.width = newPercent;
47331         otherCol.width = totalPercent - newPercent;
47332       
47333         delete this.dragHd;
47334         view.setHdWidths();
47335         view.refresh();
47336         
47337         setTimeout(function(){
47338             view.disableHeaders = disabled;
47339         }, 100);
47340     }
47341 });
47342
47343 // Backwards compatibility alias
47344 Ext.ListView.ColumnResizer = Ext.list.ColumnResizer;/**
47345  * @class Ext.list.Sorter
47346  * @extends Ext.util.Observable
47347  * <p>Supporting Class for Ext.list.ListView</p>
47348  * @constructor
47349  * @param {Object} config
47350  */
47351 Ext.list.Sorter = Ext.extend(Ext.util.Observable, {
47352     /**
47353      * @cfg {Array} sortClasses
47354      * The CSS classes applied to a header when it is sorted. (defaults to <tt>["sort-asc", "sort-desc"]</tt>)
47355      */
47356     sortClasses : ["sort-asc", "sort-desc"],
47357
47358     constructor: function(config){
47359         Ext.apply(this, config);
47360         Ext.list.Sorter.superclass.constructor.call(this);
47361     },
47362
47363     init : function(listView){
47364         this.view = listView;
47365         listView.on('render', this.initEvents, this);
47366     },
47367
47368     initEvents : function(view){
47369         view.mon(view.innerHd, 'click', this.onHdClick, this);
47370         view.innerHd.setStyle('cursor', 'pointer');
47371         view.mon(view.store, 'datachanged', this.updateSortState, this);
47372         this.updateSortState.defer(10, this, [view.store]);
47373     },
47374
47375     updateSortState : function(store){
47376         var state = store.getSortState();
47377         if(!state){
47378             return;
47379         }
47380         this.sortState = state;
47381         var cs = this.view.columns, sortColumn = -1;
47382         for(var i = 0, len = cs.length; i < len; i++){
47383             if(cs[i].dataIndex == state.field){
47384                 sortColumn = i;
47385                 break;
47386             }
47387         }
47388         if(sortColumn != -1){
47389             var sortDir = state.direction;
47390             this.updateSortIcon(sortColumn, sortDir);
47391         }
47392     },
47393
47394     updateSortIcon : function(col, dir){
47395         var sc = this.sortClasses;
47396         var hds = this.view.innerHd.select('em').removeClass(sc);
47397         hds.item(col).addClass(sc[dir == "DESC" ? 1 : 0]);
47398     },
47399
47400     onHdClick : function(e){
47401         var hd = e.getTarget('em', 3);
47402         if(hd && !this.view.disableHeaders){
47403             var index = this.view.findHeaderIndex(hd);
47404             this.view.store.sort(this.view.columns[index].dataIndex);
47405         }
47406     }
47407 });
47408
47409 // Backwards compatibility alias
47410 Ext.ListView.Sorter = Ext.list.Sorter;/**
47411  * @class Ext.TabPanel
47412  * <p>A basic tab container. TabPanels can be used exactly like a standard {@link Ext.Panel}
47413  * for layout purposes, but also have special support for containing child Components
47414  * (<tt>{@link Ext.Container#items items}</tt>) that are managed using a
47415  * {@link Ext.layout.CardLayout CardLayout layout manager}, and displayed as separate tabs.</p>
47416  *
47417  * <b>Note:</b> By default, a tab's close tool <i>destroys</i> the child tab Component
47418  * and all its descendants. This makes the child tab Component, and all its descendants <b>unusable</b>. To enable
47419  * re-use of a tab, configure the TabPanel with <b><code>{@link #autoDestroy autoDestroy: false}</code></b>.
47420  *
47421  * <p><b><u>TabPanel header/footer elements</u></b></p>
47422  * <p>TabPanels use their {@link Ext.Panel#header header} or {@link Ext.Panel#footer footer} element
47423  * (depending on the {@link #tabPosition} configuration) to accommodate the tab selector buttons.
47424  * This means that a TabPanel will not display any configured title, and will not display any
47425  * configured header {@link Ext.Panel#tools tools}.</p>
47426  * <p>To display a header, embed the TabPanel in a {@link Ext.Panel Panel} which uses
47427  * <b><tt>{@link Ext.Container#layout layout:'fit'}</tt></b>.</p>
47428  *
47429  * <p><b><u>Tab Events</u></b></p>
47430  * <p>There is no actual tab class &mdash; each tab is simply a {@link Ext.BoxComponent Component}
47431  * such as a {@link Ext.Panel Panel}. However, when rendered in a TabPanel, each child Component
47432  * can fire additional events that only exist for tabs and are not available from other Components.
47433  * These events are:</p>
47434  * <div><ul class="mdetail-params">
47435  * <li><tt><b>{@link Ext.Panel#activate activate}</b></tt> : Fires when this Component becomes
47436  * the active tab.</li>
47437  * <li><tt><b>{@link Ext.Panel#deactivate deactivate}</b></tt> : Fires when the Component that
47438  * was the active tab becomes deactivated.</li>
47439  * <li><tt><b>{@link Ext.Panel#beforeclose beforeclose}</b></tt> : Fires when the user clicks on the close tool of a closeable tab.
47440  * May be vetoed by returning <code>false</code> from a handler.</li>
47441  * <li><tt><b>{@link Ext.Panel#close close}</b></tt> : Fires a closeable tab has been closed by the user.</li>
47442  * </ul></div>
47443  * <p><b><u>Creating TabPanels from Code</u></b></p>
47444  * <p>TabPanels can be created and rendered completely in code, as in this example:</p>
47445  * <pre><code>
47446 var tabs = new Ext.TabPanel({
47447     renderTo: Ext.getBody(),
47448     activeTab: 0,
47449     items: [{
47450         title: 'Tab 1',
47451         html: 'A simple tab'
47452     },{
47453         title: 'Tab 2',
47454         html: 'Another one'
47455     }]
47456 });
47457 </code></pre>
47458  * <p><b><u>Creating TabPanels from Existing Markup</u></b></p>
47459  * <p>TabPanels can also be rendered from pre-existing markup in a couple of ways.</p>
47460  * <div><ul class="mdetail-params">
47461  *
47462  * <li>Pre-Structured Markup</li>
47463  * <div class="sub-desc">
47464  * <p>A container div with one or more nested tab divs with class <tt>'x-tab'</tt> can be rendered entirely
47465  * from existing markup (See the {@link #autoTabs} example).</p>
47466  * </div>
47467  *
47468  * <li>Un-Structured Markup</li>
47469  * <div class="sub-desc">
47470  * <p>A TabPanel can also be rendered from markup that is not strictly structured by simply specifying by id
47471  * which elements should be the container and the tabs. Using this method tab content can be pulled from different
47472  * elements within the page by id regardless of page structure. For example:</p>
47473  * <pre><code>
47474 var tabs = new Ext.TabPanel({
47475     renderTo: 'my-tabs',
47476     activeTab: 0,
47477     items:[
47478         {contentEl:'tab1', title:'Tab 1'},
47479         {contentEl:'tab2', title:'Tab 2'}
47480     ]
47481 });
47482
47483 // Note that the tabs do not have to be nested within the container (although they can be)
47484 &lt;div id="my-tabs">&lt;/div>
47485 &lt;div id="tab1" class="x-hide-display">A simple tab&lt;/div>
47486 &lt;div id="tab2" class="x-hide-display">Another one&lt;/div>
47487 </code></pre>
47488  * Note that the tab divs in this example contain the class <tt>'x-hide-display'</tt> so that they can be rendered
47489  * deferred without displaying outside the tabs. You could alternately set <tt>{@link #deferredRender} = false </tt>
47490  * to render all content tabs on page load.
47491  * </div>
47492  *
47493  * </ul></div>
47494  *
47495  * @extends Ext.Panel
47496  * @constructor
47497  * @param {Object} config The configuration options
47498  * @xtype tabpanel
47499  */
47500 Ext.TabPanel = Ext.extend(Ext.Panel,  {
47501     /**
47502      * @cfg {Boolean} layoutOnTabChange
47503      * Set to true to force a layout of the active tab when the tab is changed. Defaults to false.
47504      * See {@link Ext.layout.CardLayout}.<code>{@link Ext.layout.CardLayout#layoutOnCardChange layoutOnCardChange}</code>.
47505      */
47506     /**
47507      * @cfg {String} tabCls <b>This config option is used on <u>child Components</u> of ths TabPanel.</b> A CSS
47508      * class name applied to the tab strip item representing the child Component, allowing special
47509      * styling to be applied.
47510      */
47511     /**
47512      * @cfg {Boolean} deferredRender
47513      * <p><tt>true</tt> by default to defer the rendering of child <tt>{@link Ext.Container#items items}</tt>
47514      * to the browsers DOM until a tab is activated. <tt>false</tt> will render all contained
47515      * <tt>{@link Ext.Container#items items}</tt> as soon as the {@link Ext.layout.CardLayout layout}
47516      * is rendered. If there is a significant amount of content or a lot of heavy controls being
47517      * rendered into panels that are not displayed by default, setting this to <tt>true</tt> might
47518      * improve performance.</p>
47519      * <br><p>The <tt>deferredRender</tt> property is internally passed to the layout manager for
47520      * TabPanels ({@link Ext.layout.CardLayout}) as its {@link Ext.layout.CardLayout#deferredRender}
47521      * configuration value.</p>
47522      * <br><p><b>Note</b>: leaving <tt>deferredRender</tt> as <tt>true</tt> means that the content
47523      * within an unactivated tab will not be available. For example, this means that if the TabPanel
47524      * is within a {@link Ext.form.FormPanel form}, then until a tab is activated, any Fields within
47525      * unactivated tabs will not be rendered, and will therefore not be submitted and will not be
47526      * available to either {@link Ext.form.BasicForm#getValues getValues} or
47527      * {@link Ext.form.BasicForm#setValues setValues}.</p>
47528      */
47529     deferredRender : true,
47530     /**
47531      * @cfg {Number} tabWidth The initial width in pixels of each new tab (defaults to 120).
47532      */
47533     tabWidth : 120,
47534     /**
47535      * @cfg {Number} minTabWidth The minimum width in pixels for each tab when {@link #resizeTabs} = true (defaults to 30).
47536      */
47537     minTabWidth : 30,
47538     /**
47539      * @cfg {Boolean} resizeTabs True to automatically resize each tab so that the tabs will completely fill the
47540      * tab strip (defaults to false).  Setting this to true may cause specific widths that might be set per tab to
47541      * be overridden in order to fit them all into view (although {@link #minTabWidth} will always be honored).
47542      */
47543     resizeTabs : false,
47544     /**
47545      * @cfg {Boolean} enableTabScroll True to enable scrolling to tabs that may be invisible due to overflowing the
47546      * overall TabPanel width. Only available with tabPosition:'top' (defaults to false).
47547      */
47548     enableTabScroll : false,
47549     /**
47550      * @cfg {Number} scrollIncrement The number of pixels to scroll each time a tab scroll button is pressed
47551      * (defaults to <tt>100</tt>, or if <tt>{@link #resizeTabs} = true</tt>, the calculated tab width).  Only
47552      * applies when <tt>{@link #enableTabScroll} = true</tt>.
47553      */
47554     scrollIncrement : 0,
47555     /**
47556      * @cfg {Number} scrollRepeatInterval Number of milliseconds between each scroll while a tab scroll button is
47557      * continuously pressed (defaults to <tt>400</tt>).
47558      */
47559     scrollRepeatInterval : 400,
47560     /**
47561      * @cfg {Float} scrollDuration The number of milliseconds that each scroll animation should last (defaults
47562      * to <tt>.35</tt>). Only applies when <tt>{@link #animScroll} = true</tt>.
47563      */
47564     scrollDuration : 0.35,
47565     /**
47566      * @cfg {Boolean} animScroll True to animate tab scrolling so that hidden tabs slide smoothly into view (defaults
47567      * to <tt>true</tt>).  Only applies when <tt>{@link #enableTabScroll} = true</tt>.
47568      */
47569     animScroll : true,
47570     /**
47571      * @cfg {String} tabPosition The position where the tab strip should be rendered (defaults to <tt>'top'</tt>).
47572      * The only other supported value is <tt>'bottom'</tt>.  <b>Note</b>: tab scrolling is only supported for
47573      * <tt>tabPosition: 'top'</tt>.
47574      */
47575     tabPosition : 'top',
47576     /**
47577      * @cfg {String} baseCls The base CSS class applied to the panel (defaults to <tt>'x-tab-panel'</tt>).
47578      */
47579     baseCls : 'x-tab-panel',
47580     /**
47581      * @cfg {Boolean} autoTabs
47582      * <p><tt>true</tt> to query the DOM for any divs with a class of 'x-tab' to be automatically converted
47583      * to tabs and added to this panel (defaults to <tt>false</tt>).  Note that the query will be executed within
47584      * the scope of the container element only (so that multiple tab panels from markup can be supported via this
47585      * method).</p>
47586      * <p>This method is only possible when the markup is structured correctly as a container with nested divs
47587      * containing the class <tt>'x-tab'</tt>. To create TabPanels without these limitations, or to pull tab content
47588      * from other elements on the page, see the example at the top of the class for generating tabs from markup.</p>
47589      * <p>There are a couple of things to note when using this method:<ul>
47590      * <li>When using the <tt>autoTabs</tt> config (as opposed to passing individual tab configs in the TabPanel's
47591      * {@link #items} collection), you must use <tt>{@link #applyTo}</tt> to correctly use the specified <tt>id</tt>
47592      * as the tab container. The <tt>autoTabs</tt> method <em>replaces</em> existing content with the TabPanel
47593      * components.</li>
47594      * <li>Make sure that you set <tt>{@link #deferredRender}: false</tt> so that the content elements for each
47595      * tab will be rendered into the TabPanel immediately upon page load, otherwise they will not be transformed
47596      * until each tab is activated and will be visible outside the TabPanel.</li>
47597      * </ul>Example usage:</p>
47598      * <pre><code>
47599 var tabs = new Ext.TabPanel({
47600     applyTo: 'my-tabs',
47601     activeTab: 0,
47602     deferredRender: false,
47603     autoTabs: true
47604 });
47605
47606 // This markup will be converted to a TabPanel from the code above
47607 &lt;div id="my-tabs">
47608     &lt;div class="x-tab" title="Tab 1">A simple tab&lt;/div>
47609     &lt;div class="x-tab" title="Tab 2">Another one&lt;/div>
47610 &lt;/div>
47611 </code></pre>
47612      */
47613     autoTabs : false,
47614     /**
47615      * @cfg {String} autoTabSelector The CSS selector used to search for tabs in existing markup when
47616      * <tt>{@link #autoTabs} = true</tt> (defaults to <tt>'div.x-tab'</tt>).  This can be any valid selector
47617      * supported by {@link Ext.DomQuery#select}. Note that the query will be executed within the scope of this
47618      * tab panel only (so that multiple tab panels from markup can be supported on a page).
47619      */
47620     autoTabSelector : 'div.x-tab',
47621     /**
47622      * @cfg {String/Number} activeTab A string id or the numeric index of the tab that should be initially
47623      * activated on render (defaults to undefined).
47624      */
47625     activeTab : undefined,
47626     /**
47627      * @cfg {Number} tabMargin The number of pixels of space to calculate into the sizing and scrolling of
47628      * tabs. If you change the margin in CSS, you will need to update this value so calculations are correct
47629      * with either <tt>{@link #resizeTabs}</tt> or scrolling tabs. (defaults to <tt>2</tt>)
47630      */
47631     tabMargin : 2,
47632     /**
47633      * @cfg {Boolean} plain </tt>true</tt> to render the tab strip without a background container image
47634      * (defaults to <tt>false</tt>).
47635      */
47636     plain : false,
47637     /**
47638      * @cfg {Number} wheelIncrement For scrolling tabs, the number of pixels to increment on mouse wheel
47639      * scrolling (defaults to <tt>20</tt>).
47640      */
47641     wheelIncrement : 20,
47642
47643     /*
47644      * This is a protected property used when concatenating tab ids to the TabPanel id for internal uniqueness.
47645      * It does not generally need to be changed, but can be if external code also uses an id scheme that can
47646      * potentially clash with this one.
47647      */
47648     idDelimiter : '__',
47649
47650     // private
47651     itemCls : 'x-tab-item',
47652
47653     // private config overrides
47654     elements : 'body',
47655     headerAsText : false,
47656     frame : false,
47657     hideBorders :true,
47658
47659     // private
47660     initComponent : function(){
47661         this.frame = false;
47662         Ext.TabPanel.superclass.initComponent.call(this);
47663         this.addEvents(
47664             /**
47665              * @event beforetabchange
47666              * Fires before the active tab changes. Handlers can <tt>return false</tt> to cancel the tab change.
47667              * @param {TabPanel} this
47668              * @param {Panel} newTab The tab being activated
47669              * @param {Panel} currentTab The current active tab
47670              */
47671             'beforetabchange',
47672             /**
47673              * @event tabchange
47674              * Fires after the active tab has changed.
47675              * @param {TabPanel} this
47676              * @param {Panel} tab The new active tab
47677              */
47678             'tabchange',
47679             /**
47680              * @event contextmenu
47681              * Relays the contextmenu event from a tab selector element in the tab strip.
47682              * @param {TabPanel} this
47683              * @param {Panel} tab The target tab
47684              * @param {EventObject} e
47685              */
47686             'contextmenu'
47687         );
47688         /**
47689          * @cfg {Object} layoutConfig
47690          * TabPanel implicitly uses {@link Ext.layout.CardLayout} as its layout manager.
47691          * <code>layoutConfig</code> may be used to configure this layout manager.
47692          * <code>{@link #deferredRender}</code> and <code>{@link #layoutOnTabChange}</code>
47693          * configured on the TabPanel will be applied as configs to the layout manager.
47694          */
47695         this.setLayout(new Ext.layout.CardLayout(Ext.apply({
47696             layoutOnCardChange: this.layoutOnTabChange,
47697             deferredRender: this.deferredRender
47698         }, this.layoutConfig)));
47699
47700         if(this.tabPosition == 'top'){
47701             this.elements += ',header';
47702             this.stripTarget = 'header';
47703         }else {
47704             this.elements += ',footer';
47705             this.stripTarget = 'footer';
47706         }
47707         if(!this.stack){
47708             this.stack = Ext.TabPanel.AccessStack();
47709         }
47710         this.initItems();
47711     },
47712
47713     // private
47714     onRender : function(ct, position){
47715         Ext.TabPanel.superclass.onRender.call(this, ct, position);
47716
47717         if(this.plain){
47718             var pos = this.tabPosition == 'top' ? 'header' : 'footer';
47719             this[pos].addClass('x-tab-panel-'+pos+'-plain');
47720         }
47721
47722         var st = this[this.stripTarget];
47723
47724         this.stripWrap = st.createChild({cls:'x-tab-strip-wrap', cn:{
47725             tag:'ul', cls:'x-tab-strip x-tab-strip-'+this.tabPosition}});
47726
47727         var beforeEl = (this.tabPosition=='bottom' ? this.stripWrap : null);
47728         st.createChild({cls:'x-tab-strip-spacer'}, beforeEl);
47729         this.strip = new Ext.Element(this.stripWrap.dom.firstChild);
47730
47731         // create an empty span with class x-tab-strip-text to force the height of the header element when there's no tabs.
47732         this.edge = this.strip.createChild({tag:'li', cls:'x-tab-edge', cn: [{tag: 'span', cls: 'x-tab-strip-text', cn: '&#160;'}]});
47733         this.strip.createChild({cls:'x-clear'});
47734
47735         this.body.addClass('x-tab-panel-body-'+this.tabPosition);
47736
47737         /**
47738          * @cfg {Template/XTemplate} itemTpl <p>(Optional) A {@link Ext.Template Template} or
47739          * {@link Ext.XTemplate XTemplate} which may be provided to process the data object returned from
47740          * <tt>{@link #getTemplateArgs}</tt> to produce a clickable selector element in the tab strip.</p>
47741          * <p>The main element created should be a <tt>&lt;li></tt> element. In order for a click event on
47742          * a selector element to be connected to its item, it must take its <i>id</i> from the TabPanel's
47743          * native <tt>{@link #getTemplateArgs}</tt>.</p>
47744          * <p>The child element which contains the title text must be marked by the CSS class
47745          * <tt>x-tab-strip-inner</tt>.</p>
47746          * <p>To enable closability, the created element should contain an element marked by the CSS class
47747          * <tt>x-tab-strip-close</tt>.</p>
47748          * <p>If a custom <tt>itemTpl</tt> is supplied, it is the developer's responsibility to create CSS
47749          * style rules to create the desired appearance.</p>
47750          * Below is an example of how to create customized tab selector items:<pre><code>
47751 new Ext.TabPanel({
47752     renderTo: document.body,
47753     minTabWidth: 115,
47754     tabWidth: 135,
47755     enableTabScroll: true,
47756     width: 600,
47757     height: 250,
47758     defaults: {autoScroll:true},
47759     itemTpl: new Ext.XTemplate(
47760     '&lt;li class="{cls}" id="{id}" style="overflow:hidden">',
47761          '&lt;tpl if="closable">',
47762             '&lt;a class="x-tab-strip-close">&lt;/a>',
47763          '&lt;/tpl>',
47764          '&lt;a class="x-tab-right" href="#" style="padding-left:6px">',
47765             '&lt;em class="x-tab-left">',
47766                 '&lt;span class="x-tab-strip-inner">',
47767                     '&lt;img src="{src}" style="float:left;margin:3px 3px 0 0">',
47768                     '&lt;span style="margin-left:20px" class="x-tab-strip-text {iconCls}">{text} {extra}&lt;/span>',
47769                 '&lt;/span>',
47770             '&lt;/em>',
47771         '&lt;/a>',
47772     '&lt;/li>'
47773     ),
47774     getTemplateArgs: function(item) {
47775 //      Call the native method to collect the base data. Like the ID!
47776         var result = Ext.TabPanel.prototype.getTemplateArgs.call(this, item);
47777
47778 //      Add stuff used in our template
47779         return Ext.apply(result, {
47780             closable: item.closable,
47781             src: item.iconSrc,
47782             extra: item.extraText || ''
47783         });
47784     },
47785     items: [{
47786         title: 'New Tab 1',
47787         iconSrc: '../shared/icons/fam/grid.png',
47788         html: 'Tab Body 1',
47789         closable: true
47790     }, {
47791         title: 'New Tab 2',
47792         iconSrc: '../shared/icons/fam/grid.png',
47793         html: 'Tab Body 2',
47794         extraText: 'Extra stuff in the tab button'
47795     }]
47796 });
47797 </code></pre>
47798          */
47799         if(!this.itemTpl){
47800             var tt = new Ext.Template(
47801                  '<li class="{cls}" id="{id}"><a class="x-tab-strip-close"></a>',
47802                  '<a class="x-tab-right" href="#"><em class="x-tab-left">',
47803                  '<span class="x-tab-strip-inner"><span class="x-tab-strip-text {iconCls}">{text}</span></span>',
47804                  '</em></a></li>'
47805             );
47806             tt.disableFormats = true;
47807             tt.compile();
47808             Ext.TabPanel.prototype.itemTpl = tt;
47809         }
47810
47811         this.items.each(this.initTab, this);
47812     },
47813
47814     // private
47815     afterRender : function(){
47816         Ext.TabPanel.superclass.afterRender.call(this);
47817         if(this.autoTabs){
47818             this.readTabs(false);
47819         }
47820         if(this.activeTab !== undefined){
47821             var item = Ext.isObject(this.activeTab) ? this.activeTab : this.items.get(this.activeTab);
47822             delete this.activeTab;
47823             this.setActiveTab(item);
47824         }
47825     },
47826
47827     // private
47828     initEvents : function(){
47829         Ext.TabPanel.superclass.initEvents.call(this);
47830         this.mon(this.strip, {
47831             scope: this,
47832             mousedown: this.onStripMouseDown,
47833             contextmenu: this.onStripContextMenu
47834         });
47835         if(this.enableTabScroll){
47836             this.mon(this.strip, 'mousewheel', this.onWheel, this);
47837         }
47838     },
47839
47840     // private
47841     findTargets : function(e){
47842         var item = null,
47843             itemEl = e.getTarget('li:not(.x-tab-edge)', this.strip);
47844
47845         if(itemEl){
47846             item = this.getComponent(itemEl.id.split(this.idDelimiter)[1]);
47847             if(item.disabled){
47848                 return {
47849                     close : null,
47850                     item : null,
47851                     el : null
47852                 };
47853             }
47854         }
47855         return {
47856             close : e.getTarget('.x-tab-strip-close', this.strip),
47857             item : item,
47858             el : itemEl
47859         };
47860     },
47861
47862     // private
47863     onStripMouseDown : function(e){
47864         if(e.button !== 0){
47865             return;
47866         }
47867         e.preventDefault();
47868         var t = this.findTargets(e);
47869         if(t.close){
47870             if (t.item.fireEvent('beforeclose', t.item) !== false) {
47871                 t.item.fireEvent('close', t.item);
47872                 this.remove(t.item);
47873             }
47874             return;
47875         }
47876         if(t.item && t.item != this.activeTab){
47877             this.setActiveTab(t.item);
47878         }
47879     },
47880
47881     // private
47882     onStripContextMenu : function(e){
47883         e.preventDefault();
47884         var t = this.findTargets(e);
47885         if(t.item){
47886             this.fireEvent('contextmenu', this, t.item, e);
47887         }
47888     },
47889
47890     /**
47891      * True to scan the markup in this tab panel for <tt>{@link #autoTabs}</tt> using the
47892      * <tt>{@link #autoTabSelector}</tt>
47893      * @param {Boolean} removeExisting True to remove existing tabs
47894      */
47895     readTabs : function(removeExisting){
47896         if(removeExisting === true){
47897             this.items.each(function(item){
47898                 this.remove(item);
47899             }, this);
47900         }
47901         var tabs = this.el.query(this.autoTabSelector);
47902         for(var i = 0, len = tabs.length; i < len; i++){
47903             var tab = tabs[i],
47904                 title = tab.getAttribute('title');
47905             tab.removeAttribute('title');
47906             this.add({
47907                 title: title,
47908                 contentEl: tab
47909             });
47910         }
47911     },
47912
47913     // private
47914     initTab : function(item, index){
47915         var before = this.strip.dom.childNodes[index],
47916             p = this.getTemplateArgs(item),
47917             el = before ?
47918                  this.itemTpl.insertBefore(before, p) :
47919                  this.itemTpl.append(this.strip, p),
47920             cls = 'x-tab-strip-over',
47921             tabEl = Ext.get(el);
47922
47923         tabEl.hover(function(){
47924             if(!item.disabled){
47925                 tabEl.addClass(cls);
47926             }
47927         }, function(){
47928             tabEl.removeClass(cls);
47929         });
47930
47931         if(item.tabTip){
47932             tabEl.child('span.x-tab-strip-text', true).qtip = item.tabTip;
47933         }
47934         item.tabEl = el;
47935
47936         // Route *keyboard triggered* click events to the tab strip mouse handler.
47937         tabEl.select('a').on('click', function(e){
47938             if(!e.getPageX()){
47939                 this.onStripMouseDown(e);
47940             }
47941         }, this, {preventDefault: true});
47942
47943         item.on({
47944             scope: this,
47945             disable: this.onItemDisabled,
47946             enable: this.onItemEnabled,
47947             titlechange: this.onItemTitleChanged,
47948             iconchange: this.onItemIconChanged,
47949             beforeshow: this.onBeforeShowItem
47950         });
47951     },
47952
47953
47954
47955     /**
47956      * <p>Provides template arguments for rendering a tab selector item in the tab strip.</p>
47957      * <p>This method returns an object hash containing properties used by the TabPanel's <tt>{@link #itemTpl}</tt>
47958      * to create a formatted, clickable tab selector element. The properties which must be returned
47959      * are:</p><div class="mdetail-params"><ul>
47960      * <li><b>id</b> : String<div class="sub-desc">A unique identifier which links to the item</div></li>
47961      * <li><b>text</b> : String<div class="sub-desc">The text to display</div></li>
47962      * <li><b>cls</b> : String<div class="sub-desc">The CSS class name</div></li>
47963      * <li><b>iconCls</b> : String<div class="sub-desc">A CSS class to provide appearance for an icon.</div></li>
47964      * </ul></div>
47965      * @param {Ext.BoxComponent} item The {@link Ext.BoxComponent BoxComponent} for which to create a selector element in the tab strip.
47966      * @return {Object} An object hash containing the properties required to render the selector element.
47967      */
47968     getTemplateArgs : function(item) {
47969         var cls = item.closable ? 'x-tab-strip-closable' : '';
47970         if(item.disabled){
47971             cls += ' x-item-disabled';
47972         }
47973         if(item.iconCls){
47974             cls += ' x-tab-with-icon';
47975         }
47976         if(item.tabCls){
47977             cls += ' ' + item.tabCls;
47978         }
47979
47980         return {
47981             id: this.id + this.idDelimiter + item.getItemId(),
47982             text: item.title,
47983             cls: cls,
47984             iconCls: item.iconCls || ''
47985         };
47986     },
47987
47988     // private
47989     onAdd : function(c){
47990         Ext.TabPanel.superclass.onAdd.call(this, c);
47991         if(this.rendered){
47992             var items = this.items;
47993             this.initTab(c, items.indexOf(c));
47994             this.delegateUpdates();
47995         }
47996     },
47997
47998     // private
47999     onBeforeAdd : function(item){
48000         var existing = item.events ? (this.items.containsKey(item.getItemId()) ? item : null) : this.items.get(item);
48001         if(existing){
48002             this.setActiveTab(item);
48003             return false;
48004         }
48005         Ext.TabPanel.superclass.onBeforeAdd.apply(this, arguments);
48006         var es = item.elements;
48007         item.elements = es ? es.replace(',header', '') : es;
48008         item.border = (item.border === true);
48009     },
48010
48011     // private
48012     onRemove : function(c){
48013         var te = Ext.get(c.tabEl);
48014         // check if the tabEl exists, it won't if the tab isn't rendered
48015         if(te){
48016             te.select('a').removeAllListeners();
48017             Ext.destroy(te);
48018         }
48019         Ext.TabPanel.superclass.onRemove.call(this, c);
48020         this.stack.remove(c);
48021         delete c.tabEl;
48022         c.un('disable', this.onItemDisabled, this);
48023         c.un('enable', this.onItemEnabled, this);
48024         c.un('titlechange', this.onItemTitleChanged, this);
48025         c.un('iconchange', this.onItemIconChanged, this);
48026         c.un('beforeshow', this.onBeforeShowItem, this);
48027         if(c == this.activeTab){
48028             var next = this.stack.next();
48029             if(next){
48030                 this.setActiveTab(next);
48031             }else if(this.items.getCount() > 0){
48032                 this.setActiveTab(0);
48033             }else{
48034                 this.setActiveTab(null);
48035             }
48036         }
48037         if(!this.destroying){
48038             this.delegateUpdates();
48039         }
48040     },
48041
48042     // private
48043     onBeforeShowItem : function(item){
48044         if(item != this.activeTab){
48045             this.setActiveTab(item);
48046             return false;
48047         }
48048     },
48049
48050     // private
48051     onItemDisabled : function(item){
48052         var el = this.getTabEl(item);
48053         if(el){
48054             Ext.fly(el).addClass('x-item-disabled');
48055         }
48056         this.stack.remove(item);
48057     },
48058
48059     // private
48060     onItemEnabled : function(item){
48061         var el = this.getTabEl(item);
48062         if(el){
48063             Ext.fly(el).removeClass('x-item-disabled');
48064         }
48065     },
48066
48067     // private
48068     onItemTitleChanged : function(item){
48069         var el = this.getTabEl(item);
48070         if(el){
48071             Ext.fly(el).child('span.x-tab-strip-text', true).innerHTML = item.title;
48072         }
48073     },
48074
48075     //private
48076     onItemIconChanged : function(item, iconCls, oldCls){
48077         var el = this.getTabEl(item);
48078         if(el){
48079             el = Ext.get(el);
48080             el.child('span.x-tab-strip-text').replaceClass(oldCls, iconCls);
48081             el[Ext.isEmpty(iconCls) ? 'removeClass' : 'addClass']('x-tab-with-icon');
48082         }
48083     },
48084
48085     /**
48086      * Gets the DOM element for the tab strip item which activates the child panel with the specified
48087      * ID. Access this to change the visual treatment of the item, for example by changing the CSS class name.
48088      * @param {Panel/Number/String} tab The tab component, or the tab's index, or the tabs id or itemId.
48089      * @return {HTMLElement} The DOM node
48090      */
48091     getTabEl : function(item){
48092         var c = this.getComponent(item);
48093         return c ? c.tabEl : null;
48094     },
48095
48096     // private
48097     onResize : function(){
48098         Ext.TabPanel.superclass.onResize.apply(this, arguments);
48099         this.delegateUpdates();
48100     },
48101
48102     /**
48103      * Suspends any internal calculations or scrolling while doing a bulk operation. See {@link #endUpdate}
48104      */
48105     beginUpdate : function(){
48106         this.suspendUpdates = true;
48107     },
48108
48109     /**
48110      * Resumes calculations and scrolling at the end of a bulk operation. See {@link #beginUpdate}
48111      */
48112     endUpdate : function(){
48113         this.suspendUpdates = false;
48114         this.delegateUpdates();
48115     },
48116
48117     /**
48118      * Hides the tab strip item for the passed tab
48119      * @param {Number/String/Panel} item The tab index, id or item
48120      */
48121     hideTabStripItem : function(item){
48122         item = this.getComponent(item);
48123         var el = this.getTabEl(item);
48124         if(el){
48125             el.style.display = 'none';
48126             this.delegateUpdates();
48127         }
48128         this.stack.remove(item);
48129     },
48130
48131     /**
48132      * Unhides the tab strip item for the passed tab
48133      * @param {Number/String/Panel} item The tab index, id or item
48134      */
48135     unhideTabStripItem : function(item){
48136         item = this.getComponent(item);
48137         var el = this.getTabEl(item);
48138         if(el){
48139             el.style.display = '';
48140             this.delegateUpdates();
48141         }
48142     },
48143
48144     // private
48145     delegateUpdates : function(){
48146         var rendered = this.rendered;
48147         if(this.suspendUpdates){
48148             return;
48149         }
48150         if(this.resizeTabs && rendered){
48151             this.autoSizeTabs();
48152         }
48153         if(this.enableTabScroll && rendered){
48154             this.autoScrollTabs();
48155         }
48156     },
48157
48158     // private
48159     autoSizeTabs : function(){
48160         var count = this.items.length,
48161             ce = this.tabPosition != 'bottom' ? 'header' : 'footer',
48162             ow = this[ce].dom.offsetWidth,
48163             aw = this[ce].dom.clientWidth;
48164
48165         if(!this.resizeTabs || count < 1 || !aw){ // !aw for display:none
48166             return;
48167         }
48168
48169         var each = Math.max(Math.min(Math.floor((aw-4) / count) - this.tabMargin, this.tabWidth), this.minTabWidth); // -4 for float errors in IE
48170         this.lastTabWidth = each;
48171         var lis = this.strip.query('li:not(.x-tab-edge)');
48172         for(var i = 0, len = lis.length; i < len; i++) {
48173             var li = lis[i],
48174                 inner = Ext.fly(li).child('.x-tab-strip-inner', true),
48175                 tw = li.offsetWidth,
48176                 iw = inner.offsetWidth;
48177             inner.style.width = (each - (tw-iw)) + 'px';
48178         }
48179     },
48180
48181     // private
48182     adjustBodyWidth : function(w){
48183         if(this.header){
48184             this.header.setWidth(w);
48185         }
48186         if(this.footer){
48187             this.footer.setWidth(w);
48188         }
48189         return w;
48190     },
48191
48192     /**
48193      * Sets the specified tab as the active tab. This method fires the {@link #beforetabchange} event which
48194      * can <tt>return false</tt> to cancel the tab change.
48195      * @param {String/Number} item
48196      * The id or tab Panel to activate. This parameter may be any of the following:
48197      * <div><ul class="mdetail-params">
48198      * <li>a <b><tt>String</tt></b> : representing the <code>{@link Ext.Component#itemId itemId}</code>
48199      * or <code>{@link Ext.Component#id id}</code> of the child component </li>
48200      * <li>a <b><tt>Number</tt></b> : representing the position of the child component
48201      * within the <code>{@link Ext.Container#items items}</code> <b>property</b></li>
48202      * </ul></div>
48203      * <p>For additional information see {@link Ext.util.MixedCollection#get}.
48204      */
48205     setActiveTab : function(item){
48206         item = this.getComponent(item);
48207         if(this.fireEvent('beforetabchange', this, item, this.activeTab) === false){
48208             return;
48209         }
48210         if(!this.rendered){
48211             this.activeTab = item;
48212             return;
48213         }
48214         if(this.activeTab != item){
48215             if(this.activeTab){
48216                 var oldEl = this.getTabEl(this.activeTab);
48217                 if(oldEl){
48218                     Ext.fly(oldEl).removeClass('x-tab-strip-active');
48219                 }
48220             }
48221             this.activeTab = item;
48222             if(item){
48223                 var el = this.getTabEl(item);
48224                 Ext.fly(el).addClass('x-tab-strip-active');
48225                 this.stack.add(item);
48226
48227                 this.layout.setActiveItem(item);
48228                 // Need to do this here, since setting the active tab slightly changes the size
48229                 this.delegateUpdates();
48230                 if(this.scrolling){
48231                     this.scrollToTab(item, this.animScroll);
48232                 }
48233             }
48234             this.fireEvent('tabchange', this, item);
48235         }
48236     },
48237
48238     /**
48239      * Returns the Component which is the currently active tab. <b>Note that before the TabPanel
48240      * first activates a child Component, this method will return whatever was configured in the
48241      * {@link #activeTab} config option.</b>
48242      * @return {BoxComponent} The currently active child Component if one <i>is</i> active, or the {@link #activeTab} config value.
48243      */
48244     getActiveTab : function(){
48245         return this.activeTab || null;
48246     },
48247
48248     /**
48249      * Gets the specified tab by id.
48250      * @param {String} id The tab id
48251      * @return {Panel} The tab
48252      */
48253     getItem : function(item){
48254         return this.getComponent(item);
48255     },
48256
48257     // private
48258     autoScrollTabs : function(){
48259         this.pos = this.tabPosition=='bottom' ? this.footer : this.header;
48260         var count = this.items.length,
48261             ow = this.pos.dom.offsetWidth,
48262             tw = this.pos.dom.clientWidth,
48263             wrap = this.stripWrap,
48264             wd = wrap.dom,
48265             cw = wd.offsetWidth,
48266             pos = this.getScrollPos(),
48267             l = this.edge.getOffsetsTo(this.stripWrap)[0] + pos;
48268
48269         if(!this.enableTabScroll || cw < 20){ // 20 to prevent display:none issues
48270             return;
48271         }
48272         if(count == 0 || l <= tw){
48273             // ensure the width is set if there's no tabs
48274             wd.scrollLeft = 0;
48275             wrap.setWidth(tw);
48276             if(this.scrolling){
48277                 this.scrolling = false;
48278                 this.pos.removeClass('x-tab-scrolling');
48279                 this.scrollLeft.hide();
48280                 this.scrollRight.hide();
48281                 // See here: http://extjs.com/forum/showthread.php?t=49308&highlight=isSafari
48282                 if(Ext.isAir || Ext.isWebKit){
48283                     wd.style.marginLeft = '';
48284                     wd.style.marginRight = '';
48285                 }
48286             }
48287         }else{
48288             if(!this.scrolling){
48289                 this.pos.addClass('x-tab-scrolling');
48290                 // See here: http://extjs.com/forum/showthread.php?t=49308&highlight=isSafari
48291                 if(Ext.isAir || Ext.isWebKit){
48292                     wd.style.marginLeft = '18px';
48293                     wd.style.marginRight = '18px';
48294                 }
48295             }
48296             tw -= wrap.getMargins('lr');
48297             wrap.setWidth(tw > 20 ? tw : 20);
48298             if(!this.scrolling){
48299                 if(!this.scrollLeft){
48300                     this.createScrollers();
48301                 }else{
48302                     this.scrollLeft.show();
48303                     this.scrollRight.show();
48304                 }
48305             }
48306             this.scrolling = true;
48307             if(pos > (l-tw)){ // ensure it stays within bounds
48308                 wd.scrollLeft = l-tw;
48309             }else{ // otherwise, make sure the active tab is still visible
48310                 this.scrollToTab(this.activeTab, false);
48311             }
48312             this.updateScrollButtons();
48313         }
48314     },
48315
48316     // private
48317     createScrollers : function(){
48318         this.pos.addClass('x-tab-scrolling-' + this.tabPosition);
48319         var h = this.stripWrap.dom.offsetHeight;
48320
48321         // left
48322         var sl = this.pos.insertFirst({
48323             cls:'x-tab-scroller-left'
48324         });
48325         sl.setHeight(h);
48326         sl.addClassOnOver('x-tab-scroller-left-over');
48327         this.leftRepeater = new Ext.util.ClickRepeater(sl, {
48328             interval : this.scrollRepeatInterval,
48329             handler: this.onScrollLeft,
48330             scope: this
48331         });
48332         this.scrollLeft = sl;
48333
48334         // right
48335         var sr = this.pos.insertFirst({
48336             cls:'x-tab-scroller-right'
48337         });
48338         sr.setHeight(h);
48339         sr.addClassOnOver('x-tab-scroller-right-over');
48340         this.rightRepeater = new Ext.util.ClickRepeater(sr, {
48341             interval : this.scrollRepeatInterval,
48342             handler: this.onScrollRight,
48343             scope: this
48344         });
48345         this.scrollRight = sr;
48346     },
48347
48348     // private
48349     getScrollWidth : function(){
48350         return this.edge.getOffsetsTo(this.stripWrap)[0] + this.getScrollPos();
48351     },
48352
48353     // private
48354     getScrollPos : function(){
48355         return parseInt(this.stripWrap.dom.scrollLeft, 10) || 0;
48356     },
48357
48358     // private
48359     getScrollArea : function(){
48360         return parseInt(this.stripWrap.dom.clientWidth, 10) || 0;
48361     },
48362
48363     // private
48364     getScrollAnim : function(){
48365         return {duration:this.scrollDuration, callback: this.updateScrollButtons, scope: this};
48366     },
48367
48368     // private
48369     getScrollIncrement : function(){
48370         return this.scrollIncrement || (this.resizeTabs ? this.lastTabWidth+2 : 100);
48371     },
48372
48373     /**
48374      * Scrolls to a particular tab if tab scrolling is enabled
48375      * @param {Panel} item The item to scroll to
48376      * @param {Boolean} animate True to enable animations
48377      */
48378
48379     scrollToTab : function(item, animate){
48380         if(!item){
48381             return;
48382         }
48383         var el = this.getTabEl(item),
48384             pos = this.getScrollPos(),
48385             area = this.getScrollArea(),
48386             left = Ext.fly(el).getOffsetsTo(this.stripWrap)[0] + pos,
48387             right = left + el.offsetWidth;
48388         if(left < pos){
48389             this.scrollTo(left, animate);
48390         }else if(right > (pos + area)){
48391             this.scrollTo(right - area, animate);
48392         }
48393     },
48394
48395     // private
48396     scrollTo : function(pos, animate){
48397         this.stripWrap.scrollTo('left', pos, animate ? this.getScrollAnim() : false);
48398         if(!animate){
48399             this.updateScrollButtons();
48400         }
48401     },
48402
48403     onWheel : function(e){
48404         var d = e.getWheelDelta()*this.wheelIncrement*-1;
48405         e.stopEvent();
48406
48407         var pos = this.getScrollPos(),
48408             newpos = pos + d,
48409             sw = this.getScrollWidth()-this.getScrollArea();
48410
48411         var s = Math.max(0, Math.min(sw, newpos));
48412         if(s != pos){
48413             this.scrollTo(s, false);
48414         }
48415     },
48416
48417     // private
48418     onScrollRight : function(){
48419         var sw = this.getScrollWidth()-this.getScrollArea(),
48420             pos = this.getScrollPos(),
48421             s = Math.min(sw, pos + this.getScrollIncrement());
48422         if(s != pos){
48423             this.scrollTo(s, this.animScroll);
48424         }
48425     },
48426
48427     // private
48428     onScrollLeft : function(){
48429         var pos = this.getScrollPos(),
48430             s = Math.max(0, pos - this.getScrollIncrement());
48431         if(s != pos){
48432             this.scrollTo(s, this.animScroll);
48433         }
48434     },
48435
48436     // private
48437     updateScrollButtons : function(){
48438         var pos = this.getScrollPos();
48439         this.scrollLeft[pos === 0 ? 'addClass' : 'removeClass']('x-tab-scroller-left-disabled');
48440         this.scrollRight[pos >= (this.getScrollWidth()-this.getScrollArea()) ? 'addClass' : 'removeClass']('x-tab-scroller-right-disabled');
48441     },
48442
48443     // private
48444     beforeDestroy : function() {
48445         Ext.destroy(this.leftRepeater, this.rightRepeater);
48446         this.deleteMembers('strip', 'edge', 'scrollLeft', 'scrollRight', 'stripWrap');
48447         this.activeTab = null;
48448         Ext.TabPanel.superclass.beforeDestroy.apply(this);
48449     }
48450
48451     /**
48452      * @cfg {Boolean} collapsible
48453      * @hide
48454      */
48455     /**
48456      * @cfg {String} header
48457      * @hide
48458      */
48459     /**
48460      * @cfg {Boolean} headerAsText
48461      * @hide
48462      */
48463     /**
48464      * @property header
48465      * @hide
48466      */
48467     /**
48468      * @cfg title
48469      * @hide
48470      */
48471     /**
48472      * @cfg {Array} tools
48473      * @hide
48474      */
48475     /**
48476      * @cfg {Array} toolTemplate
48477      * @hide
48478      */
48479     /**
48480      * @cfg {Boolean} hideCollapseTool
48481      * @hide
48482      */
48483     /**
48484      * @cfg {Boolean} titleCollapse
48485      * @hide
48486      */
48487     /**
48488      * @cfg {Boolean} collapsed
48489      * @hide
48490      */
48491     /**
48492      * @cfg {String} layout
48493      * @hide
48494      */
48495     /**
48496      * @cfg {Boolean} preventBodyReset
48497      * @hide
48498      */
48499 });
48500 Ext.reg('tabpanel', Ext.TabPanel);
48501
48502 /**
48503  * See {@link #setActiveTab}. Sets the specified tab as the active tab. This method fires
48504  * the {@link #beforetabchange} event which can <tt>return false</tt> to cancel the tab change.
48505  * @param {String/Panel} tab The id or tab Panel to activate
48506  * @method activate
48507  */
48508 Ext.TabPanel.prototype.activate = Ext.TabPanel.prototype.setActiveTab;
48509
48510 // private utility class used by TabPanel
48511 Ext.TabPanel.AccessStack = function(){
48512     var items = [];
48513     return {
48514         add : function(item){
48515             items.push(item);
48516             if(items.length > 10){
48517                 items.shift();
48518             }
48519         },
48520
48521         remove : function(item){
48522             var s = [];
48523             for(var i = 0, len = items.length; i < len; i++) {
48524                 if(items[i] != item){
48525                     s.push(items[i]);
48526                 }
48527             }
48528             items = s;
48529         },
48530
48531         next : function(){
48532             return items.pop();
48533         }
48534     };
48535 };
48536 /**
48537  * @class Ext.Button
48538  * @extends Ext.BoxComponent
48539  * Simple Button class
48540  * @cfg {String} text The button text to be used as innerHTML (html tags are accepted)
48541  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
48542  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:'x-btn-text-icon')
48543  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event).
48544  * The handler is passed the following parameters:<div class="mdetail-params"><ul>
48545  * <li><code>b</code> : Button<div class="sub-desc">This Button.</div></li>
48546  * <li><code>e</code> : EventObject<div class="sub-desc">The click event.</div></li>
48547  * </ul></div>
48548  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width).
48549  * See also {@link Ext.Panel}.<tt>{@link Ext.Panel#minButtonWidth minButtonWidth}</tt>.
48550  * @cfg {String/Object} tooltip The tooltip for the button - can be a string to be used as innerHTML (html tags are accepted) or QuickTips config object
48551  * @cfg {Boolean} hidden True to start hidden (defaults to false)
48552  * @cfg {Boolean} disabled True to start disabled (defaults to false)
48553  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
48554  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed)
48555  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
48556  * a {@link Ext.util.ClickRepeater ClickRepeater} config object (defaults to false).
48557  * @constructor
48558  * Create a new button
48559  * @param {Object} config The config object
48560  * @xtype button
48561  */
48562 Ext.Button = Ext.extend(Ext.BoxComponent, {
48563     /**
48564      * Read-only. True if this button is hidden
48565      * @type Boolean
48566      */
48567     hidden : false,
48568     /**
48569      * Read-only. True if this button is disabled
48570      * @type Boolean
48571      */
48572     disabled : false,
48573     /**
48574      * Read-only. True if this button is pressed (only if enableToggle = true)
48575      * @type Boolean
48576      */
48577     pressed : false,
48578
48579     /**
48580      * @cfg {Number} tabIndex Set a DOM tabIndex for this button (defaults to undefined)
48581      */
48582
48583     /**
48584      * @cfg {Boolean} allowDepress
48585      * False to not allow a pressed Button to be depressed (defaults to undefined). Only valid when {@link #enableToggle} is true.
48586      */
48587
48588     /**
48589      * @cfg {Boolean} enableToggle
48590      * True to enable pressed/not pressed toggling (defaults to false)
48591      */
48592     enableToggle : false,
48593     /**
48594      * @cfg {Function} toggleHandler
48595      * Function called when a Button with {@link #enableToggle} set to true is clicked. Two arguments are passed:<ul class="mdetail-params">
48596      * <li><b>button</b> : Ext.Button<div class="sub-desc">this Button object</div></li>
48597      * <li><b>state</b> : Boolean<div class="sub-desc">The next state of the Button, true means pressed.</div></li>
48598      * </ul>
48599      */
48600     /**
48601      * @cfg {Mixed} menu
48602      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
48603      */
48604     /**
48605      * @cfg {String} menuAlign
48606      * The position to align the menu to (see {@link Ext.Element#alignTo} for more details, defaults to 'tl-bl?').
48607      */
48608     menuAlign : 'tl-bl?',
48609
48610     /**
48611      * @cfg {String} overflowText If used in a {@link Ext.Toolbar Toolbar}, the
48612      * text to be used if this item is shown in the overflow menu. See also
48613      * {@link Ext.Toolbar.Item}.<code>{@link Ext.Toolbar.Item#overflowText overflowText}</code>.
48614      */
48615     /**
48616      * @cfg {String} iconCls
48617      * A css class which sets a background image to be used as the icon for this button
48618      */
48619     /**
48620      * @cfg {String} type
48621      * submit, reset or button - defaults to 'button'
48622      */
48623     type : 'button',
48624
48625     // private
48626     menuClassTarget : 'tr:nth(2)',
48627
48628     /**
48629      * @cfg {String} clickEvent
48630      * The DOM event that will fire the handler of the button. This can be any valid event name (dblclick, contextmenu).
48631      * Defaults to <tt>'click'</tt>.
48632      */
48633     clickEvent : 'click',
48634
48635     /**
48636      * @cfg {Boolean} handleMouseEvents
48637      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
48638      */
48639     handleMouseEvents : true,
48640
48641     /**
48642      * @cfg {String} tooltipType
48643      * The type of tooltip to use. Either 'qtip' (default) for QuickTips or 'title' for title attribute.
48644      */
48645     tooltipType : 'qtip',
48646
48647     /**
48648      * @cfg {String} buttonSelector
48649      * <p>(Optional) A {@link Ext.DomQuery DomQuery} selector which is used to extract the active, clickable element from the
48650      * DOM structure created.</p>
48651      * <p>When a custom {@link #template} is used, you  must ensure that this selector results in the selection of
48652      * a focussable element.</p>
48653      * <p>Defaults to <b><tt>'button:first-child'</tt></b>.</p>
48654      */
48655     buttonSelector : 'button:first-child',
48656
48657     /**
48658      * @cfg {String} scale
48659      * <p>(Optional) The size of the Button. Three values are allowed:</p>
48660      * <ul class="mdetail-params">
48661      * <li>'small'<div class="sub-desc">Results in the button element being 16px high.</div></li>
48662      * <li>'medium'<div class="sub-desc">Results in the button element being 24px high.</div></li>
48663      * <li>'large'<div class="sub-desc">Results in the button element being 32px high.</div></li>
48664      * </ul>
48665      * <p>Defaults to <b><tt>'small'</tt></b>.</p>
48666      */
48667     scale : 'small',
48668
48669     /**
48670      * @cfg {Object} scope The scope (<tt><b>this</b></tt> reference) in which the
48671      * <code>{@link #handler}</code> and <code>{@link #toggleHandler}</code> is
48672      * executed. Defaults to this Button.
48673      */
48674
48675     /**
48676      * @cfg {String} iconAlign
48677      * <p>(Optional) The side of the Button box to render the icon. Four values are allowed:</p>
48678      * <ul class="mdetail-params">
48679      * <li>'top'<div class="sub-desc"></div></li>
48680      * <li>'right'<div class="sub-desc"></div></li>
48681      * <li>'bottom'<div class="sub-desc"></div></li>
48682      * <li>'left'<div class="sub-desc"></div></li>
48683      * </ul>
48684      * <p>Defaults to <b><tt>'left'</tt></b>.</p>
48685      */
48686     iconAlign : 'left',
48687
48688     /**
48689      * @cfg {String} arrowAlign
48690      * <p>(Optional) The side of the Button box to render the arrow if the button has an associated {@link #menu}.
48691      * Two values are allowed:</p>
48692      * <ul class="mdetail-params">
48693      * <li>'right'<div class="sub-desc"></div></li>
48694      * <li>'bottom'<div class="sub-desc"></div></li>
48695      * </ul>
48696      * <p>Defaults to <b><tt>'right'</tt></b>.</p>
48697      */
48698     arrowAlign : 'right',
48699
48700     /**
48701      * @cfg {Ext.Template} template (Optional)
48702      * <p>A {@link Ext.Template Template} used to create the Button's DOM structure.</p>
48703      * Instances, or subclasses which need a different DOM structure may provide a different
48704      * template layout in conjunction with an implementation of {@link #getTemplateArgs}.
48705      * @type Ext.Template
48706      * @property template
48707      */
48708     /**
48709      * @cfg {String} cls
48710      * A CSS class string to apply to the button's main element.
48711      */
48712     /**
48713      * @property menu
48714      * @type Menu
48715      * The {@link Ext.menu.Menu Menu} object associated with this Button when configured with the {@link #menu} config option.
48716      */
48717     /**
48718      * @cfg {Boolean} autoWidth
48719      * By default, if a width is not specified the button will attempt to stretch horizontally to fit its content.
48720      * If the button is being managed by a width sizing layout (hbox, fit, anchor), set this to false to prevent
48721      * the button from doing this automatic sizing.
48722      * Defaults to <tt>undefined</tt>.
48723      */
48724
48725     initComponent : function(){
48726         if(this.menu){
48727             this.menu = Ext.menu.MenuMgr.get(this.menu);
48728             this.menu.ownerCt = this;
48729         }
48730         
48731         Ext.Button.superclass.initComponent.call(this);
48732
48733         this.addEvents(
48734             /**
48735              * @event click
48736              * Fires when this button is clicked
48737              * @param {Button} this
48738              * @param {EventObject} e The click event
48739              */
48740             'click',
48741             /**
48742              * @event toggle
48743              * Fires when the 'pressed' state of this button changes (only if enableToggle = true)
48744              * @param {Button} this
48745              * @param {Boolean} pressed
48746              */
48747             'toggle',
48748             /**
48749              * @event mouseover
48750              * Fires when the mouse hovers over the button
48751              * @param {Button} this
48752              * @param {Event} e The event object
48753              */
48754             'mouseover',
48755             /**
48756              * @event mouseout
48757              * Fires when the mouse exits the button
48758              * @param {Button} this
48759              * @param {Event} e The event object
48760              */
48761             'mouseout',
48762             /**
48763              * @event menushow
48764              * If this button has a menu, this event fires when it is shown
48765              * @param {Button} this
48766              * @param {Menu} menu
48767              */
48768             'menushow',
48769             /**
48770              * @event menuhide
48771              * If this button has a menu, this event fires when it is hidden
48772              * @param {Button} this
48773              * @param {Menu} menu
48774              */
48775             'menuhide',
48776             /**
48777              * @event menutriggerover
48778              * If this button has a menu, this event fires when the mouse enters the menu triggering element
48779              * @param {Button} this
48780              * @param {Menu} menu
48781              * @param {EventObject} e
48782              */
48783             'menutriggerover',
48784             /**
48785              * @event menutriggerout
48786              * If this button has a menu, this event fires when the mouse leaves the menu triggering element
48787              * @param {Button} this
48788              * @param {Menu} menu
48789              * @param {EventObject} e
48790              */
48791             'menutriggerout'
48792         );
48793         
48794         if (this.menu){
48795             this.menu.ownerCt = undefined;
48796         }
48797         if(Ext.isString(this.toggleGroup)){
48798             this.enableToggle = true;
48799         }
48800     },
48801
48802 /**
48803   * <p>This method returns an Array which provides substitution parameters for the {@link #template Template} used
48804   * to create this Button's DOM structure.</p>
48805   * <p>Instances or subclasses which use a different Template to create a different DOM structure may need to provide their
48806   * own implementation of this method.</p>
48807   * <p>The default implementation which provides data for the default {@link #template} returns an Array containing the
48808   * following items:</p><div class="mdetail-params"><ul>
48809   * <li>The &lt;button&gt;'s {@link #type}</li>
48810   * <li>A CSS class name applied to the Button's main &lt;tbody&gt; element which determines the button's scale and icon alignment.</li>
48811   * <li>A CSS class to determine the presence and position of an arrow icon. (<code>'x-btn-arrow'</code> or <code>'x-btn-arrow-bottom'</code> or <code>''</code>)</li>
48812   * <li>The {@link #cls} CSS class name applied to the button's wrapping &lt;table&gt; element.</li>
48813   * <li>The Component id which is applied to the button's wrapping &lt;table&gt; element.</li>
48814   * </ul></div>
48815   * @return {Array} Substitution data for a Template.
48816  */
48817     getTemplateArgs : function(){
48818         return [this.type, 'x-btn-' + this.scale + ' x-btn-icon-' + this.scale + '-' + this.iconAlign, this.getMenuClass(), this.cls, this.id];
48819     },
48820
48821     // private
48822     setButtonClass : function(){
48823         if(this.useSetClass){
48824             if(!Ext.isEmpty(this.oldCls)){
48825                 this.el.removeClass([this.oldCls, 'x-btn-pressed']);
48826             }
48827             this.oldCls = (this.iconCls || this.icon) ? (this.text ? 'x-btn-text-icon' : 'x-btn-icon') : 'x-btn-noicon';
48828             this.el.addClass([this.oldCls, this.pressed ? 'x-btn-pressed' : null]);
48829         }
48830     },
48831
48832     // protected
48833     getMenuClass : function(){
48834         return this.menu ? (this.arrowAlign != 'bottom' ? 'x-btn-arrow' : 'x-btn-arrow-bottom') : '';
48835     },
48836
48837     // private
48838     onRender : function(ct, position){
48839         if(!this.template){
48840             if(!Ext.Button.buttonTemplate){
48841                 // hideous table template
48842                 Ext.Button.buttonTemplate = new Ext.Template(
48843                     '<table id="{4}" cellspacing="0" class="x-btn {3}"><tbody class="{1}">',
48844                     '<tr><td class="x-btn-tl"><i>&#160;</i></td><td class="x-btn-tc"></td><td class="x-btn-tr"><i>&#160;</i></td></tr>',
48845                     '<tr><td class="x-btn-ml"><i>&#160;</i></td><td class="x-btn-mc"><em class="{2}" unselectable="on"><button type="{0}"></button></em></td><td class="x-btn-mr"><i>&#160;</i></td></tr>',
48846                     '<tr><td class="x-btn-bl"><i>&#160;</i></td><td class="x-btn-bc"></td><td class="x-btn-br"><i>&#160;</i></td></tr>',
48847                     '</tbody></table>');
48848                 Ext.Button.buttonTemplate.compile();
48849             }
48850             this.template = Ext.Button.buttonTemplate;
48851         }
48852
48853         var btn, targs = this.getTemplateArgs();
48854
48855         if(position){
48856             btn = this.template.insertBefore(position, targs, true);
48857         }else{
48858             btn = this.template.append(ct, targs, true);
48859         }
48860         /**
48861          * An {@link Ext.Element Element} encapsulating the Button's clickable element. By default,
48862          * this references a <tt>&lt;button&gt;</tt> element. Read only.
48863          * @type Ext.Element
48864          * @property btnEl
48865          */
48866         this.btnEl = btn.child(this.buttonSelector);
48867         this.mon(this.btnEl, {
48868             scope: this,
48869             focus: this.onFocus,
48870             blur: this.onBlur
48871         });
48872
48873         this.initButtonEl(btn, this.btnEl);
48874
48875         Ext.ButtonToggleMgr.register(this);
48876     },
48877
48878     // private
48879     initButtonEl : function(btn, btnEl){
48880         this.el = btn;
48881         this.setIcon(this.icon);
48882         this.setText(this.text);
48883         this.setIconClass(this.iconCls);
48884         if(Ext.isDefined(this.tabIndex)){
48885             btnEl.dom.tabIndex = this.tabIndex;
48886         }
48887         if(this.tooltip){
48888             this.setTooltip(this.tooltip, true);
48889         }
48890
48891         if(this.handleMouseEvents){
48892             this.mon(btn, {
48893                 scope: this,
48894                 mouseover: this.onMouseOver,
48895                 mousedown: this.onMouseDown
48896             });
48897
48898             // new functionality for monitoring on the document level
48899             //this.mon(btn, 'mouseout', this.onMouseOut, this);
48900         }
48901
48902         if(this.menu){
48903             this.mon(this.menu, {
48904                 scope: this,
48905                 show: this.onMenuShow,
48906                 hide: this.onMenuHide
48907             });
48908         }
48909
48910         if(this.repeat){
48911             var repeater = new Ext.util.ClickRepeater(btn, Ext.isObject(this.repeat) ? this.repeat : {});
48912             this.mon(repeater, 'click', this.onRepeatClick, this);
48913         }else{
48914             this.mon(btn, this.clickEvent, this.onClick, this);
48915         }
48916     },
48917
48918     // private
48919     afterRender : function(){
48920         Ext.Button.superclass.afterRender.call(this);
48921         this.useSetClass = true;
48922         this.setButtonClass();
48923         this.doc = Ext.getDoc();
48924         this.doAutoWidth();
48925     },
48926
48927     /**
48928      * Sets the CSS class that provides a background image to use as the button's icon.  This method also changes
48929      * the value of the {@link iconCls} config internally.
48930      * @param {String} cls The CSS class providing the icon image
48931      * @return {Ext.Button} this
48932      */
48933     setIconClass : function(cls){
48934         this.iconCls = cls;
48935         if(this.el){
48936             this.btnEl.dom.className = '';
48937             this.btnEl.addClass(['x-btn-text', cls || '']);
48938             this.setButtonClass();
48939         }
48940         return this;
48941     },
48942
48943     /**
48944      * Sets the tooltip for this Button.
48945      * @param {String/Object} tooltip. This may be:<div class="mdesc-details"><ul>
48946      * <li><b>String</b> : A string to be used as innerHTML (html tags are accepted) to show in a tooltip</li>
48947      * <li><b>Object</b> : A configuration object for {@link Ext.QuickTips#register}.</li>
48948      * </ul></div>
48949      * @return {Ext.Button} this
48950      */
48951     setTooltip : function(tooltip, /* private */ initial){
48952         if(this.rendered){
48953             if(!initial){
48954                 this.clearTip();
48955             }
48956             if(Ext.isObject(tooltip)){
48957                 Ext.QuickTips.register(Ext.apply({
48958                       target: this.btnEl.id
48959                 }, tooltip));
48960                 this.tooltip = tooltip;
48961             }else{
48962                 this.btnEl.dom[this.tooltipType] = tooltip;
48963             }
48964         }else{
48965             this.tooltip = tooltip;
48966         }
48967         return this;
48968     },
48969
48970     // private
48971     clearTip : function(){
48972         if(Ext.isObject(this.tooltip)){
48973             Ext.QuickTips.unregister(this.btnEl);
48974         }
48975     },
48976
48977     // private
48978     beforeDestroy : function(){
48979         if(this.rendered){
48980             this.clearTip();
48981         }
48982         if(this.menu && this.destroyMenu !== false) {
48983             Ext.destroy(this.btnEl, this.menu);
48984         }
48985         Ext.destroy(this.repeater);
48986     },
48987
48988     // private
48989     onDestroy : function(){
48990         if(this.rendered){
48991             this.doc.un('mouseover', this.monitorMouseOver, this);
48992             this.doc.un('mouseup', this.onMouseUp, this);
48993             delete this.doc;
48994             delete this.btnEl;
48995             Ext.ButtonToggleMgr.unregister(this);
48996         }
48997         Ext.Button.superclass.onDestroy.call(this);
48998     },
48999
49000     // private
49001     doAutoWidth : function(){
49002         if(this.autoWidth !== false && this.el && this.text && this.width === undefined){
49003             this.el.setWidth('auto');
49004             if(Ext.isIE7 && Ext.isStrict){
49005                 var ib = this.btnEl;
49006                 if(ib && ib.getWidth() > 20){
49007                     ib.clip();
49008                     ib.setWidth(Ext.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
49009                 }
49010             }
49011             if(this.minWidth){
49012                 if(this.el.getWidth() < this.minWidth){
49013                     this.el.setWidth(this.minWidth);
49014                 }
49015             }
49016         }
49017     },
49018
49019     /**
49020      * Assigns this Button's click handler
49021      * @param {Function} handler The function to call when the button is clicked
49022      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function is executed.
49023      * Defaults to this Button.
49024      * @return {Ext.Button} this
49025      */
49026     setHandler : function(handler, scope){
49027         this.handler = handler;
49028         this.scope = scope;
49029         return this;
49030     },
49031
49032     /**
49033      * Sets this Button's text
49034      * @param {String} text The button text
49035      * @return {Ext.Button} this
49036      */
49037     setText : function(text){
49038         this.text = text;
49039         if(this.el){
49040             this.btnEl.update(text || '&#160;');
49041             this.setButtonClass();
49042         }
49043         this.doAutoWidth();
49044         return this;
49045     },
49046
49047     /**
49048      * Sets the background image (inline style) of the button.  This method also changes
49049      * the value of the {@link icon} config internally.
49050      * @param {String} icon The path to an image to display in the button
49051      * @return {Ext.Button} this
49052      */
49053     setIcon : function(icon){
49054         this.icon = icon;
49055         if(this.el){
49056             this.btnEl.setStyle('background-image', icon ? 'url(' + icon + ')' : '');
49057             this.setButtonClass();
49058         }
49059         return this;
49060     },
49061
49062     /**
49063      * Gets the text for this Button
49064      * @return {String} The button text
49065      */
49066     getText : function(){
49067         return this.text;
49068     },
49069
49070     /**
49071      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
49072      * @param {Boolean} state (optional) Force a particular state
49073      * @param {Boolean} supressEvent (optional) True to stop events being fired when calling this method.
49074      * @return {Ext.Button} this
49075      */
49076     toggle : function(state, suppressEvent){
49077         state = state === undefined ? !this.pressed : !!state;
49078         if(state != this.pressed){
49079             if(this.rendered){
49080                 this.el[state ? 'addClass' : 'removeClass']('x-btn-pressed');
49081             }
49082             this.pressed = state;
49083             if(!suppressEvent){
49084                 this.fireEvent('toggle', this, state);
49085                 if(this.toggleHandler){
49086                     this.toggleHandler.call(this.scope || this, this, state);
49087                 }
49088             }
49089         }
49090         return this;
49091     },
49092
49093     // private
49094     onDisable : function(){
49095         this.onDisableChange(true);
49096     },
49097
49098     // private
49099     onEnable : function(){
49100         this.onDisableChange(false);
49101     },
49102
49103     onDisableChange : function(disabled){
49104         if(this.el){
49105             if(!Ext.isIE6 || !this.text){
49106                 this.el[disabled ? 'addClass' : 'removeClass'](this.disabledClass);
49107             }
49108             this.el.dom.disabled = disabled;
49109         }
49110         this.disabled = disabled;
49111     },
49112
49113     /**
49114      * Show this button's menu (if it has one)
49115      */
49116     showMenu : function(){
49117         if(this.rendered && this.menu){
49118             if(this.tooltip){
49119                 Ext.QuickTips.getQuickTip().cancelShow(this.btnEl);
49120             }
49121             if(this.menu.isVisible()){
49122                 this.menu.hide();
49123             }
49124             this.menu.ownerCt = this;
49125             this.menu.show(this.el, this.menuAlign);
49126         }
49127         return this;
49128     },
49129
49130     /**
49131      * Hide this button's menu (if it has one)
49132      */
49133     hideMenu : function(){
49134         if(this.hasVisibleMenu()){
49135             this.menu.hide();
49136         }
49137         return this;
49138     },
49139
49140     /**
49141      * Returns true if the button has a menu and it is visible
49142      * @return {Boolean}
49143      */
49144     hasVisibleMenu : function(){
49145         return this.menu && this.menu.ownerCt == this && this.menu.isVisible();
49146     },
49147     
49148     // private
49149     onRepeatClick : function(repeat, e){
49150         this.onClick(e);
49151     },
49152
49153     // private
49154     onClick : function(e){
49155         if(e){
49156             e.preventDefault();
49157         }
49158         if(e.button !== 0){
49159             return;
49160         }
49161         if(!this.disabled){
49162             this.doToggle();
49163             if(this.menu && !this.hasVisibleMenu() && !this.ignoreNextClick){
49164                 this.showMenu();
49165             }
49166             this.fireEvent('click', this, e);
49167             if(this.handler){
49168                 //this.el.removeClass('x-btn-over');
49169                 this.handler.call(this.scope || this, this, e);
49170             }
49171         }
49172     },
49173     
49174     // private
49175     doToggle: function(){
49176         if (this.enableToggle && (this.allowDepress !== false || !this.pressed)) {
49177             this.toggle();
49178         }
49179     },
49180
49181     // private
49182     isMenuTriggerOver : function(e, internal){
49183         return this.menu && !internal;
49184     },
49185
49186     // private
49187     isMenuTriggerOut : function(e, internal){
49188         return this.menu && !internal;
49189     },
49190
49191     // private
49192     onMouseOver : function(e){
49193         if(!this.disabled){
49194             var internal = e.within(this.el,  true);
49195             if(!internal){
49196                 this.el.addClass('x-btn-over');
49197                 if(!this.monitoringMouseOver){
49198                     this.doc.on('mouseover', this.monitorMouseOver, this);
49199                     this.monitoringMouseOver = true;
49200                 }
49201                 this.fireEvent('mouseover', this, e);
49202             }
49203             if(this.isMenuTriggerOver(e, internal)){
49204                 this.fireEvent('menutriggerover', this, this.menu, e);
49205             }
49206         }
49207     },
49208
49209     // private
49210     monitorMouseOver : function(e){
49211         if(e.target != this.el.dom && !e.within(this.el)){
49212             if(this.monitoringMouseOver){
49213                 this.doc.un('mouseover', this.monitorMouseOver, this);
49214                 this.monitoringMouseOver = false;
49215             }
49216             this.onMouseOut(e);
49217         }
49218     },
49219
49220     // private
49221     onMouseOut : function(e){
49222         var internal = e.within(this.el) && e.target != this.el.dom;
49223         this.el.removeClass('x-btn-over');
49224         this.fireEvent('mouseout', this, e);
49225         if(this.isMenuTriggerOut(e, internal)){
49226             this.fireEvent('menutriggerout', this, this.menu, e);
49227         }
49228     },
49229
49230     focus : function() {
49231         this.btnEl.focus();
49232     },
49233
49234     blur : function() {
49235         this.btnEl.blur();
49236     },
49237
49238     // private
49239     onFocus : function(e){
49240         if(!this.disabled){
49241             this.el.addClass('x-btn-focus');
49242         }
49243     },
49244     // private
49245     onBlur : function(e){
49246         this.el.removeClass('x-btn-focus');
49247     },
49248
49249     // private
49250     getClickEl : function(e, isUp){
49251        return this.el;
49252     },
49253
49254     // private
49255     onMouseDown : function(e){
49256         if(!this.disabled && e.button === 0){
49257             this.getClickEl(e).addClass('x-btn-click');
49258             this.doc.on('mouseup', this.onMouseUp, this);
49259         }
49260     },
49261     // private
49262     onMouseUp : function(e){
49263         if(e.button === 0){
49264             this.getClickEl(e, true).removeClass('x-btn-click');
49265             this.doc.un('mouseup', this.onMouseUp, this);
49266         }
49267     },
49268     // private
49269     onMenuShow : function(e){
49270         if(this.menu.ownerCt == this){
49271             this.menu.ownerCt = this;
49272             this.ignoreNextClick = 0;
49273             this.el.addClass('x-btn-menu-active');
49274             this.fireEvent('menushow', this, this.menu);
49275         }
49276     },
49277     // private
49278     onMenuHide : function(e){
49279         if(this.menu.ownerCt == this){
49280             this.el.removeClass('x-btn-menu-active');
49281             this.ignoreNextClick = this.restoreClick.defer(250, this);
49282             this.fireEvent('menuhide', this, this.menu);
49283             delete this.menu.ownerCt;
49284         }
49285     },
49286
49287     // private
49288     restoreClick : function(){
49289         this.ignoreNextClick = 0;
49290     }
49291
49292     /**
49293      * @cfg {String} autoEl @hide
49294      */
49295     /**
49296      * @cfg {String/Object} html @hide
49297      */
49298     /**
49299      * @cfg {String} contentEl  @hide
49300      */
49301     /**
49302      * @cfg {Mixed} data  @hide
49303      */
49304     /**
49305      * @cfg {Mixed} tpl  @hide
49306      */
49307     /**
49308      * @cfg {String} tplWriteMode  @hide
49309      */
49310 });
49311 Ext.reg('button', Ext.Button);
49312
49313 // Private utility class used by Button
49314 Ext.ButtonToggleMgr = function(){
49315    var groups = {};
49316
49317    function toggleGroup(btn, state){
49318        if(state){
49319            var g = groups[btn.toggleGroup];
49320            for(var i = 0, l = g.length; i < l; i++){
49321                if(g[i] != btn){
49322                    g[i].toggle(false);
49323                }
49324            }
49325        }
49326    }
49327
49328    return {
49329        register : function(btn){
49330            if(!btn.toggleGroup){
49331                return;
49332            }
49333            var g = groups[btn.toggleGroup];
49334            if(!g){
49335                g = groups[btn.toggleGroup] = [];
49336            }
49337            g.push(btn);
49338            btn.on('toggle', toggleGroup);
49339        },
49340
49341        unregister : function(btn){
49342            if(!btn.toggleGroup){
49343                return;
49344            }
49345            var g = groups[btn.toggleGroup];
49346            if(g){
49347                g.remove(btn);
49348                btn.un('toggle', toggleGroup);
49349            }
49350        },
49351
49352        /**
49353         * Gets the pressed button in the passed group or null
49354         * @param {String} group
49355         * @return Button
49356         */
49357        getPressed : function(group){
49358            var g = groups[group];
49359            if(g){
49360                for(var i = 0, len = g.length; i < len; i++){
49361                    if(g[i].pressed === true){
49362                        return g[i];
49363                    }
49364                }
49365            }
49366            return null;
49367        }
49368    };
49369 }();
49370 /**
49371  * @class Ext.SplitButton
49372  * @extends Ext.Button
49373  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
49374  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
49375  * options to the primary button action, but any custom handler can provide the arrowclick implementation.  Example usage:
49376  * <pre><code>
49377 // display a dropdown menu:
49378 new Ext.SplitButton({
49379         renderTo: 'button-ct', // the container id
49380         text: 'Options',
49381         handler: optionsHandler, // handle a click on the button itself
49382         menu: new Ext.menu.Menu({
49383         items: [
49384                 // these items will render as dropdown menu items when the arrow is clicked:
49385                 {text: 'Item 1', handler: item1Handler},
49386                 {text: 'Item 2', handler: item2Handler}
49387         ]
49388         })
49389 });
49390
49391 // Instead of showing a menu, you provide any type of custom
49392 // functionality you want when the dropdown arrow is clicked:
49393 new Ext.SplitButton({
49394         renderTo: 'button-ct',
49395         text: 'Options',
49396         handler: optionsHandler,
49397         arrowHandler: myCustomHandler
49398 });
49399 </code></pre>
49400  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
49401  * @cfg {String} arrowTooltip The title attribute of the arrow
49402  * @constructor
49403  * Create a new menu button
49404  * @param {Object} config The config object
49405  * @xtype splitbutton
49406  */
49407 Ext.SplitButton = Ext.extend(Ext.Button, {
49408         // private
49409     arrowSelector : 'em',
49410     split: true,
49411
49412     // private
49413     initComponent : function(){
49414         Ext.SplitButton.superclass.initComponent.call(this);
49415         /**
49416          * @event arrowclick
49417          * Fires when this button's arrow is clicked
49418          * @param {MenuButton} this
49419          * @param {EventObject} e The click event
49420          */
49421         this.addEvents("arrowclick");
49422     },
49423
49424     // private
49425     onRender : function(){
49426         Ext.SplitButton.superclass.onRender.apply(this, arguments);
49427         if(this.arrowTooltip){
49428             this.el.child(this.arrowSelector).dom[this.tooltipType] = this.arrowTooltip;
49429         }
49430     },
49431
49432     /**
49433      * Sets this button's arrow click handler.
49434      * @param {Function} handler The function to call when the arrow is clicked
49435      * @param {Object} scope (optional) Scope for the function passed above
49436      */
49437     setArrowHandler : function(handler, scope){
49438         this.arrowHandler = handler;
49439         this.scope = scope;
49440     },
49441
49442     getMenuClass : function(){
49443         return 'x-btn-split' + (this.arrowAlign == 'bottom' ? '-bottom' : '');
49444     },
49445
49446     isClickOnArrow : function(e){
49447         if (this.arrowAlign != 'bottom') {
49448             var visBtn = this.el.child('em.x-btn-split');
49449             var right = visBtn.getRegion().right - visBtn.getPadding('r');
49450             return e.getPageX() > right;
49451         } else {
49452             return e.getPageY() > this.btnEl.getRegion().bottom;
49453         }
49454     },
49455
49456     // private
49457     onClick : function(e, t){
49458         e.preventDefault();
49459         if(!this.disabled){
49460             if(this.isClickOnArrow(e)){
49461                 if(this.menu && !this.menu.isVisible() && !this.ignoreNextClick){
49462                     this.showMenu();
49463                 }
49464                 this.fireEvent("arrowclick", this, e);
49465                 if(this.arrowHandler){
49466                     this.arrowHandler.call(this.scope || this, this, e);
49467                 }
49468             }else{
49469                 this.doToggle();
49470                 this.fireEvent("click", this, e);
49471                 if(this.handler){
49472                     this.handler.call(this.scope || this, this, e);
49473                 }
49474             }
49475         }
49476     },
49477
49478     // private
49479     isMenuTriggerOver : function(e){
49480         return this.menu && e.target.tagName == this.arrowSelector;
49481     },
49482
49483     // private
49484     isMenuTriggerOut : function(e, internal){
49485         return this.menu && e.target.tagName != this.arrowSelector;
49486     }
49487 });
49488
49489 Ext.reg('splitbutton', Ext.SplitButton);/**
49490  * @class Ext.CycleButton
49491  * @extends Ext.SplitButton
49492  * A specialized SplitButton that contains a menu of {@link Ext.menu.CheckItem} elements.  The button automatically
49493  * cycles through each menu item on click, raising the button's {@link #change} event (or calling the button's
49494  * {@link #changeHandler} function, if supplied) for the active menu item. Clicking on the arrow section of the
49495  * button displays the dropdown menu just like a normal SplitButton.  Example usage:
49496  * <pre><code>
49497 var btn = new Ext.CycleButton({
49498     showText: true,
49499     prependText: 'View as ',
49500     items: [{
49501         text:'text only',
49502         iconCls:'view-text',
49503         checked:true
49504     },{
49505         text:'HTML',
49506         iconCls:'view-html'
49507     }],
49508     changeHandler:function(btn, item){
49509         Ext.Msg.alert('Change View', item.text);
49510     }
49511 });
49512 </code></pre>
49513  * @constructor
49514  * Create a new split button
49515  * @param {Object} config The config object
49516  * @xtype cycle
49517  */
49518 Ext.CycleButton = Ext.extend(Ext.SplitButton, {
49519     /**
49520      * @cfg {Array} items An array of {@link Ext.menu.CheckItem} <b>config</b> objects to be used when creating the
49521      * button's menu items (e.g., {text:'Foo', iconCls:'foo-icon'})
49522      */
49523     /**
49524      * @cfg {Boolean} showText True to display the active item's text as the button text (defaults to false)
49525      */
49526     /**
49527      * @cfg {String} prependText A static string to prepend before the active item's text when displayed as the
49528      * button's text (only applies when showText = true, defaults to '')
49529      */
49530     /**
49531      * @cfg {Function} changeHandler A callback function that will be invoked each time the active menu
49532      * item in the button's menu has changed.  If this callback is not supplied, the SplitButton will instead
49533      * fire the {@link #change} event on active item change.  The changeHandler function will be called with the
49534      * following argument list: (SplitButton this, Ext.menu.CheckItem item)
49535      */
49536     /**
49537      * @cfg {String} forceIcon A css class which sets an image to be used as the static icon for this button.  This
49538      * icon will always be displayed regardless of which item is selected in the dropdown list.  This overrides the 
49539      * default behavior of changing the button's icon to match the selected item's icon on change.
49540      */
49541     /**
49542      * @property menu
49543      * @type Menu
49544      * The {@link Ext.menu.Menu Menu} object used to display the {@link Ext.menu.CheckItem CheckItems} representing the available choices.
49545      */
49546
49547     // private
49548     getItemText : function(item){
49549         if(item && this.showText === true){
49550             var text = '';
49551             if(this.prependText){
49552                 text += this.prependText;
49553             }
49554             text += item.text;
49555             return text;
49556         }
49557         return undefined;
49558     },
49559
49560     /**
49561      * Sets the button's active menu item.
49562      * @param {Ext.menu.CheckItem} item The item to activate
49563      * @param {Boolean} suppressEvent True to prevent the button's change event from firing (defaults to false)
49564      */
49565     setActiveItem : function(item, suppressEvent){
49566         if(!Ext.isObject(item)){
49567             item = this.menu.getComponent(item);
49568         }
49569         if(item){
49570             if(!this.rendered){
49571                 this.text = this.getItemText(item);
49572                 this.iconCls = item.iconCls;
49573             }else{
49574                 var t = this.getItemText(item);
49575                 if(t){
49576                     this.setText(t);
49577                 }
49578                 this.setIconClass(item.iconCls);
49579             }
49580             this.activeItem = item;
49581             if(!item.checked){
49582                 item.setChecked(true, false);
49583             }
49584             if(this.forceIcon){
49585                 this.setIconClass(this.forceIcon);
49586             }
49587             if(!suppressEvent){
49588                 this.fireEvent('change', this, item);
49589             }
49590         }
49591     },
49592
49593     /**
49594      * Gets the currently active menu item.
49595      * @return {Ext.menu.CheckItem} The active item
49596      */
49597     getActiveItem : function(){
49598         return this.activeItem;
49599     },
49600
49601     // private
49602     initComponent : function(){
49603         this.addEvents(
49604             /**
49605              * @event change
49606              * Fires after the button's active menu item has changed.  Note that if a {@link #changeHandler} function
49607              * is set on this CycleButton, it will be called instead on active item change and this change event will
49608              * not be fired.
49609              * @param {Ext.CycleButton} this
49610              * @param {Ext.menu.CheckItem} item The menu item that was selected
49611              */
49612             "change"
49613         );
49614
49615         if(this.changeHandler){
49616             this.on('change', this.changeHandler, this.scope||this);
49617             delete this.changeHandler;
49618         }
49619
49620         this.itemCount = this.items.length;
49621
49622         this.menu = {cls:'x-cycle-menu', items:[]};
49623         var checked = 0;
49624         Ext.each(this.items, function(item, i){
49625             Ext.apply(item, {
49626                 group: item.group || this.id,
49627                 itemIndex: i,
49628                 checkHandler: this.checkHandler,
49629                 scope: this,
49630                 checked: item.checked || false
49631             });
49632             this.menu.items.push(item);
49633             if(item.checked){
49634                 checked = i;
49635             }
49636         }, this);
49637         Ext.CycleButton.superclass.initComponent.call(this);
49638         this.on('click', this.toggleSelected, this);
49639         this.setActiveItem(checked, true);
49640     },
49641
49642     // private
49643     checkHandler : function(item, pressed){
49644         if(pressed){
49645             this.setActiveItem(item);
49646         }
49647     },
49648
49649     /**
49650      * This is normally called internally on button click, but can be called externally to advance the button's
49651      * active item programmatically to the next one in the menu.  If the current item is the last one in the menu
49652      * the active item will be set to the first item in the menu.
49653      */
49654     toggleSelected : function(){
49655         var m = this.menu;
49656         m.render();
49657         // layout if we haven't before so the items are active
49658         if(!m.hasLayout){
49659             m.doLayout();
49660         }
49661         
49662         var nextIdx, checkItem;
49663         for (var i = 1; i < this.itemCount; i++) {
49664             nextIdx = (this.activeItem.itemIndex + i) % this.itemCount;
49665             // check the potential item
49666             checkItem = m.items.itemAt(nextIdx);
49667             // if its not disabled then check it.
49668             if (!checkItem.disabled) {
49669                 checkItem.setChecked(true);
49670                 break;
49671             }
49672         }
49673     }
49674 });
49675 Ext.reg('cycle', Ext.CycleButton);/**
49676  * @class Ext.Toolbar
49677  * @extends Ext.Container
49678  * <p>Basic Toolbar class. Although the <tt>{@link Ext.Container#defaultType defaultType}</tt> for Toolbar
49679  * is <tt>{@link Ext.Button button}</tt>, Toolbar elements (child items for the Toolbar container) may
49680  * be virtually any type of Component. Toolbar elements can be created explicitly via their constructors,
49681  * or implicitly via their xtypes, and can be <tt>{@link #add}</tt>ed dynamically.</p>
49682  * <p>Some items have shortcut strings for creation:</p>
49683  * <pre>
49684 <u>Shortcut</u>  <u>xtype</u>          <u>Class</u>                  <u>Description</u>
49685 '->'      'tbfill'       {@link Ext.Toolbar.Fill}       begin using the right-justified button container
49686 '-'       'tbseparator'  {@link Ext.Toolbar.Separator}  add a vertical separator bar between toolbar items
49687 ' '       'tbspacer'     {@link Ext.Toolbar.Spacer}     add horiztonal space between elements
49688  * </pre>
49689  *
49690  * Example usage of various elements:
49691  * <pre><code>
49692 var tb = new Ext.Toolbar({
49693     renderTo: document.body,
49694     width: 600,
49695     height: 100,
49696     items: [
49697         {
49698             // xtype: 'button', // default for Toolbars, same as 'tbbutton'
49699             text: 'Button'
49700         },
49701         {
49702             xtype: 'splitbutton', // same as 'tbsplitbutton'
49703             text: 'Split Button'
49704         },
49705         // begin using the right-justified button container
49706         '->', // same as {xtype: 'tbfill'}, // Ext.Toolbar.Fill
49707         {
49708             xtype: 'textfield',
49709             name: 'field1',
49710             emptyText: 'enter search term'
49711         },
49712         // add a vertical separator bar between toolbar items
49713         '-', // same as {xtype: 'tbseparator'} to create Ext.Toolbar.Separator
49714         'text 1', // same as {xtype: 'tbtext', text: 'text1'} to create Ext.Toolbar.TextItem
49715         {xtype: 'tbspacer'},// same as ' ' to create Ext.Toolbar.Spacer
49716         'text 2',
49717         {xtype: 'tbspacer', width: 50}, // add a 50px space
49718         'text 3'
49719     ]
49720 });
49721  * </code></pre>
49722  * Example adding a ComboBox within a menu of a button:
49723  * <pre><code>
49724 // ComboBox creation
49725 var combo = new Ext.form.ComboBox({
49726     store: new Ext.data.ArrayStore({
49727         autoDestroy: true,
49728         fields: ['initials', 'fullname'],
49729         data : [
49730             ['FF', 'Fred Flintstone'],
49731             ['BR', 'Barney Rubble']
49732         ]
49733     }),
49734     displayField: 'fullname',
49735     typeAhead: true,
49736     mode: 'local',
49737     forceSelection: true,
49738     triggerAction: 'all',
49739     emptyText: 'Select a name...',
49740     selectOnFocus: true,
49741     width: 135,
49742     getListParent: function() {
49743         return this.el.up('.x-menu');
49744     },
49745     iconCls: 'no-icon' //use iconCls if placing within menu to shift to right side of menu
49746 });
49747
49748 // put ComboBox in a Menu
49749 var menu = new Ext.menu.Menu({
49750     id: 'mainMenu',
49751     items: [
49752         combo // A Field in a Menu
49753     ]
49754 });
49755
49756 // add a Button with the menu
49757 tb.add({
49758         text:'Button w/ Menu',
49759         menu: menu  // assign menu by instance
49760     });
49761 tb.doLayout();
49762  * </code></pre>
49763  * @constructor
49764  * Creates a new Toolbar
49765  * @param {Object/Array} config A config object or an array of buttons to <tt>{@link #add}</tt>
49766  * @xtype toolbar
49767  */
49768 Ext.Toolbar = function(config){
49769     if(Ext.isArray(config)){
49770         config = {items: config, layout: 'toolbar'};
49771     } else {
49772         config = Ext.apply({
49773             layout: 'toolbar'
49774         }, config);
49775         if(config.buttons) {
49776             config.items = config.buttons;
49777         }
49778     }
49779     Ext.Toolbar.superclass.constructor.call(this, config);
49780 };
49781
49782 (function(){
49783
49784 var T = Ext.Toolbar;
49785
49786 Ext.extend(T, Ext.Container, {
49787
49788     defaultType: 'button',
49789
49790     /**
49791      * @cfg {String/Object} layout
49792      * This class assigns a default layout (<code>layout:'<b>toolbar</b>'</code>).
49793      * Developers <i>may</i> override this configuration option if another layout
49794      * is required (the constructor must be passed a configuration object in this
49795      * case instead of an array).
49796      * See {@link Ext.Container#layout} for additional information.
49797      */
49798
49799     enableOverflow : false,
49800
49801     /**
49802      * @cfg {Boolean} enableOverflow
49803      * Defaults to false. Configure <tt>true</tt> to make the toolbar provide a button
49804      * which activates a dropdown Menu to show items which overflow the Toolbar's width.
49805      */
49806     /**
49807      * @cfg {String} buttonAlign
49808      * <p>The default position at which to align child items. Defaults to <code>"left"</code></p>
49809      * <p>May be specified as <code>"center"</code> to cause items added before a Fill (A <code>"->"</code>) item
49810      * to be centered in the Toolbar. Items added after a Fill are still right-aligned.</p>
49811      * <p>Specify as <code>"right"</code> to right align all child items.</p>
49812      */
49813
49814     trackMenus : true,
49815     internalDefaults: {removeMode: 'container', hideParent: true},
49816     toolbarCls: 'x-toolbar',
49817
49818     initComponent : function(){
49819         T.superclass.initComponent.call(this);
49820
49821         /**
49822          * @event overflowchange
49823          * Fires after the overflow state has changed.
49824          * @param {Object} c The Container
49825          * @param {Boolean} lastOverflow overflow state
49826          */
49827         this.addEvents('overflowchange');
49828     },
49829
49830     // private
49831     onRender : function(ct, position){
49832         if(!this.el){
49833             if(!this.autoCreate){
49834                 this.autoCreate = {
49835                     cls: this.toolbarCls + ' x-small-editor'
49836                 };
49837             }
49838             this.el = ct.createChild(Ext.apply({ id: this.id },this.autoCreate), position);
49839             Ext.Toolbar.superclass.onRender.apply(this, arguments);
49840         }
49841     },
49842
49843     /**
49844      * <p>Adds element(s) to the toolbar -- this function takes a variable number of
49845      * arguments of mixed type and adds them to the toolbar.</p>
49846      * <br><p><b>Note</b>: See the notes within {@link Ext.Container#add}.</p>
49847      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
49848      * <ul>
49849      * <li>{@link Ext.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
49850      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
49851      * <li>Field: Any form field (equivalent to {@link #addField})</li>
49852      * <li>Item: Any subclass of {@link Ext.Toolbar.Item} (equivalent to {@link #addItem})</li>
49853      * <li>String: Any generic string (gets wrapped in a {@link Ext.Toolbar.TextItem}, equivalent to {@link #addText}).
49854      * Note that there are a few special strings that are treated differently as explained next.</li>
49855      * <li>'-': Creates a separator element (equivalent to {@link #addSeparator})</li>
49856      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
49857      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
49858      * </ul>
49859      * @param {Mixed} arg2
49860      * @param {Mixed} etc.
49861      * @method add
49862      */
49863
49864     // private
49865     lookupComponent : function(c){
49866         if(Ext.isString(c)){
49867             if(c == '-'){
49868                 c = new T.Separator();
49869             }else if(c == ' '){
49870                 c = new T.Spacer();
49871             }else if(c == '->'){
49872                 c = new T.Fill();
49873             }else{
49874                 c = new T.TextItem(c);
49875             }
49876             this.applyDefaults(c);
49877         }else{
49878             if(c.isFormField || c.render){ // some kind of form field, some kind of Toolbar.Item
49879                 c = this.createComponent(c);
49880             }else if(c.tag){ // DomHelper spec
49881                 c = new T.Item({autoEl: c});
49882             }else if(c.tagName){ // element
49883                 c = new T.Item({el:c});
49884             }else if(Ext.isObject(c)){ // must be button config?
49885                 c = c.xtype ? this.createComponent(c) : this.constructButton(c);
49886             }
49887         }
49888         return c;
49889     },
49890
49891     // private
49892     applyDefaults : function(c){
49893         if(!Ext.isString(c)){
49894             c = Ext.Toolbar.superclass.applyDefaults.call(this, c);
49895             var d = this.internalDefaults;
49896             if(c.events){
49897                 Ext.applyIf(c.initialConfig, d);
49898                 Ext.apply(c, d);
49899             }else{
49900                 Ext.applyIf(c, d);
49901             }
49902         }
49903         return c;
49904     },
49905
49906     /**
49907      * Adds a separator
49908      * <br><p><b>Note</b>: See the notes within {@link Ext.Container#add}.</p>
49909      * @return {Ext.Toolbar.Item} The separator {@link Ext.Toolbar.Item item}
49910      */
49911     addSeparator : function(){
49912         return this.add(new T.Separator());
49913     },
49914
49915     /**
49916      * Adds a spacer element
49917      * <br><p><b>Note</b>: See the notes within {@link Ext.Container#add}.</p>
49918      * @return {Ext.Toolbar.Spacer} The spacer item
49919      */
49920     addSpacer : function(){
49921         return this.add(new T.Spacer());
49922     },
49923
49924     /**
49925      * Forces subsequent additions into the float:right toolbar
49926      * <br><p><b>Note</b>: See the notes within {@link Ext.Container#add}.</p>
49927      */
49928     addFill : function(){
49929         this.add(new T.Fill());
49930     },
49931
49932     /**
49933      * Adds any standard HTML element to the toolbar
49934      * <br><p><b>Note</b>: See the notes within {@link Ext.Container#add}.</p>
49935      * @param {Mixed} el The element or id of the element to add
49936      * @return {Ext.Toolbar.Item} The element's item
49937      */
49938     addElement : function(el){
49939         return this.addItem(new T.Item({el:el}));
49940     },
49941
49942     /**
49943      * Adds any Toolbar.Item or subclass
49944      * <br><p><b>Note</b>: See the notes within {@link Ext.Container#add}.</p>
49945      * @param {Ext.Toolbar.Item} item
49946      * @return {Ext.Toolbar.Item} The item
49947      */
49948     addItem : function(item){
49949         return this.add.apply(this, arguments);
49950     },
49951
49952     /**
49953      * Adds a button (or buttons). See {@link Ext.Button} for more info on the config.
49954      * <br><p><b>Note</b>: See the notes within {@link Ext.Container#add}.</p>
49955      * @param {Object/Array} config A button config or array of configs
49956      * @return {Ext.Button/Array}
49957      */
49958     addButton : function(config){
49959         if(Ext.isArray(config)){
49960             var buttons = [];
49961             for(var i = 0, len = config.length; i < len; i++) {
49962                 buttons.push(this.addButton(config[i]));
49963             }
49964             return buttons;
49965         }
49966         return this.add(this.constructButton(config));
49967     },
49968
49969     /**
49970      * Adds text to the toolbar
49971      * <br><p><b>Note</b>: See the notes within {@link Ext.Container#add}.</p>
49972      * @param {String} text The text to add
49973      * @return {Ext.Toolbar.Item} The element's item
49974      */
49975     addText : function(text){
49976         return this.addItem(new T.TextItem(text));
49977     },
49978
49979     /**
49980      * Adds a new element to the toolbar from the passed {@link Ext.DomHelper} config
49981      * <br><p><b>Note</b>: See the notes within {@link Ext.Container#add}.</p>
49982      * @param {Object} config
49983      * @return {Ext.Toolbar.Item} The element's item
49984      */
49985     addDom : function(config){
49986         return this.add(new T.Item({autoEl: config}));
49987     },
49988
49989     /**
49990      * Adds a dynamically rendered Ext.form field (TextField, ComboBox, etc). Note: the field should not have
49991      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
49992      * <br><p><b>Note</b>: See the notes within {@link Ext.Container#add}.</p>
49993      * @param {Ext.form.Field} field
49994      * @return {Ext.Toolbar.Item}
49995      */
49996     addField : function(field){
49997         return this.add(field);
49998     },
49999
50000     /**
50001      * Inserts any {@link Ext.Toolbar.Item}/{@link Ext.Button} at the specified index.
50002      * <br><p><b>Note</b>: See the notes within {@link Ext.Container#add}.</p>
50003      * @param {Number} index The index where the item is to be inserted
50004      * @param {Object/Ext.Toolbar.Item/Ext.Button/Array} item The button, or button config object to be
50005      * inserted, or an array of buttons/configs.
50006      * @return {Ext.Button/Item}
50007      */
50008     insertButton : function(index, item){
50009         if(Ext.isArray(item)){
50010             var buttons = [];
50011             for(var i = 0, len = item.length; i < len; i++) {
50012                buttons.push(this.insertButton(index + i, item[i]));
50013             }
50014             return buttons;
50015         }
50016         return Ext.Toolbar.superclass.insert.call(this, index, item);
50017     },
50018
50019     // private
50020     trackMenu : function(item, remove){
50021         if(this.trackMenus && item.menu){
50022             var method = remove ? 'mun' : 'mon';
50023             this[method](item, 'menutriggerover', this.onButtonTriggerOver, this);
50024             this[method](item, 'menushow', this.onButtonMenuShow, this);
50025             this[method](item, 'menuhide', this.onButtonMenuHide, this);
50026         }
50027     },
50028
50029     // private
50030     constructButton : function(item){
50031         var b = item.events ? item : this.createComponent(item, item.split ? 'splitbutton' : this.defaultType);
50032         return b;
50033     },
50034
50035     // private
50036     onAdd : function(c){
50037         Ext.Toolbar.superclass.onAdd.call(this);
50038         this.trackMenu(c);
50039         if(this.disabled){
50040             c.disable();
50041         }
50042     },
50043
50044     // private
50045     onRemove : function(c){
50046         Ext.Toolbar.superclass.onRemove.call(this);
50047         if (c == this.activeMenuBtn) {
50048             delete this.activeMenuBtn;
50049         }
50050         this.trackMenu(c, true);
50051     },
50052
50053     // private
50054     onDisable : function(){
50055         this.items.each(function(item){
50056              if(item.disable){
50057                  item.disable();
50058              }
50059         });
50060     },
50061
50062     // private
50063     onEnable : function(){
50064         this.items.each(function(item){
50065              if(item.enable){
50066                  item.enable();
50067              }
50068         });
50069     },
50070
50071     // private
50072     onButtonTriggerOver : function(btn){
50073         if(this.activeMenuBtn && this.activeMenuBtn != btn){
50074             this.activeMenuBtn.hideMenu();
50075             btn.showMenu();
50076             this.activeMenuBtn = btn;
50077         }
50078     },
50079
50080     // private
50081     onButtonMenuShow : function(btn){
50082         this.activeMenuBtn = btn;
50083     },
50084
50085     // private
50086     onButtonMenuHide : function(btn){
50087         delete this.activeMenuBtn;
50088     }
50089 });
50090 Ext.reg('toolbar', Ext.Toolbar);
50091
50092 /**
50093  * @class Ext.Toolbar.Item
50094  * @extends Ext.BoxComponent
50095  * The base class that other non-interacting Toolbar Item classes should extend in order to
50096  * get some basic common toolbar item functionality.
50097  * @constructor
50098  * Creates a new Item
50099  * @param {HTMLElement} el
50100  * @xtype tbitem
50101  */
50102 T.Item = Ext.extend(Ext.BoxComponent, {
50103     hideParent: true, //  Hiding a Toolbar.Item hides its containing TD
50104     enable:Ext.emptyFn,
50105     disable:Ext.emptyFn,
50106     focus:Ext.emptyFn
50107     /**
50108      * @cfg {String} overflowText Text to be used for the menu if the item is overflowed.
50109      */
50110 });
50111 Ext.reg('tbitem', T.Item);
50112
50113 /**
50114  * @class Ext.Toolbar.Separator
50115  * @extends Ext.Toolbar.Item
50116  * A simple class that adds a vertical separator bar between toolbar items
50117  * (css class:<tt>'xtb-sep'</tt>). Example usage:
50118  * <pre><code>
50119 new Ext.Panel({
50120     tbar : [
50121         'Item 1',
50122         {xtype: 'tbseparator'}, // or '-'
50123         'Item 2'
50124     ]
50125 });
50126 </code></pre>
50127  * @constructor
50128  * Creates a new Separator
50129  * @xtype tbseparator
50130  */
50131 T.Separator = Ext.extend(T.Item, {
50132     onRender : function(ct, position){
50133         this.el = ct.createChild({tag:'span', cls:'xtb-sep'}, position);
50134     }
50135 });
50136 Ext.reg('tbseparator', T.Separator);
50137
50138 /**
50139  * @class Ext.Toolbar.Spacer
50140  * @extends Ext.Toolbar.Item
50141  * A simple element that adds extra horizontal space between items in a toolbar.
50142  * By default a 2px wide space is added via css specification:<pre><code>
50143 .x-toolbar .xtb-spacer {
50144     width:2px;
50145 }
50146  * </code></pre>
50147  * <p>Example usage:</p>
50148  * <pre><code>
50149 new Ext.Panel({
50150     tbar : [
50151         'Item 1',
50152         {xtype: 'tbspacer'}, // or ' '
50153         'Item 2',
50154         // space width is also configurable via javascript
50155         {xtype: 'tbspacer', width: 50}, // add a 50px space
50156         'Item 3'
50157     ]
50158 });
50159 </code></pre>
50160  * @constructor
50161  * Creates a new Spacer
50162  * @xtype tbspacer
50163  */
50164 T.Spacer = Ext.extend(T.Item, {
50165     /**
50166      * @cfg {Number} width
50167      * The width of the spacer in pixels (defaults to 2px via css style <tt>.x-toolbar .xtb-spacer</tt>).
50168      */
50169
50170     onRender : function(ct, position){
50171         this.el = ct.createChild({tag:'div', cls:'xtb-spacer', style: this.width?'width:'+this.width+'px':''}, position);
50172     }
50173 });
50174 Ext.reg('tbspacer', T.Spacer);
50175
50176 /**
50177  * @class Ext.Toolbar.Fill
50178  * @extends Ext.Toolbar.Spacer
50179  * A non-rendering placeholder item which instructs the Toolbar's Layout to begin using
50180  * the right-justified button container.
50181  * <pre><code>
50182 new Ext.Panel({
50183     tbar : [
50184         'Item 1',
50185         {xtype: 'tbfill'}, // or '->'
50186         'Item 2'
50187     ]
50188 });
50189 </code></pre>
50190  * @constructor
50191  * Creates a new Fill
50192  * @xtype tbfill
50193  */
50194 T.Fill = Ext.extend(T.Item, {
50195     // private
50196     render : Ext.emptyFn,
50197     isFill : true
50198 });
50199 Ext.reg('tbfill', T.Fill);
50200
50201 /**
50202  * @class Ext.Toolbar.TextItem
50203  * @extends Ext.Toolbar.Item
50204  * A simple class that renders text directly into a toolbar
50205  * (with css class:<tt>'xtb-text'</tt>). Example usage:
50206  * <pre><code>
50207 new Ext.Panel({
50208     tbar : [
50209         {xtype: 'tbtext', text: 'Item 1'} // or simply 'Item 1'
50210     ]
50211 });
50212 </code></pre>
50213  * @constructor
50214  * Creates a new TextItem
50215  * @param {String/Object} text A text string, or a config object containing a <tt>text</tt> property
50216  * @xtype tbtext
50217  */
50218 T.TextItem = Ext.extend(T.Item, {
50219     /**
50220      * @cfg {String} text The text to be used as innerHTML (html tags are accepted)
50221      */
50222
50223     constructor: function(config){
50224         T.TextItem.superclass.constructor.call(this, Ext.isString(config) ? {text: config} : config);
50225     },
50226
50227     // private
50228     onRender : function(ct, position) {
50229         this.autoEl = {cls: 'xtb-text', html: this.text || ''};
50230         T.TextItem.superclass.onRender.call(this, ct, position);
50231     },
50232
50233     /**
50234      * Updates this item's text, setting the text to be used as innerHTML.
50235      * @param {String} t The text to display (html accepted).
50236      */
50237     setText : function(t) {
50238         if(this.rendered){
50239             this.el.update(t);
50240         }else{
50241             this.text = t;
50242         }
50243     }
50244 });
50245 Ext.reg('tbtext', T.TextItem);
50246
50247 // backwards compat
50248 T.Button = Ext.extend(Ext.Button, {});
50249 T.SplitButton = Ext.extend(Ext.SplitButton, {});
50250 Ext.reg('tbbutton', T.Button);
50251 Ext.reg('tbsplit', T.SplitButton);
50252
50253 })();
50254 /**
50255  * @class Ext.ButtonGroup
50256  * @extends Ext.Panel
50257  * Container for a group of buttons. Example usage:
50258  * <pre><code>
50259 var p = new Ext.Panel({
50260     title: 'Panel with Button Group',
50261     width: 300,
50262     height:200,
50263     renderTo: document.body,
50264     html: 'whatever',
50265     tbar: [{
50266         xtype: 'buttongroup',
50267         {@link #columns}: 3,
50268         title: 'Clipboard',
50269         items: [{
50270             text: 'Paste',
50271             scale: 'large',
50272             rowspan: 3, iconCls: 'add',
50273             iconAlign: 'top',
50274             cls: 'x-btn-as-arrow'
50275         },{
50276             xtype:'splitbutton',
50277             text: 'Menu Button',
50278             scale: 'large',
50279             rowspan: 3,
50280             iconCls: 'add',
50281             iconAlign: 'top',
50282             arrowAlign:'bottom',
50283             menu: [{text: 'Menu Item 1'}]
50284         },{
50285             xtype:'splitbutton', text: 'Cut', iconCls: 'add16', menu: [{text: 'Cut Menu Item'}]
50286         },{
50287             text: 'Copy', iconCls: 'add16'
50288         },{
50289             text: 'Format', iconCls: 'add16'
50290         }]
50291     }]
50292 });
50293  * </code></pre>
50294  * @constructor
50295  * Create a new ButtonGroup.
50296  * @param {Object} config The config object
50297  * @xtype buttongroup
50298  */
50299 Ext.ButtonGroup = Ext.extend(Ext.Panel, {
50300     /**
50301      * @cfg {Number} columns The <tt>columns</tt> configuration property passed to the
50302      * {@link #layout configured layout manager}. See {@link Ext.layout.TableLayout#columns}.
50303      */
50304     /**
50305      * @cfg {String} baseCls  Defaults to <tt>'x-btn-group'</tt>.  See {@link Ext.Panel#baseCls}.
50306      */
50307     baseCls: 'x-btn-group',
50308     /**
50309      * @cfg {String} layout  Defaults to <tt>'table'</tt>.  See {@link Ext.Container#layout}.
50310      */
50311     layout:'table',
50312     defaultType: 'button',
50313     /**
50314      * @cfg {Boolean} frame  Defaults to <tt>true</tt>.  See {@link Ext.Panel#frame}.
50315      */
50316     frame: true,
50317     internalDefaults: {removeMode: 'container', hideParent: true},
50318
50319     initComponent : function(){
50320         this.layoutConfig = this.layoutConfig || {};
50321         Ext.applyIf(this.layoutConfig, {
50322             columns : this.columns
50323         });
50324         if(!this.title){
50325             this.addClass('x-btn-group-notitle');
50326         }
50327         this.on('afterlayout', this.onAfterLayout, this);
50328         Ext.ButtonGroup.superclass.initComponent.call(this);
50329     },
50330
50331     applyDefaults : function(c){
50332         c = Ext.ButtonGroup.superclass.applyDefaults.call(this, c);
50333         var d = this.internalDefaults;
50334         if(c.events){
50335             Ext.applyIf(c.initialConfig, d);
50336             Ext.apply(c, d);
50337         }else{
50338             Ext.applyIf(c, d);
50339         }
50340         return c;
50341     },
50342
50343     onAfterLayout : function(){
50344         var bodyWidth = this.body.getFrameWidth('lr') + this.body.dom.firstChild.offsetWidth;
50345         this.body.setWidth(bodyWidth);
50346         this.el.setWidth(bodyWidth + this.getFrameWidth());
50347     }
50348     /**
50349      * @cfg {Array} tools  @hide
50350      */
50351 });
50352
50353 Ext.reg('buttongroup', Ext.ButtonGroup);
50354 /**
50355  * @class Ext.PagingToolbar
50356  * @extends Ext.Toolbar
50357  * <p>As the amount of records increases, the time required for the browser to render
50358  * them increases. Paging is used to reduce the amount of data exchanged with the client.
50359  * Note: if there are more records/rows than can be viewed in the available screen area, vertical
50360  * scrollbars will be added.</p>
50361  * <p>Paging is typically handled on the server side (see exception below). The client sends
50362  * parameters to the server side, which the server needs to interpret and then respond with the
50363  * approprate data.</p>
50364  * <p><b>Ext.PagingToolbar</b> is a specialized toolbar that is bound to a {@link Ext.data.Store}
50365  * and provides automatic paging control. This Component {@link Ext.data.Store#load load}s blocks
50366  * of data into the <tt>{@link #store}</tt> by passing {@link Ext.data.Store#paramNames paramNames} used for
50367  * paging criteria.</p>
50368  * <p>PagingToolbar is typically used as one of the Grid's toolbars:</p>
50369  * <pre><code>
50370 Ext.QuickTips.init(); // to display button quicktips
50371
50372 var myStore = new Ext.data.Store({
50373     reader: new Ext.data.JsonReader({
50374         {@link Ext.data.JsonReader#totalProperty totalProperty}: 'results', 
50375         ...
50376     }),
50377     ...
50378 });
50379
50380 var myPageSize = 25;  // server script should only send back 25 items at a time
50381
50382 var grid = new Ext.grid.GridPanel({
50383     ...
50384     store: myStore,
50385     bbar: new Ext.PagingToolbar({
50386         {@link #store}: myStore,       // grid and PagingToolbar using same store
50387         {@link #displayInfo}: true,
50388         {@link #pageSize}: myPageSize,
50389         {@link #prependButtons}: true,
50390         items: [
50391             'text 1'
50392         ]
50393     })
50394 });
50395  * </code></pre>
50396  *
50397  * <p>To use paging, pass the paging requirements to the server when the store is first loaded.</p>
50398  * <pre><code>
50399 store.load({
50400     params: {
50401         // specify params for the first page load if using paging
50402         start: 0,          
50403         limit: myPageSize,
50404         // other params
50405         foo:   'bar'
50406     }
50407 });
50408  * </code></pre>
50409  * 
50410  * <p>If using {@link Ext.data.Store#autoLoad store's autoLoad} configuration:</p>
50411  * <pre><code>
50412 var myStore = new Ext.data.Store({
50413     {@link Ext.data.Store#autoLoad autoLoad}: {params:{start: 0, limit: 25}},
50414     ...
50415 });
50416  * </code></pre>
50417  * 
50418  * <p>The packet sent back from the server would have this form:</p>
50419  * <pre><code>
50420 {
50421     "success": true,
50422     "results": 2000, 
50423     "rows": [ // <b>*Note:</b> this must be an Array 
50424         { "id":  1, "name": "Bill", "occupation": "Gardener" },
50425         { "id":  2, "name":  "Ben", "occupation": "Horticulturalist" },
50426         ...
50427         { "id": 25, "name":  "Sue", "occupation": "Botanist" }
50428     ]
50429 }
50430  * </code></pre>
50431  * <p><u>Paging with Local Data</u></p>
50432  * <p>Paging can also be accomplished with local data using extensions:</p>
50433  * <div class="mdetail-params"><ul>
50434  * <li><a href="http://extjs.com/forum/showthread.php?t=71532">Ext.ux.data.PagingStore</a></li>
50435  * <li>Paging Memory Proxy (examples/ux/PagingMemoryProxy.js)</li>
50436  * </ul></div>
50437  * @constructor Create a new PagingToolbar
50438  * @param {Object} config The config object
50439  * @xtype paging
50440  */
50441 (function() {
50442
50443 var T = Ext.Toolbar;
50444
50445 Ext.PagingToolbar = Ext.extend(Ext.Toolbar, {
50446     /**
50447      * @cfg {Ext.data.Store} store
50448      * The {@link Ext.data.Store} the paging toolbar should use as its data source (required).
50449      */
50450     /**
50451      * @cfg {Boolean} displayInfo
50452      * <tt>true</tt> to display the displayMsg (defaults to <tt>false</tt>)
50453      */
50454     /**
50455      * @cfg {Number} pageSize
50456      * The number of records to display per page (defaults to <tt>20</tt>)
50457      */
50458     pageSize : 20,
50459     /**
50460      * @cfg {Boolean} prependButtons
50461      * <tt>true</tt> to insert any configured <tt>items</tt> <i>before</i> the paging buttons.
50462      * Defaults to <tt>false</tt>.
50463      */
50464     /**
50465      * @cfg {String} displayMsg
50466      * The paging status message to display (defaults to <tt>'Displaying {0} - {1} of {2}'</tt>).
50467      * Note that this string is formatted using the braced numbers <tt>{0}-{2}</tt> as tokens
50468      * that are replaced by the values for start, end and total respectively. These tokens should
50469      * be preserved when overriding this string if showing those values is desired.
50470      */
50471     displayMsg : 'Displaying {0} - {1} of {2}',
50472     /**
50473      * @cfg {String} emptyMsg
50474      * The message to display when no records are found (defaults to 'No data to display')
50475      */
50476     emptyMsg : 'No data to display',
50477     /**
50478      * @cfg {String} beforePageText
50479      * The text displayed before the input item (defaults to <tt>'Page'</tt>).
50480      */
50481     beforePageText : 'Page',
50482     /**
50483      * @cfg {String} afterPageText
50484      * Customizable piece of the default paging text (defaults to <tt>'of {0}'</tt>). Note that
50485      * this string is formatted using <tt>{0}</tt> as a token that is replaced by the number of
50486      * total pages. This token should be preserved when overriding this string if showing the
50487      * total page count is desired.
50488      */
50489     afterPageText : 'of {0}',
50490     /**
50491      * @cfg {String} firstText
50492      * The quicktip text displayed for the first page button (defaults to <tt>'First Page'</tt>).
50493      * <b>Note</b>: quick tips must be initialized for the quicktip to show.
50494      */
50495     firstText : 'First Page',
50496     /**
50497      * @cfg {String} prevText
50498      * The quicktip text displayed for the previous page button (defaults to <tt>'Previous Page'</tt>).
50499      * <b>Note</b>: quick tips must be initialized for the quicktip to show.
50500      */
50501     prevText : 'Previous Page',
50502     /**
50503      * @cfg {String} nextText
50504      * The quicktip text displayed for the next page button (defaults to <tt>'Next Page'</tt>).
50505      * <b>Note</b>: quick tips must be initialized for the quicktip to show.
50506      */
50507     nextText : 'Next Page',
50508     /**
50509      * @cfg {String} lastText
50510      * The quicktip text displayed for the last page button (defaults to <tt>'Last Page'</tt>).
50511      * <b>Note</b>: quick tips must be initialized for the quicktip to show.
50512      */
50513     lastText : 'Last Page',
50514     /**
50515      * @cfg {String} refreshText
50516      * The quicktip text displayed for the Refresh button (defaults to <tt>'Refresh'</tt>).
50517      * <b>Note</b>: quick tips must be initialized for the quicktip to show.
50518      */
50519     refreshText : 'Refresh',
50520
50521     /**
50522      * <p><b>Deprecated</b>. <code>paramNames</code> should be set in the <b>data store</b>
50523      * (see {@link Ext.data.Store#paramNames}).</p>
50524      * <br><p>Object mapping of parameter names used for load calls, initially set to:</p>
50525      * <pre>{start: 'start', limit: 'limit'}</pre>
50526      * @type Object
50527      * @property paramNames
50528      * @deprecated
50529      */
50530
50531     /**
50532      * The number of records to display per page.  See also <tt>{@link #cursor}</tt>.
50533      * @type Number
50534      * @property pageSize
50535      */
50536
50537     /**
50538      * Indicator for the record position.  This property might be used to get the active page
50539      * number for example:<pre><code>
50540      * // t is reference to the paging toolbar instance
50541      * var activePage = Math.ceil((t.cursor + t.pageSize) / t.pageSize);
50542      * </code></pre>
50543      * @type Number
50544      * @property cursor
50545      */
50546
50547     initComponent : function(){
50548         var pagingItems = [this.first = new T.Button({
50549             tooltip: this.firstText,
50550             overflowText: this.firstText,
50551             iconCls: 'x-tbar-page-first',
50552             disabled: true,
50553             handler: this.moveFirst,
50554             scope: this
50555         }), this.prev = new T.Button({
50556             tooltip: this.prevText,
50557             overflowText: this.prevText,
50558             iconCls: 'x-tbar-page-prev',
50559             disabled: true,
50560             handler: this.movePrevious,
50561             scope: this
50562         }), '-', this.beforePageText,
50563         this.inputItem = new Ext.form.NumberField({
50564             cls: 'x-tbar-page-number',
50565             allowDecimals: false,
50566             allowNegative: false,
50567             enableKeyEvents: true,
50568             selectOnFocus: true,
50569             submitValue: false,
50570             listeners: {
50571                 scope: this,
50572                 keydown: this.onPagingKeyDown,
50573                 blur: this.onPagingBlur
50574             }
50575         }), this.afterTextItem = new T.TextItem({
50576             text: String.format(this.afterPageText, 1)
50577         }), '-', this.next = new T.Button({
50578             tooltip: this.nextText,
50579             overflowText: this.nextText,
50580             iconCls: 'x-tbar-page-next',
50581             disabled: true,
50582             handler: this.moveNext,
50583             scope: this
50584         }), this.last = new T.Button({
50585             tooltip: this.lastText,
50586             overflowText: this.lastText,
50587             iconCls: 'x-tbar-page-last',
50588             disabled: true,
50589             handler: this.moveLast,
50590             scope: this
50591         }), '-', this.refresh = new T.Button({
50592             tooltip: this.refreshText,
50593             overflowText: this.refreshText,
50594             iconCls: 'x-tbar-loading',
50595             handler: this.doRefresh,
50596             scope: this
50597         })];
50598
50599
50600         var userItems = this.items || this.buttons || [];
50601         if (this.prependButtons) {
50602             this.items = userItems.concat(pagingItems);
50603         }else{
50604             this.items = pagingItems.concat(userItems);
50605         }
50606         delete this.buttons;
50607         if(this.displayInfo){
50608             this.items.push('->');
50609             this.items.push(this.displayItem = new T.TextItem({}));
50610         }
50611         Ext.PagingToolbar.superclass.initComponent.call(this);
50612         this.addEvents(
50613             /**
50614              * @event change
50615              * Fires after the active page has been changed.
50616              * @param {Ext.PagingToolbar} this
50617              * @param {Object} pageData An object that has these properties:<ul>
50618              * <li><code>total</code> : Number <div class="sub-desc">The total number of records in the dataset as
50619              * returned by the server</div></li>
50620              * <li><code>activePage</code> : Number <div class="sub-desc">The current page number</div></li>
50621              * <li><code>pages</code> : Number <div class="sub-desc">The total number of pages (calculated from
50622              * the total number of records in the dataset as returned by the server and the current {@link #pageSize})</div></li>
50623              * </ul>
50624              */
50625             'change',
50626             /**
50627              * @event beforechange
50628              * Fires just before the active page is changed.
50629              * Return false to prevent the active page from being changed.
50630              * @param {Ext.PagingToolbar} this
50631              * @param {Object} params An object hash of the parameters which the PagingToolbar will send when
50632              * loading the required page. This will contain:<ul>
50633              * <li><code>start</code> : Number <div class="sub-desc">The starting row number for the next page of records to
50634              * be retrieved from the server</div></li>
50635              * <li><code>limit</code> : Number <div class="sub-desc">The number of records to be retrieved from the server</div></li>
50636              * </ul>
50637              * <p>(note: the names of the <b>start</b> and <b>limit</b> properties are determined
50638              * by the store's {@link Ext.data.Store#paramNames paramNames} property.)</p>
50639              * <p>Parameters may be added as required in the event handler.</p>
50640              */
50641             'beforechange'
50642         );
50643         this.on('afterlayout', this.onFirstLayout, this, {single: true});
50644         this.cursor = 0;
50645         this.bindStore(this.store, true);
50646     },
50647
50648     // private
50649     onFirstLayout : function(){
50650         if(this.dsLoaded){
50651             this.onLoad.apply(this, this.dsLoaded);
50652         }
50653     },
50654
50655     // private
50656     updateInfo : function(){
50657         if(this.displayItem){
50658             var count = this.store.getCount();
50659             var msg = count == 0 ?
50660                 this.emptyMsg :
50661                 String.format(
50662                     this.displayMsg,
50663                     this.cursor+1, this.cursor+count, this.store.getTotalCount()
50664                 );
50665             this.displayItem.setText(msg);
50666         }
50667     },
50668
50669     // private
50670     onLoad : function(store, r, o){
50671         if(!this.rendered){
50672             this.dsLoaded = [store, r, o];
50673             return;
50674         }
50675         var p = this.getParams();
50676         this.cursor = (o.params && o.params[p.start]) ? o.params[p.start] : 0;
50677         var d = this.getPageData(), ap = d.activePage, ps = d.pages;
50678
50679         this.afterTextItem.setText(String.format(this.afterPageText, d.pages));
50680         this.inputItem.setValue(ap);
50681         this.first.setDisabled(ap == 1);
50682         this.prev.setDisabled(ap == 1);
50683         this.next.setDisabled(ap == ps);
50684         this.last.setDisabled(ap == ps);
50685         this.refresh.enable();
50686         this.updateInfo();
50687         this.fireEvent('change', this, d);
50688     },
50689
50690     // private
50691     getPageData : function(){
50692         var total = this.store.getTotalCount();
50693         return {
50694             total : total,
50695             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
50696             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
50697         };
50698     },
50699
50700     /**
50701      * Change the active page
50702      * @param {Integer} page The page to display
50703      */
50704     changePage : function(page){
50705         this.doLoad(((page-1) * this.pageSize).constrain(0, this.store.getTotalCount()));
50706     },
50707
50708     // private
50709     onLoadError : function(){
50710         if(!this.rendered){
50711             return;
50712         }
50713         this.refresh.enable();
50714     },
50715
50716     // private
50717     readPage : function(d){
50718         var v = this.inputItem.getValue(), pageNum;
50719         if (!v || isNaN(pageNum = parseInt(v, 10))) {
50720             this.inputItem.setValue(d.activePage);
50721             return false;
50722         }
50723         return pageNum;
50724     },
50725
50726     onPagingFocus : function(){
50727         this.inputItem.select();
50728     },
50729
50730     //private
50731     onPagingBlur : function(e){
50732         this.inputItem.setValue(this.getPageData().activePage);
50733     },
50734
50735     // private
50736     onPagingKeyDown : function(field, e){
50737         var k = e.getKey(), d = this.getPageData(), pageNum;
50738         if (k == e.RETURN) {
50739             e.stopEvent();
50740             pageNum = this.readPage(d);
50741             if(pageNum !== false){
50742                 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
50743                 this.doLoad(pageNum * this.pageSize);
50744             }
50745         }else if (k == e.HOME || k == e.END){
50746             e.stopEvent();
50747             pageNum = k == e.HOME ? 1 : d.pages;
50748             field.setValue(pageNum);
50749         }else if (k == e.UP || k == e.PAGEUP || k == e.DOWN || k == e.PAGEDOWN){
50750             e.stopEvent();
50751             if((pageNum = this.readPage(d))){
50752                 var increment = e.shiftKey ? 10 : 1;
50753                 if(k == e.DOWN || k == e.PAGEDOWN){
50754                     increment *= -1;
50755                 }
50756                 pageNum += increment;
50757                 if(pageNum >= 1 & pageNum <= d.pages){
50758                     field.setValue(pageNum);
50759                 }
50760             }
50761         }
50762     },
50763
50764     // private
50765     getParams : function(){
50766         //retain backwards compat, allow params on the toolbar itself, if they exist.
50767         return this.paramNames || this.store.paramNames;
50768     },
50769
50770     // private
50771     beforeLoad : function(){
50772         if(this.rendered && this.refresh){
50773             this.refresh.disable();
50774         }
50775     },
50776
50777     // private
50778     doLoad : function(start){
50779         var o = {}, pn = this.getParams();
50780         o[pn.start] = start;
50781         o[pn.limit] = this.pageSize;
50782         if(this.fireEvent('beforechange', this, o) !== false){
50783             this.store.load({params:o});
50784         }
50785     },
50786
50787     /**
50788      * Move to the first page, has the same effect as clicking the 'first' button.
50789      */
50790     moveFirst : function(){
50791         this.doLoad(0);
50792     },
50793
50794     /**
50795      * Move to the previous page, has the same effect as clicking the 'previous' button.
50796      */
50797     movePrevious : function(){
50798         this.doLoad(Math.max(0, this.cursor-this.pageSize));
50799     },
50800
50801     /**
50802      * Move to the next page, has the same effect as clicking the 'next' button.
50803      */
50804     moveNext : function(){
50805         this.doLoad(this.cursor+this.pageSize);
50806     },
50807
50808     /**
50809      * Move to the last page, has the same effect as clicking the 'last' button.
50810      */
50811     moveLast : function(){
50812         var total = this.store.getTotalCount(),
50813             extra = total % this.pageSize;
50814
50815         this.doLoad(extra ? (total - extra) : total - this.pageSize);
50816     },
50817
50818     /**
50819      * Refresh the current page, has the same effect as clicking the 'refresh' button.
50820      */
50821     doRefresh : function(){
50822         this.doLoad(this.cursor);
50823     },
50824
50825     /**
50826      * Binds the paging toolbar to the specified {@link Ext.data.Store}
50827      * @param {Store} store The store to bind to this toolbar
50828      * @param {Boolean} initial (Optional) true to not remove listeners
50829      */
50830     bindStore : function(store, initial){
50831         var doLoad;
50832         if(!initial && this.store){
50833             if(store !== this.store && this.store.autoDestroy){
50834                 this.store.destroy();
50835             }else{
50836                 this.store.un('beforeload', this.beforeLoad, this);
50837                 this.store.un('load', this.onLoad, this);
50838                 this.store.un('exception', this.onLoadError, this);
50839             }
50840             if(!store){
50841                 this.store = null;
50842             }
50843         }
50844         if(store){
50845             store = Ext.StoreMgr.lookup(store);
50846             store.on({
50847                 scope: this,
50848                 beforeload: this.beforeLoad,
50849                 load: this.onLoad,
50850                 exception: this.onLoadError
50851             });
50852             doLoad = true;
50853         }
50854         this.store = store;
50855         if(doLoad){
50856             this.onLoad(store, null, {});
50857         }
50858     },
50859
50860     /**
50861      * Unbinds the paging toolbar from the specified {@link Ext.data.Store} <b>(deprecated)</b>
50862      * @param {Ext.data.Store} store The data store to unbind
50863      */
50864     unbind : function(store){
50865         this.bindStore(null);
50866     },
50867
50868     /**
50869      * Binds the paging toolbar to the specified {@link Ext.data.Store} <b>(deprecated)</b>
50870      * @param {Ext.data.Store} store The data store to bind
50871      */
50872     bind : function(store){
50873         this.bindStore(store);
50874     },
50875
50876     // private
50877     onDestroy : function(){
50878         this.bindStore(null);
50879         Ext.PagingToolbar.superclass.onDestroy.call(this);
50880     }
50881 });
50882
50883 })();
50884 Ext.reg('paging', Ext.PagingToolbar);/**
50885  * @class Ext.History
50886  * @extends Ext.util.Observable
50887  * History management component that allows you to register arbitrary tokens that signify application
50888  * history state on navigation actions.  You can then handle the history {@link #change} event in order
50889  * to reset your application UI to the appropriate state when the user navigates forward or backward through
50890  * the browser history stack.
50891  * @singleton
50892  */
50893 Ext.History = (function () {
50894     var iframe, hiddenField;
50895     var ready = false;
50896     var currentToken;
50897
50898     function getHash() {
50899         var href = location.href, i = href.indexOf("#");
50900         return i >= 0 ? href.substr(i + 1) : null;
50901     }
50902
50903     function doSave() {
50904         hiddenField.value = currentToken;
50905     }
50906
50907     function handleStateChange(token) {
50908         currentToken = token;
50909         Ext.History.fireEvent('change', token);
50910     }
50911
50912     function updateIFrame (token) {
50913         var html = ['<html><body><div id="state">',Ext.util.Format.htmlEncode(token),'</div></body></html>'].join('');
50914         try {
50915             var doc = iframe.contentWindow.document;
50916             doc.open();
50917             doc.write(html);
50918             doc.close();
50919             return true;
50920         } catch (e) {
50921             return false;
50922         }
50923     }
50924
50925     function checkIFrame() {
50926         if (!iframe.contentWindow || !iframe.contentWindow.document) {
50927             setTimeout(checkIFrame, 10);
50928             return;
50929         }
50930
50931         var doc = iframe.contentWindow.document;
50932         var elem = doc.getElementById("state");
50933         var token = elem ? elem.innerText : null;
50934
50935         var hash = getHash();
50936
50937         setInterval(function () {
50938
50939             doc = iframe.contentWindow.document;
50940             elem = doc.getElementById("state");
50941
50942             var newtoken = elem ? elem.innerText : null;
50943
50944             var newHash = getHash();
50945
50946             if (newtoken !== token) {
50947                 token = newtoken;
50948                 handleStateChange(token);
50949                 top.location.hash = token;
50950                 hash = token;
50951                 doSave();
50952             } else if (newHash !== hash) {
50953                 hash = newHash;
50954                 updateIFrame(newHash);
50955             }
50956
50957         }, 50);
50958
50959         ready = true;
50960
50961         Ext.History.fireEvent('ready', Ext.History);
50962     }
50963
50964     function startUp() {
50965         currentToken = hiddenField.value ? hiddenField.value : getHash();
50966
50967         if (Ext.isIE) {
50968             checkIFrame();
50969         } else {
50970             var hash = getHash();
50971             setInterval(function () {
50972                 var newHash = getHash();
50973                 if (newHash !== hash) {
50974                     hash = newHash;
50975                     handleStateChange(hash);
50976                     doSave();
50977                 }
50978             }, 50);
50979             ready = true;
50980             Ext.History.fireEvent('ready', Ext.History);
50981         }
50982     }
50983
50984     return {
50985         /**
50986          * The id of the hidden field required for storing the current history token.
50987          * @type String
50988          * @property
50989          */
50990         fieldId: 'x-history-field',
50991         /**
50992          * The id of the iframe required by IE to manage the history stack.
50993          * @type String
50994          * @property
50995          */
50996         iframeId: 'x-history-frame',
50997
50998         events:{},
50999
51000         /**
51001          * Initialize the global History instance.
51002          * @param {Boolean} onReady (optional) A callback function that will be called once the history
51003          * component is fully initialized.
51004          * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to the browser window.
51005          */
51006         init: function (onReady, scope) {
51007             if(ready) {
51008                 Ext.callback(onReady, scope, [this]);
51009                 return;
51010             }
51011             if(!Ext.isReady){
51012                 Ext.onReady(function(){
51013                     Ext.History.init(onReady, scope);
51014                 });
51015                 return;
51016             }
51017             hiddenField = Ext.getDom(Ext.History.fieldId);
51018             if (Ext.isIE) {
51019                 iframe = Ext.getDom(Ext.History.iframeId);
51020             }
51021             this.addEvents(
51022                 /**
51023                  * @event ready
51024                  * Fires when the Ext.History singleton has been initialized and is ready for use.
51025                  * @param {Ext.History} The Ext.History singleton.
51026                  */
51027                 'ready',
51028                 /**
51029                  * @event change
51030                  * Fires when navigation back or forwards within the local page's history occurs.
51031                  * @param {String} token An identifier associated with the page state at that point in its history.
51032                  */
51033                 'change'
51034             );
51035             if(onReady){
51036                 this.on('ready', onReady, scope, {single:true});
51037             }
51038             startUp();
51039         },
51040
51041         /**
51042          * Add a new token to the history stack. This can be any arbitrary value, although it would
51043          * commonly be the concatenation of a component id and another id marking the specifc history
51044          * state of that component.  Example usage:
51045          * <pre><code>
51046 // Handle tab changes on a TabPanel
51047 tabPanel.on('tabchange', function(tabPanel, tab){
51048     Ext.History.add(tabPanel.id + ':' + tab.id);
51049 });
51050 </code></pre>
51051          * @param {String} token The value that defines a particular application-specific history state
51052          * @param {Boolean} preventDuplicates When true, if the passed token matches the current token
51053          * it will not save a new history step. Set to false if the same state can be saved more than once
51054          * at the same history stack location (defaults to true).
51055          */
51056         add: function (token, preventDup) {
51057             if(preventDup !== false){
51058                 if(this.getToken() == token){
51059                     return true;
51060                 }
51061             }
51062             if (Ext.isIE) {
51063                 return updateIFrame(token);
51064             } else {
51065                 top.location.hash = token;
51066                 return true;
51067             }
51068         },
51069
51070         /**
51071          * Programmatically steps back one step in browser history (equivalent to the user pressing the Back button).
51072          */
51073         back: function(){
51074             history.go(-1);
51075         },
51076
51077         /**
51078          * Programmatically steps forward one step in browser history (equivalent to the user pressing the Forward button).
51079          */
51080         forward: function(){
51081             history.go(1);
51082         },
51083
51084         /**
51085          * Retrieves the currently-active history token.
51086          * @return {String} The token
51087          */
51088         getToken: function() {
51089             return ready ? currentToken : getHash();
51090         }
51091     };
51092 })();
51093 Ext.apply(Ext.History, new Ext.util.Observable());/**
51094  * @class Ext.Tip
51095  * @extends Ext.Panel
51096  * @xtype tip
51097  * This is the base class for {@link Ext.QuickTip} and {@link Ext.Tooltip} that provides the basic layout and
51098  * positioning that all tip-based classes require. This class can be used directly for simple, statically-positioned
51099  * tips that are displayed programmatically, or it can be extended to provide custom tip implementations.
51100  * @constructor
51101  * Create a new Tip
51102  * @param {Object} config The configuration options
51103  */
51104 Ext.Tip = Ext.extend(Ext.Panel, {
51105     /**
51106      * @cfg {Boolean} closable True to render a close tool button into the tooltip header (defaults to false).
51107      */
51108     /**
51109      * @cfg {Number} width
51110      * Width in pixels of the tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
51111      * {@link #minWidth} or {@link #maxWidth}.  The maximum supported value is 500.
51112      */
51113     /**
51114      * @cfg {Number} minWidth The minimum width of the tip in pixels (defaults to 40).
51115      */
51116     minWidth : 40,
51117     /**
51118      * @cfg {Number} maxWidth The maximum width of the tip in pixels (defaults to 300).  The maximum supported value is 500.
51119      */
51120     maxWidth : 300,
51121     /**
51122      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
51123      * for bottom-right shadow (defaults to "sides").
51124      */
51125     shadow : "sides",
51126     /**
51127      * @cfg {String} defaultAlign <b>Experimental</b>. The default {@link Ext.Element#alignTo} anchor position value
51128      * for this tip relative to its element of origin (defaults to "tl-bl?").
51129      */
51130     defaultAlign : "tl-bl?",
51131     autoRender: true,
51132     quickShowInterval : 250,
51133
51134     // private panel overrides
51135     frame:true,
51136     hidden:true,
51137     baseCls: 'x-tip',
51138     floating:{shadow:true,shim:true,useDisplay:true,constrain:false},
51139     autoHeight:true,
51140
51141     closeAction: 'hide',
51142
51143     // private
51144     initComponent : function(){
51145         Ext.Tip.superclass.initComponent.call(this);
51146         if(this.closable && !this.title){
51147             this.elements += ',header';
51148         }
51149     },
51150
51151     // private
51152     afterRender : function(){
51153         Ext.Tip.superclass.afterRender.call(this);
51154         if(this.closable){
51155             this.addTool({
51156                 id: 'close',
51157                 handler: this[this.closeAction],
51158                 scope: this
51159             });
51160         }
51161     },
51162
51163     /**
51164      * Shows this tip at the specified XY position.  Example usage:
51165      * <pre><code>
51166 // Show the tip at x:50 and y:100
51167 tip.showAt([50,100]);
51168 </code></pre>
51169      * @param {Array} xy An array containing the x and y coordinates
51170      */
51171     showAt : function(xy){
51172         Ext.Tip.superclass.show.call(this);
51173         if(this.measureWidth !== false && (!this.initialConfig || typeof this.initialConfig.width != 'number')){
51174             this.doAutoWidth();
51175         }
51176         if(this.constrainPosition){
51177             xy = this.el.adjustForConstraints(xy);
51178         }
51179         this.setPagePosition(xy[0], xy[1]);
51180     },
51181
51182     // protected
51183     doAutoWidth : function(adjust){
51184         adjust = adjust || 0;
51185         var bw = this.body.getTextWidth();
51186         if(this.title){
51187             bw = Math.max(bw, this.header.child('span').getTextWidth(this.title));
51188         }
51189         bw += this.getFrameWidth() + (this.closable ? 20 : 0) + this.body.getPadding("lr") + adjust;
51190         this.setWidth(bw.constrain(this.minWidth, this.maxWidth));
51191         
51192         // IE7 repaint bug on initial show
51193         if(Ext.isIE7 && !this.repainted){
51194             this.el.repaint();
51195             this.repainted = true;
51196         }
51197     },
51198
51199     /**
51200      * <b>Experimental</b>. Shows this tip at a position relative to another element using a standard {@link Ext.Element#alignTo}
51201      * anchor position value.  Example usage:
51202      * <pre><code>
51203 // Show the tip at the default position ('tl-br?')
51204 tip.showBy('my-el');
51205
51206 // Show the tip's top-left corner anchored to the element's top-right corner
51207 tip.showBy('my-el', 'tl-tr');
51208 </code></pre>
51209      * @param {Mixed} el An HTMLElement, Ext.Element or string id of the target element to align to
51210      * @param {String} position (optional) A valid {@link Ext.Element#alignTo} anchor position (defaults to 'tl-br?' or
51211      * {@link #defaultAlign} if specified).
51212      */
51213     showBy : function(el, pos){
51214         if(!this.rendered){
51215             this.render(Ext.getBody());
51216         }
51217         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign));
51218     },
51219
51220     initDraggable : function(){
51221         this.dd = new Ext.Tip.DD(this, typeof this.draggable == 'boolean' ? null : this.draggable);
51222         this.header.addClass('x-tip-draggable');
51223     }
51224 });
51225
51226 Ext.reg('tip', Ext.Tip);
51227
51228 // private - custom Tip DD implementation
51229 Ext.Tip.DD = function(tip, config){
51230     Ext.apply(this, config);
51231     this.tip = tip;
51232     Ext.Tip.DD.superclass.constructor.call(this, tip.el.id, 'WindowDD-'+tip.id);
51233     this.setHandleElId(tip.header.id);
51234     this.scroll = false;
51235 };
51236
51237 Ext.extend(Ext.Tip.DD, Ext.dd.DD, {
51238     moveOnly:true,
51239     scroll:false,
51240     headerOffsets:[100, 25],
51241     startDrag : function(){
51242         this.tip.el.disableShadow();
51243     },
51244     endDrag : function(e){
51245         this.tip.el.enableShadow(true);
51246     }
51247 });/**
51248  * @class Ext.ToolTip
51249  * @extends Ext.Tip
51250  * A standard tooltip implementation for providing additional information when hovering over a target element.
51251  * @xtype tooltip
51252  * @constructor
51253  * Create a new Tooltip
51254  * @param {Object} config The configuration options
51255  */
51256 Ext.ToolTip = Ext.extend(Ext.Tip, {
51257     /**
51258      * When a Tooltip is configured with the <code>{@link #delegate}</code>
51259      * option to cause selected child elements of the <code>{@link #target}</code>
51260      * Element to each trigger a seperate show event, this property is set to
51261      * the DOM element which triggered the show.
51262      * @type DOMElement
51263      * @property triggerElement
51264      */
51265     /**
51266      * @cfg {Mixed} target The target HTMLElement, Ext.Element or id to monitor
51267      * for mouseover events to trigger showing this ToolTip.
51268      */
51269     /**
51270      * @cfg {Boolean} autoHide True to automatically hide the tooltip after the
51271      * mouse exits the target element or after the <code>{@link #dismissDelay}</code>
51272      * has expired if set (defaults to true).  If <code>{@link closable} = true</code>
51273      * a close tool button will be rendered into the tooltip header.
51274      */
51275     /**
51276      * @cfg {Number} showDelay Delay in milliseconds before the tooltip displays
51277      * after the mouse enters the target element (defaults to 500)
51278      */
51279     showDelay : 500,
51280     /**
51281      * @cfg {Number} hideDelay Delay in milliseconds after the mouse exits the
51282      * target element but before the tooltip actually hides (defaults to 200).
51283      * Set to 0 for the tooltip to hide immediately.
51284      */
51285     hideDelay : 200,
51286     /**
51287      * @cfg {Number} dismissDelay Delay in milliseconds before the tooltip
51288      * automatically hides (defaults to 5000). To disable automatic hiding, set
51289      * dismissDelay = 0.
51290      */
51291     dismissDelay : 5000,
51292     /**
51293      * @cfg {Array} mouseOffset An XY offset from the mouse position where the
51294      * tooltip should be shown (defaults to [15,18]).
51295      */
51296     /**
51297      * @cfg {Boolean} trackMouse True to have the tooltip follow the mouse as it
51298      * moves over the target element (defaults to false).
51299      */
51300     trackMouse : false,
51301     /**
51302      * @cfg {Boolean} anchorToTarget True to anchor the tooltip to the target
51303      * element, false to anchor it relative to the mouse coordinates (defaults
51304      * to true).  When <code>anchorToTarget</code> is true, use
51305      * <code>{@link #defaultAlign}</code> to control tooltip alignment to the
51306      * target element.  When <code>anchorToTarget</code> is false, use
51307      * <code>{@link #anchorPosition}</code> instead to control alignment.
51308      */
51309     anchorToTarget : true,
51310     /**
51311      * @cfg {Number} anchorOffset A numeric pixel value used to offset the
51312      * default position of the anchor arrow (defaults to 0).  When the anchor
51313      * position is on the top or bottom of the tooltip, <code>anchorOffset</code>
51314      * will be used as a horizontal offset.  Likewise, when the anchor position
51315      * is on the left or right side, <code>anchorOffset</code> will be used as
51316      * a vertical offset.
51317      */
51318     anchorOffset : 0,
51319     /**
51320      * @cfg {String} delegate <p>Optional. A {@link Ext.DomQuery DomQuery}
51321      * selector which allows selection of individual elements within the
51322      * <code>{@link #target}</code> element to trigger showing and hiding the
51323      * ToolTip as the mouse moves within the target.</p>
51324      * <p>When specified, the child element of the target which caused a show
51325      * event is placed into the <code>{@link #triggerElement}</code> property
51326      * before the ToolTip is shown.</p>
51327      * <p>This may be useful when a Component has regular, repeating elements
51328      * in it, each of which need a Tooltip which contains information specific
51329      * to that element. For example:</p><pre><code>
51330 var myGrid = new Ext.grid.gridPanel(gridConfig);
51331 myGrid.on('render', function(grid) {
51332     var store = grid.getStore();  // Capture the Store.
51333     var view = grid.getView();    // Capture the GridView.
51334     myGrid.tip = new Ext.ToolTip({
51335         target: view.mainBody,    // The overall target element.
51336         delegate: '.x-grid3-row', // Each grid row causes its own seperate show and hide.
51337         trackMouse: true,         // Moving within the row should not hide the tip.
51338         renderTo: document.body,  // Render immediately so that tip.body can be
51339                                   //  referenced prior to the first show.
51340         listeners: {              // Change content dynamically depending on which element
51341                                   //  triggered the show.
51342             beforeshow: function updateTipBody(tip) {
51343                 var rowIndex = view.findRowIndex(tip.triggerElement);
51344                 tip.body.dom.innerHTML = 'Over Record ID ' + store.getAt(rowIndex).id;
51345             }
51346         }
51347     });
51348 });
51349      *</code></pre>
51350      */
51351
51352     // private
51353     targetCounter : 0,
51354
51355     constrainPosition : false,
51356
51357     // private
51358     initComponent : function(){
51359         Ext.ToolTip.superclass.initComponent.call(this);
51360         this.lastActive = new Date();
51361         this.initTarget(this.target);
51362         this.origAnchor = this.anchor;
51363     },
51364
51365     // private
51366     onRender : function(ct, position){
51367         Ext.ToolTip.superclass.onRender.call(this, ct, position);
51368         this.anchorCls = 'x-tip-anchor-' + this.getAnchorPosition();
51369         this.anchorEl = this.el.createChild({
51370             cls: 'x-tip-anchor ' + this.anchorCls
51371         });
51372     },
51373
51374     // private
51375     afterRender : function(){
51376         Ext.ToolTip.superclass.afterRender.call(this);
51377         this.anchorEl.setStyle('z-index', this.el.getZIndex() + 1).setVisibilityMode(Ext.Element.DISPLAY);
51378     },
51379
51380     /**
51381      * Binds this ToolTip to the specified element. The tooltip will be displayed when the mouse moves over the element.
51382      * @param {Mixed} t The Element, HtmlElement, or ID of an element to bind to
51383      */
51384     initTarget : function(target){
51385         var t;
51386         if((t = Ext.get(target))){
51387             if(this.target){
51388                 var tg = Ext.get(this.target);
51389                 this.mun(tg, 'mouseover', this.onTargetOver, this);
51390                 this.mun(tg, 'mouseout', this.onTargetOut, this);
51391                 this.mun(tg, 'mousemove', this.onMouseMove, this);
51392             }
51393             this.mon(t, {
51394                 mouseover: this.onTargetOver,
51395                 mouseout: this.onTargetOut,
51396                 mousemove: this.onMouseMove,
51397                 scope: this
51398             });
51399             this.target = t;
51400         }
51401         if(this.anchor){
51402             this.anchorTarget = this.target;
51403         }
51404     },
51405
51406     // private
51407     onMouseMove : function(e){
51408         var t = this.delegate ? e.getTarget(this.delegate) : this.triggerElement = true;
51409         if (t) {
51410             this.targetXY = e.getXY();
51411             if (t === this.triggerElement) {
51412                 if(!this.hidden && this.trackMouse){
51413                     this.setPagePosition(this.getTargetXY());
51414                 }
51415             } else {
51416                 this.hide();
51417                 this.lastActive = new Date(0);
51418                 this.onTargetOver(e);
51419             }
51420         } else if (!this.closable && this.isVisible()) {
51421             this.hide();
51422         }
51423     },
51424
51425     // private
51426     getTargetXY : function(){
51427         if(this.delegate){
51428             this.anchorTarget = this.triggerElement;
51429         }
51430         if(this.anchor){
51431             this.targetCounter++;
51432             var offsets = this.getOffsets(),
51433                 xy = (this.anchorToTarget && !this.trackMouse) ? this.el.getAlignToXY(this.anchorTarget, this.getAnchorAlign()) : this.targetXY,
51434                 dw = Ext.lib.Dom.getViewWidth() - 5,
51435                 dh = Ext.lib.Dom.getViewHeight() - 5,
51436                 de = document.documentElement,
51437                 bd = document.body,
51438                 scrollX = (de.scrollLeft || bd.scrollLeft || 0) + 5,
51439                 scrollY = (de.scrollTop || bd.scrollTop || 0) + 5,
51440                 axy = [xy[0] + offsets[0], xy[1] + offsets[1]],
51441                 sz = this.getSize();
51442                 
51443             this.anchorEl.removeClass(this.anchorCls);
51444
51445             if(this.targetCounter < 2){
51446                 if(axy[0] < scrollX){
51447                     if(this.anchorToTarget){
51448                         this.defaultAlign = 'l-r';
51449                         if(this.mouseOffset){this.mouseOffset[0] *= -1;}
51450                     }
51451                     this.anchor = 'left';
51452                     return this.getTargetXY();
51453                 }
51454                 if(axy[0]+sz.width > dw){
51455                     if(this.anchorToTarget){
51456                         this.defaultAlign = 'r-l';
51457                         if(this.mouseOffset){this.mouseOffset[0] *= -1;}
51458                     }
51459                     this.anchor = 'right';
51460                     return this.getTargetXY();
51461                 }
51462                 if(axy[1] < scrollY){
51463                     if(this.anchorToTarget){
51464                         this.defaultAlign = 't-b';
51465                         if(this.mouseOffset){this.mouseOffset[1] *= -1;}
51466                     }
51467                     this.anchor = 'top';
51468                     return this.getTargetXY();
51469                 }
51470                 if(axy[1]+sz.height > dh){
51471                     if(this.anchorToTarget){
51472                         this.defaultAlign = 'b-t';
51473                         if(this.mouseOffset){this.mouseOffset[1] *= -1;}
51474                     }
51475                     this.anchor = 'bottom';
51476                     return this.getTargetXY();
51477                 }
51478             }
51479
51480             this.anchorCls = 'x-tip-anchor-'+this.getAnchorPosition();
51481             this.anchorEl.addClass(this.anchorCls);
51482             this.targetCounter = 0;
51483             return axy;
51484         }else{
51485             var mouseOffset = this.getMouseOffset();
51486             return [this.targetXY[0]+mouseOffset[0], this.targetXY[1]+mouseOffset[1]];
51487         }
51488     },
51489
51490     getMouseOffset : function(){
51491         var offset = this.anchor ? [0,0] : [15,18];
51492         if(this.mouseOffset){
51493             offset[0] += this.mouseOffset[0];
51494             offset[1] += this.mouseOffset[1];
51495         }
51496         return offset;
51497     },
51498
51499     // private
51500     getAnchorPosition : function(){
51501         if(this.anchor){
51502             this.tipAnchor = this.anchor.charAt(0);
51503         }else{
51504             var m = this.defaultAlign.match(/^([a-z]+)-([a-z]+)(\?)?$/);
51505             if(!m){
51506                throw 'AnchorTip.defaultAlign is invalid';
51507             }
51508             this.tipAnchor = m[1].charAt(0);
51509         }
51510
51511         switch(this.tipAnchor){
51512             case 't': return 'top';
51513             case 'b': return 'bottom';
51514             case 'r': return 'right';
51515         }
51516         return 'left';
51517     },
51518
51519     // private
51520     getAnchorAlign : function(){
51521         switch(this.anchor){
51522             case 'top'  : return 'tl-bl';
51523             case 'left' : return 'tl-tr';
51524             case 'right': return 'tr-tl';
51525             default     : return 'bl-tl';
51526         }
51527     },
51528
51529     // private
51530     getOffsets : function(){
51531         var offsets, 
51532             ap = this.getAnchorPosition().charAt(0);
51533         if(this.anchorToTarget && !this.trackMouse){
51534             switch(ap){
51535                 case 't':
51536                     offsets = [0, 9];
51537                     break;
51538                 case 'b':
51539                     offsets = [0, -13];
51540                     break;
51541                 case 'r':
51542                     offsets = [-13, 0];
51543                     break;
51544                 default:
51545                     offsets = [9, 0];
51546                     break;
51547             }
51548         }else{
51549             switch(ap){
51550                 case 't':
51551                     offsets = [-15-this.anchorOffset, 30];
51552                     break;
51553                 case 'b':
51554                     offsets = [-19-this.anchorOffset, -13-this.el.dom.offsetHeight];
51555                     break;
51556                 case 'r':
51557                     offsets = [-15-this.el.dom.offsetWidth, -13-this.anchorOffset];
51558                     break;
51559                 default:
51560                     offsets = [25, -13-this.anchorOffset];
51561                     break;
51562             }
51563         }
51564         var mouseOffset = this.getMouseOffset();
51565         offsets[0] += mouseOffset[0];
51566         offsets[1] += mouseOffset[1];
51567
51568         return offsets;
51569     },
51570
51571     // private
51572     onTargetOver : function(e){
51573         if(this.disabled || e.within(this.target.dom, true)){
51574             return;
51575         }
51576         var t = e.getTarget(this.delegate);
51577         if (t) {
51578             this.triggerElement = t;
51579             this.clearTimer('hide');
51580             this.targetXY = e.getXY();
51581             this.delayShow();
51582         }
51583     },
51584
51585     // private
51586     delayShow : function(){
51587         if(this.hidden && !this.showTimer){
51588             if(this.lastActive.getElapsed() < this.quickShowInterval){
51589                 this.show();
51590             }else{
51591                 this.showTimer = this.show.defer(this.showDelay, this);
51592             }
51593         }else if(!this.hidden && this.autoHide !== false){
51594             this.show();
51595         }
51596     },
51597
51598     // private
51599     onTargetOut : function(e){
51600         if(this.disabled || e.within(this.target.dom, true)){
51601             return;
51602         }
51603         this.clearTimer('show');
51604         if(this.autoHide !== false){
51605             this.delayHide();
51606         }
51607     },
51608
51609     // private
51610     delayHide : function(){
51611         if(!this.hidden && !this.hideTimer){
51612             this.hideTimer = this.hide.defer(this.hideDelay, this);
51613         }
51614     },
51615
51616     /**
51617      * Hides this tooltip if visible.
51618      */
51619     hide: function(){
51620         this.clearTimer('dismiss');
51621         this.lastActive = new Date();
51622         if(this.anchorEl){
51623             this.anchorEl.hide();
51624         }
51625         Ext.ToolTip.superclass.hide.call(this);
51626         delete this.triggerElement;
51627     },
51628
51629     /**
51630      * Shows this tooltip at the current event target XY position.
51631      */
51632     show : function(){
51633         if(this.anchor){
51634             // pre-show it off screen so that the el will have dimensions
51635             // for positioning calcs when getting xy next
51636             this.showAt([-1000,-1000]);
51637             this.origConstrainPosition = this.constrainPosition;
51638             this.constrainPosition = false;
51639             this.anchor = this.origAnchor;
51640         }
51641         this.showAt(this.getTargetXY());
51642
51643         if(this.anchor){
51644             this.anchorEl.show();
51645             this.syncAnchor();
51646             this.constrainPosition = this.origConstrainPosition;
51647         }else{
51648             this.anchorEl.hide();
51649         }
51650     },
51651
51652     // inherit docs
51653     showAt : function(xy){
51654         this.lastActive = new Date();
51655         this.clearTimers();
51656         Ext.ToolTip.superclass.showAt.call(this, xy);
51657         if(this.dismissDelay && this.autoHide !== false){
51658             this.dismissTimer = this.hide.defer(this.dismissDelay, this);
51659         }
51660         if(this.anchor && !this.anchorEl.isVisible()){
51661             this.syncAnchor();
51662             this.anchorEl.show();
51663         }else{
51664             this.anchorEl.hide();
51665         }
51666     },
51667
51668     // private
51669     syncAnchor : function(){
51670         var anchorPos, targetPos, offset;
51671         switch(this.tipAnchor.charAt(0)){
51672             case 't':
51673                 anchorPos = 'b';
51674                 targetPos = 'tl';
51675                 offset = [20+this.anchorOffset, 2];
51676                 break;
51677             case 'r':
51678                 anchorPos = 'l';
51679                 targetPos = 'tr';
51680                 offset = [-2, 11+this.anchorOffset];
51681                 break;
51682             case 'b':
51683                 anchorPos = 't';
51684                 targetPos = 'bl';
51685                 offset = [20+this.anchorOffset, -2];
51686                 break;
51687             default:
51688                 anchorPos = 'r';
51689                 targetPos = 'tl';
51690                 offset = [2, 11+this.anchorOffset];
51691                 break;
51692         }
51693         this.anchorEl.alignTo(this.el, anchorPos+'-'+targetPos, offset);
51694     },
51695
51696     // private
51697     setPagePosition : function(x, y){
51698         Ext.ToolTip.superclass.setPagePosition.call(this, x, y);
51699         if(this.anchor){
51700             this.syncAnchor();
51701         }
51702     },
51703
51704     // private
51705     clearTimer : function(name){
51706         name = name + 'Timer';
51707         clearTimeout(this[name]);
51708         delete this[name];
51709     },
51710
51711     // private
51712     clearTimers : function(){
51713         this.clearTimer('show');
51714         this.clearTimer('dismiss');
51715         this.clearTimer('hide');
51716     },
51717
51718     // private
51719     onShow : function(){
51720         Ext.ToolTip.superclass.onShow.call(this);
51721         Ext.getDoc().on('mousedown', this.onDocMouseDown, this);
51722     },
51723
51724     // private
51725     onHide : function(){
51726         Ext.ToolTip.superclass.onHide.call(this);
51727         Ext.getDoc().un('mousedown', this.onDocMouseDown, this);
51728     },
51729
51730     // private
51731     onDocMouseDown : function(e){
51732         if(this.autoHide !== true && !this.closable && !e.within(this.el.dom)){
51733             this.disable();
51734             this.doEnable.defer(100, this);
51735         }
51736     },
51737     
51738     // private
51739     doEnable : function(){
51740         if(!this.isDestroyed){
51741             this.enable();
51742         }
51743     },
51744
51745     // private
51746     onDisable : function(){
51747         this.clearTimers();
51748         this.hide();
51749     },
51750
51751     // private
51752     adjustPosition : function(x, y){
51753         if(this.contstrainPosition){
51754             var ay = this.targetXY[1], h = this.getSize().height;
51755             if(y <= ay && (y+h) >= ay){
51756                 y = ay-h-5;
51757             }
51758         }
51759         return {x : x, y: y};
51760     },
51761     
51762     beforeDestroy : function(){
51763         this.clearTimers();
51764         Ext.destroy(this.anchorEl);
51765         delete this.anchorEl;
51766         delete this.target;
51767         delete this.anchorTarget;
51768         delete this.triggerElement;
51769         Ext.ToolTip.superclass.beforeDestroy.call(this);    
51770     },
51771
51772     // private
51773     onDestroy : function(){
51774         Ext.getDoc().un('mousedown', this.onDocMouseDown, this);
51775         Ext.ToolTip.superclass.onDestroy.call(this);
51776     }
51777 });
51778
51779 Ext.reg('tooltip', Ext.ToolTip);/**
51780  * @class Ext.QuickTip
51781  * @extends Ext.ToolTip
51782  * @xtype quicktip
51783  * A specialized tooltip class for tooltips that can be specified in markup and automatically managed by the global
51784  * {@link Ext.QuickTips} instance.  See the QuickTips class header for additional usage details and examples.
51785  * @constructor
51786  * Create a new Tip
51787  * @param {Object} config The configuration options
51788  */
51789 Ext.QuickTip = Ext.extend(Ext.ToolTip, {
51790     /**
51791      * @cfg {Mixed} target The target HTMLElement, Ext.Element or id to associate with this quicktip (defaults to the document).
51792      */
51793     /**
51794      * @cfg {Boolean} interceptTitles True to automatically use the element's DOM title value if available (defaults to false).
51795      */
51796     interceptTitles : false,
51797
51798     // private
51799     tagConfig : {
51800         namespace : "ext",
51801         attribute : "qtip",
51802         width : "qwidth",
51803         target : "target",
51804         title : "qtitle",
51805         hide : "hide",
51806         cls : "qclass",
51807         align : "qalign",
51808         anchor : "anchor"
51809     },
51810
51811     // private
51812     initComponent : function(){
51813         this.target = this.target || Ext.getDoc();
51814         this.targets = this.targets || {};
51815         Ext.QuickTip.superclass.initComponent.call(this);
51816     },
51817
51818     /**
51819      * Configures a new quick tip instance and assigns it to a target element.  The following config values are
51820      * supported (for example usage, see the {@link Ext.QuickTips} class header):
51821      * <div class="mdetail-params"><ul>
51822      * <li>autoHide</li>
51823      * <li>cls</li>
51824      * <li>dismissDelay (overrides the singleton value)</li>
51825      * <li>target (required)</li>
51826      * <li>text (required)</li>
51827      * <li>title</li>
51828      * <li>width</li></ul></div>
51829      * @param {Object} config The config object
51830      */
51831     register : function(config){
51832         var cs = Ext.isArray(config) ? config : arguments;
51833         for(var i = 0, len = cs.length; i < len; i++){
51834             var c = cs[i];
51835             var target = c.target;
51836             if(target){
51837                 if(Ext.isArray(target)){
51838                     for(var j = 0, jlen = target.length; j < jlen; j++){
51839                         this.targets[Ext.id(target[j])] = c;
51840                     }
51841                 } else{
51842                     this.targets[Ext.id(target)] = c;
51843                 }
51844             }
51845         }
51846     },
51847
51848     /**
51849      * Removes this quick tip from its element and destroys it.
51850      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
51851      */
51852     unregister : function(el){
51853         delete this.targets[Ext.id(el)];
51854     },
51855     
51856     /**
51857      * Hides a visible tip or cancels an impending show for a particular element.
51858      * @param {String/HTMLElement/Element} el The element that is the target of the tip.
51859      */
51860     cancelShow: function(el){
51861         var at = this.activeTarget;
51862         el = Ext.get(el).dom;
51863         if(this.isVisible()){
51864             if(at && at.el == el){
51865                 this.hide();
51866             }
51867         }else if(at && at.el == el){
51868             this.clearTimer('show');
51869         }
51870     },
51871     
51872     getTipCfg: function(e) {
51873         var t = e.getTarget(), 
51874             ttp, 
51875             cfg;
51876         if(this.interceptTitles && t.title && Ext.isString(t.title)){
51877             ttp = t.title;
51878             t.qtip = ttp;
51879             t.removeAttribute("title");
51880             e.preventDefault();
51881         }else{
51882             cfg = this.tagConfig;
51883             ttp = t.qtip || Ext.fly(t).getAttribute(cfg.attribute, cfg.namespace);
51884         }
51885         return ttp;
51886     },
51887
51888     // private
51889     onTargetOver : function(e){
51890         if(this.disabled){
51891             return;
51892         }
51893         this.targetXY = e.getXY();
51894         var t = e.getTarget();
51895         if(!t || t.nodeType !== 1 || t == document || t == document.body){
51896             return;
51897         }
51898         if(this.activeTarget && ((t == this.activeTarget.el) || Ext.fly(this.activeTarget.el).contains(t))){
51899             this.clearTimer('hide');
51900             this.show();
51901             return;
51902         }
51903         if(t && this.targets[t.id]){
51904             this.activeTarget = this.targets[t.id];
51905             this.activeTarget.el = t;
51906             this.anchor = this.activeTarget.anchor;
51907             if(this.anchor){
51908                 this.anchorTarget = t;
51909             }
51910             this.delayShow();
51911             return;
51912         }
51913         var ttp, et = Ext.fly(t), cfg = this.tagConfig, ns = cfg.namespace;
51914         if(ttp = this.getTipCfg(e)){
51915             var autoHide = et.getAttribute(cfg.hide, ns);
51916             this.activeTarget = {
51917                 el: t,
51918                 text: ttp,
51919                 width: et.getAttribute(cfg.width, ns),
51920                 autoHide: autoHide != "user" && autoHide !== 'false',
51921                 title: et.getAttribute(cfg.title, ns),
51922                 cls: et.getAttribute(cfg.cls, ns),
51923                 align: et.getAttribute(cfg.align, ns)
51924                 
51925             };
51926             this.anchor = et.getAttribute(cfg.anchor, ns);
51927             if(this.anchor){
51928                 this.anchorTarget = t;
51929             }
51930             this.delayShow();
51931         }
51932     },
51933
51934     // private
51935     onTargetOut : function(e){
51936
51937         // If moving within the current target, and it does not have a new tip, ignore the mouseout
51938         if (this.activeTarget && e.within(this.activeTarget.el) && !this.getTipCfg(e)) {
51939             return;
51940         }
51941
51942         this.clearTimer('show');
51943         if(this.autoHide !== false){
51944             this.delayHide();
51945         }
51946     },
51947
51948     // inherit docs
51949     showAt : function(xy){
51950         var t = this.activeTarget;
51951         if(t){
51952             if(!this.rendered){
51953                 this.render(Ext.getBody());
51954                 this.activeTarget = t;
51955             }
51956             if(t.width){
51957                 this.setWidth(t.width);
51958                 this.body.setWidth(this.adjustBodyWidth(t.width - this.getFrameWidth()));
51959                 this.measureWidth = false;
51960             } else{
51961                 this.measureWidth = true;
51962             }
51963             this.setTitle(t.title || '');
51964             this.body.update(t.text);
51965             this.autoHide = t.autoHide;
51966             this.dismissDelay = t.dismissDelay || this.dismissDelay;
51967             if(this.lastCls){
51968                 this.el.removeClass(this.lastCls);
51969                 delete this.lastCls;
51970             }
51971             if(t.cls){
51972                 this.el.addClass(t.cls);
51973                 this.lastCls = t.cls;
51974             }
51975             if(this.anchor){
51976                 this.constrainPosition = false;
51977             }else if(t.align){ // TODO: this doesn't seem to work consistently
51978                 xy = this.el.getAlignToXY(t.el, t.align);
51979                 this.constrainPosition = false;
51980             }else{
51981                 this.constrainPosition = true;
51982             }
51983         }
51984         Ext.QuickTip.superclass.showAt.call(this, xy);
51985     },
51986
51987     // inherit docs
51988     hide: function(){
51989         delete this.activeTarget;
51990         Ext.QuickTip.superclass.hide.call(this);
51991     }
51992 });
51993 Ext.reg('quicktip', Ext.QuickTip);/**
51994  * @class Ext.QuickTips
51995  * <p>Provides attractive and customizable tooltips for any element. The QuickTips
51996  * singleton is used to configure and manage tooltips globally for multiple elements
51997  * in a generic manner.  To create individual tooltips with maximum customizability,
51998  * you should consider either {@link Ext.Tip} or {@link Ext.ToolTip}.</p>
51999  * <p>Quicktips can be configured via tag attributes directly in markup, or by
52000  * registering quick tips programmatically via the {@link #register} method.</p>
52001  * <p>The singleton's instance of {@link Ext.QuickTip} is available via
52002  * {@link #getQuickTip}, and supports all the methods, and all the all the
52003  * configuration properties of Ext.QuickTip. These settings will apply to all
52004  * tooltips shown by the singleton.</p>
52005  * <p>Below is the summary of the configuration properties which can be used.
52006  * For detailed descriptions see the config options for the {@link Ext.QuickTip QuickTip} class</p>
52007  * <p><b>QuickTips singleton configs (all are optional)</b></p>
52008  * <div class="mdetail-params"><ul><li>dismissDelay</li>
52009  * <li>hideDelay</li>
52010  * <li>maxWidth</li>
52011  * <li>minWidth</li>
52012  * <li>showDelay</li>
52013  * <li>trackMouse</li></ul></div>
52014  * <p><b>Target element configs (optional unless otherwise noted)</b></p>
52015  * <div class="mdetail-params"><ul><li>autoHide</li>
52016  * <li>cls</li>
52017  * <li>dismissDelay (overrides singleton value)</li>
52018  * <li>target (required)</li>
52019  * <li>text (required)</li>
52020  * <li>title</li>
52021  * <li>width</li></ul></div>
52022  * <p>Here is an example showing how some of these config options could be used:</p>
52023  * <pre><code>
52024 // Init the singleton.  Any tag-based quick tips will start working.
52025 Ext.QuickTips.init();
52026
52027 // Apply a set of config properties to the singleton
52028 Ext.apply(Ext.QuickTips.getQuickTip(), {
52029     maxWidth: 200,
52030     minWidth: 100,
52031     showDelay: 50,      // Show 50ms after entering target
52032     trackMouse: true
52033 });
52034
52035 // Manually register a quick tip for a specific element
52036 Ext.QuickTips.register({
52037     target: 'my-div',
52038     title: 'My Tooltip',
52039     text: 'This tooltip was added in code',
52040     width: 100,
52041     dismissDelay: 10000 // Hide after 10 seconds hover
52042 });
52043 </code></pre>
52044  * <p>To register a quick tip in markup, you simply add one or more of the valid QuickTip attributes prefixed with
52045  * the <b>ext:</b> namespace.  The HTML element itself is automatically set as the quick tip target. Here is the summary
52046  * of supported attributes (optional unless otherwise noted):</p>
52047  * <ul><li><b>hide</b>: Specifying "user" is equivalent to setting autoHide = false.  Any other value will be the
52048  * same as autoHide = true.</li>
52049  * <li><b>qclass</b>: A CSS class to be applied to the quick tip (equivalent to the 'cls' target element config).</li>
52050  * <li><b>qtip (required)</b>: The quick tip text (equivalent to the 'text' target element config).</li>
52051  * <li><b>qtitle</b>: The quick tip title (equivalent to the 'title' target element config).</li>
52052  * <li><b>qwidth</b>: The quick tip width (equivalent to the 'width' target element config).</li></ul>
52053  * <p>Here is an example of configuring an HTML element to display a tooltip from markup:</p>
52054  * <pre><code>
52055 // Add a quick tip to an HTML button
52056 &lt;input type="button" value="OK" ext:qtitle="OK Button" ext:qwidth="100"
52057      ext:qtip="This is a quick tip from markup!">&lt;/input>
52058 </code></pre>
52059  * @singleton
52060  */
52061 Ext.QuickTips = function(){
52062     var tip,
52063         disabled = false;
52064         
52065     return {
52066         /**
52067          * Initialize the global QuickTips instance and prepare any quick tips.
52068          * @param {Boolean} autoRender True to render the QuickTips container immediately to preload images. (Defaults to true) 
52069          */
52070         init : function(autoRender){
52071             if(!tip){
52072                 if(!Ext.isReady){
52073                     Ext.onReady(function(){
52074                         Ext.QuickTips.init(autoRender);
52075                     });
52076                     return;
52077                 }
52078                 tip = new Ext.QuickTip({
52079                     elements:'header,body', 
52080                     disabled: disabled
52081                 });
52082                 if(autoRender !== false){
52083                     tip.render(Ext.getBody());
52084                 }
52085             }
52086         },
52087         
52088         // Protected method called by the dd classes
52089         ddDisable : function(){
52090             // don't disable it if we don't need to
52091             if(tip && !disabled){
52092                 tip.disable();
52093             }    
52094         },
52095         
52096         // Protected method called by the dd classes
52097         ddEnable : function(){
52098             // only enable it if it hasn't been disabled
52099             if(tip && !disabled){
52100                 tip.enable();
52101             }
52102         },
52103
52104         /**
52105          * Enable quick tips globally.
52106          */
52107         enable : function(){
52108             if(tip){
52109                 tip.enable();
52110             }
52111             disabled = false;
52112         },
52113
52114         /**
52115          * Disable quick tips globally.
52116          */
52117         disable : function(){
52118             if(tip){
52119                 tip.disable();
52120             }
52121             disabled = true;
52122         },
52123
52124         /**
52125          * Returns true if quick tips are enabled, else false.
52126          * @return {Boolean}
52127          */
52128         isEnabled : function(){
52129             return tip !== undefined && !tip.disabled;
52130         },
52131
52132         /**
52133          * Gets the single {@link Ext.QuickTip QuickTip} instance used to show tips from all registered elements.
52134          * @return {Ext.QuickTip}
52135          */
52136         getQuickTip : function(){
52137             return tip;
52138         },
52139
52140         /**
52141          * Configures a new quick tip instance and assigns it to a target element.  See
52142          * {@link Ext.QuickTip#register} for details.
52143          * @param {Object} config The config object
52144          */
52145         register : function(){
52146             tip.register.apply(tip, arguments);
52147         },
52148
52149         /**
52150          * Removes any registered quick tip from the target element and destroys it.
52151          * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
52152          */
52153         unregister : function(){
52154             tip.unregister.apply(tip, arguments);
52155         },
52156
52157         /**
52158          * Alias of {@link #register}.
52159          * @param {Object} config The config object
52160          */
52161         tips : function(){
52162             tip.register.apply(tip, arguments);
52163         }
52164     };
52165 }();/**
52166  * @class Ext.slider.Tip
52167  * @extends Ext.Tip
52168  * Simple plugin for using an Ext.Tip with a slider to show the slider value. Example usage:
52169 <pre>
52170 new Ext.Slider({
52171     width: 214,
52172     minValue: 0,
52173     maxValue: 100,
52174     plugins: new Ext.slider.Tip()
52175 });
52176 </pre>
52177  * Optionally provide your own tip text by overriding getText:
52178  <pre>
52179  new Ext.Slider({
52180      width: 214,
52181      minValue: 0,
52182      maxValue: 100,
52183      plugins: new Ext.slider.Tip({
52184          getText: function(thumb){
52185              return String.format('<b>{0}% complete</b>', thumb.value);
52186          }
52187      })
52188  });
52189  </pre>
52190  */
52191 Ext.slider.Tip = Ext.extend(Ext.Tip, {
52192     minWidth: 10,
52193     offsets : [0, -10],
52194     
52195     init: function(slider) {
52196         slider.on({
52197             scope    : this,
52198             dragstart: this.onSlide,
52199             drag     : this.onSlide,
52200             dragend  : this.hide,
52201             destroy  : this.destroy
52202         });
52203     },
52204     
52205     /**
52206      * @private
52207      * Called whenever a dragstart or drag event is received on the associated Thumb. 
52208      * Aligns the Tip with the Thumb's new position.
52209      * @param {Ext.slider.MultiSlider} slider The slider
52210      * @param {Ext.EventObject} e The Event object
52211      * @param {Ext.slider.Thumb} thumb The thumb that the Tip is attached to
52212      */
52213     onSlide : function(slider, e, thumb) {
52214         this.show();
52215         this.body.update(this.getText(thumb));
52216         this.doAutoWidth();
52217         this.el.alignTo(thumb.el, 'b-t?', this.offsets);
52218     },
52219
52220     /**
52221      * Used to create the text that appears in the Tip's body. By default this just returns
52222      * the value of the Slider Thumb that the Tip is attached to. Override to customize.
52223      * @param {Ext.slider.Thumb} thumb The Thumb that the Tip is attached to
52224      * @return {String} The text to display in the tip
52225      */
52226     getText : function(thumb) {
52227         return String(thumb.value);
52228     }
52229 });
52230
52231 //backwards compatibility - SliderTip used to be a ux before 3.2
52232 Ext.ux.SliderTip = Ext.slider.Tip;/**
52233  * @class Ext.tree.TreePanel
52234  * @extends Ext.Panel
52235  * <p>The TreePanel provides tree-structured UI representation of tree-structured data.</p>
52236  * <p>{@link Ext.tree.TreeNode TreeNode}s added to the TreePanel may each contain metadata
52237  * used by your application in their {@link Ext.tree.TreeNode#attributes attributes} property.</p>
52238  * <p><b>A TreePanel must have a {@link #root} node before it is rendered.</b> This may either be
52239  * specified using the {@link #root} config option, or using the {@link #setRootNode} method.
52240  * <p>An example of tree rendered to an existing div:</p><pre><code>
52241 var tree = new Ext.tree.TreePanel({
52242     renderTo: 'tree-div',
52243     useArrows: true,
52244     autoScroll: true,
52245     animate: true,
52246     enableDD: true,
52247     containerScroll: true,
52248     border: false,
52249     // auto create TreeLoader
52250     dataUrl: 'get-nodes.php',
52251
52252     root: {
52253         nodeType: 'async',
52254         text: 'Ext JS',
52255         draggable: false,
52256         id: 'source'
52257     }
52258 });
52259
52260 tree.getRootNode().expand();
52261  * </code></pre>
52262  * <p>The example above would work with a data packet similar to this:</p><pre><code>
52263 [{
52264     "text": "adapter",
52265     "id": "source\/adapter",
52266     "cls": "folder"
52267 }, {
52268     "text": "dd",
52269     "id": "source\/dd",
52270     "cls": "folder"
52271 }, {
52272     "text": "debug.js",
52273     "id": "source\/debug.js",
52274     "leaf": true,
52275     "cls": "file"
52276 }]
52277  * </code></pre>
52278  * <p>An example of tree within a Viewport:</p><pre><code>
52279 new Ext.Viewport({
52280     layout: 'border',
52281     items: [{
52282         region: 'west',
52283         collapsible: true,
52284         title: 'Navigation',
52285         xtype: 'treepanel',
52286         width: 200,
52287         autoScroll: true,
52288         split: true,
52289         loader: new Ext.tree.TreeLoader(),
52290         root: new Ext.tree.AsyncTreeNode({
52291             expanded: true,
52292             children: [{
52293                 text: 'Menu Option 1',
52294                 leaf: true
52295             }, {
52296                 text: 'Menu Option 2',
52297                 leaf: true
52298             }, {
52299                 text: 'Menu Option 3',
52300                 leaf: true
52301             }]
52302         }),
52303         rootVisible: false,
52304         listeners: {
52305             click: function(n) {
52306                 Ext.Msg.alert('Navigation Tree Click', 'You clicked: "' + n.attributes.text + '"');
52307             }
52308         }
52309     }, {
52310         region: 'center',
52311         xtype: 'tabpanel',
52312         // remaining code not shown ...
52313     }]
52314 });
52315 </code></pre>
52316  *
52317  * @cfg {Ext.tree.TreeNode} root The root node for the tree.
52318  * @cfg {Boolean} rootVisible <tt>false</tt> to hide the root node (defaults to <tt>true</tt>)
52319  * @cfg {Boolean} lines <tt>false</tt> to disable tree lines (defaults to <tt>true</tt>)
52320  * @cfg {Boolean} enableDD <tt>true</tt> to enable drag and drop
52321  * @cfg {Boolean} enableDrag <tt>true</tt> to enable just drag
52322  * @cfg {Boolean} enableDrop <tt>true</tt> to enable just drop
52323  * @cfg {Object} dragConfig Custom config to pass to the {@link Ext.tree.TreeDragZone} instance
52324  * @cfg {Object} dropConfig Custom config to pass to the {@link Ext.tree.TreeDropZone} instance
52325  * @cfg {String} ddGroup The DD group this TreePanel belongs to
52326  * @cfg {Boolean} ddAppendOnly <tt>true</tt> if the tree should only allow append drops (use for trees which are sorted)
52327  * @cfg {Boolean} ddScroll <tt>true</tt> to enable body scrolling
52328  * @cfg {Boolean} containerScroll <tt>true</tt> to register this container with ScrollManager
52329  * @cfg {Boolean} hlDrop <tt>false</tt> to disable node highlight on drop (defaults to the value of {@link Ext#enableFx})
52330  * @cfg {String} hlColor The color of the node highlight (defaults to <tt>'C3DAF9'</tt>)
52331  * @cfg {Boolean} animate <tt>true</tt> to enable animated expand/collapse (defaults to the value of {@link Ext#enableFx})
52332  * @cfg {Boolean} singleExpand <tt>true</tt> if only 1 node per branch may be expanded
52333  * @cfg {Object} selModel A tree selection model to use with this TreePanel (defaults to an {@link Ext.tree.DefaultSelectionModel})
52334  * @cfg {Boolean} trackMouseOver <tt>false</tt> to disable mouse over highlighting
52335  * @cfg {Ext.tree.TreeLoader} loader A {@link Ext.tree.TreeLoader} for use with this TreePanel
52336  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to <tt>'/'</tt>)
52337  * @cfg {Boolean} useArrows <tt>true</tt> to use Vista-style arrows in the tree (defaults to <tt>false</tt>)
52338  * @cfg {String} requestMethod The HTTP request method for loading data (defaults to the value of {@link Ext.Ajax#method}).
52339  *
52340  * @constructor
52341  * @param {Object} config
52342  * @xtype treepanel
52343  */
52344 Ext.tree.TreePanel = Ext.extend(Ext.Panel, {
52345     rootVisible : true,
52346     animate : Ext.enableFx,
52347     lines : true,
52348     enableDD : false,
52349     hlDrop : Ext.enableFx,
52350     pathSeparator : '/',
52351
52352     /**
52353      * @cfg {Array} bubbleEvents
52354      * <p>An array of events that, when fired, should be bubbled to any parent container.
52355      * See {@link Ext.util.Observable#enableBubble}.
52356      * Defaults to <tt>[]</tt>.
52357      */
52358     bubbleEvents : [],
52359
52360     initComponent : function(){
52361         Ext.tree.TreePanel.superclass.initComponent.call(this);
52362
52363         if(!this.eventModel){
52364             this.eventModel = new Ext.tree.TreeEventModel(this);
52365         }
52366
52367         // initialize the loader
52368         var l = this.loader;
52369         if(!l){
52370             l = new Ext.tree.TreeLoader({
52371                 dataUrl: this.dataUrl,
52372                 requestMethod: this.requestMethod
52373             });
52374         }else if(Ext.isObject(l) && !l.load){
52375             l = new Ext.tree.TreeLoader(l);
52376         }
52377         this.loader = l;
52378
52379         this.nodeHash = {};
52380
52381         /**
52382         * The root node of this tree.
52383         * @type Ext.tree.TreeNode
52384         * @property root
52385         */
52386         if(this.root){
52387             var r = this.root;
52388             delete this.root;
52389             this.setRootNode(r);
52390         }
52391
52392
52393         this.addEvents(
52394
52395             /**
52396             * @event append
52397             * Fires when a new child node is appended to a node in this tree.
52398             * @param {Tree} tree The owner tree
52399             * @param {Node} parent The parent node
52400             * @param {Node} node The newly appended node
52401             * @param {Number} index The index of the newly appended node
52402             */
52403            'append',
52404            /**
52405             * @event remove
52406             * Fires when a child node is removed from a node in this tree.
52407             * @param {Tree} tree The owner tree
52408             * @param {Node} parent The parent node
52409             * @param {Node} node The child node removed
52410             */
52411            'remove',
52412            /**
52413             * @event movenode
52414             * Fires when a node is moved to a new location in the tree
52415             * @param {Tree} tree The owner tree
52416             * @param {Node} node The node moved
52417             * @param {Node} oldParent The old parent of this node
52418             * @param {Node} newParent The new parent of this node
52419             * @param {Number} index The index it was moved to
52420             */
52421            'movenode',
52422            /**
52423             * @event insert
52424             * Fires when a new child node is inserted in a node in this tree.
52425             * @param {Tree} tree The owner tree
52426             * @param {Node} parent The parent node
52427             * @param {Node} node The child node inserted
52428             * @param {Node} refNode The child node the node was inserted before
52429             */
52430            'insert',
52431            /**
52432             * @event beforeappend
52433             * Fires before a new child is appended to a node in this tree, return false to cancel the append.
52434             * @param {Tree} tree The owner tree
52435             * @param {Node} parent The parent node
52436             * @param {Node} node The child node to be appended
52437             */
52438            'beforeappend',
52439            /**
52440             * @event beforeremove
52441             * Fires before a child is removed from a node in this tree, return false to cancel the remove.
52442             * @param {Tree} tree The owner tree
52443             * @param {Node} parent The parent node
52444             * @param {Node} node The child node to be removed
52445             */
52446            'beforeremove',
52447            /**
52448             * @event beforemovenode
52449             * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
52450             * @param {Tree} tree The owner tree
52451             * @param {Node} node The node being moved
52452             * @param {Node} oldParent The parent of the node
52453             * @param {Node} newParent The new parent the node is moving to
52454             * @param {Number} index The index it is being moved to
52455             */
52456            'beforemovenode',
52457            /**
52458             * @event beforeinsert
52459             * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
52460             * @param {Tree} tree The owner tree
52461             * @param {Node} parent The parent node
52462             * @param {Node} node The child node to be inserted
52463             * @param {Node} refNode The child node the node is being inserted before
52464             */
52465             'beforeinsert',
52466
52467             /**
52468             * @event beforeload
52469             * Fires before a node is loaded, return false to cancel
52470             * @param {Node} node The node being loaded
52471             */
52472             'beforeload',
52473             /**
52474             * @event load
52475             * Fires when a node is loaded
52476             * @param {Node} node The node that was loaded
52477             */
52478             'load',
52479             /**
52480             * @event textchange
52481             * Fires when the text for a node is changed
52482             * @param {Node} node The node
52483             * @param {String} text The new text
52484             * @param {String} oldText The old text
52485             */
52486             'textchange',
52487             /**
52488             * @event beforeexpandnode
52489             * Fires before a node is expanded, return false to cancel.
52490             * @param {Node} node The node
52491             * @param {Boolean} deep
52492             * @param {Boolean} anim
52493             */
52494             'beforeexpandnode',
52495             /**
52496             * @event beforecollapsenode
52497             * Fires before a node is collapsed, return false to cancel.
52498             * @param {Node} node The node
52499             * @param {Boolean} deep
52500             * @param {Boolean} anim
52501             */
52502             'beforecollapsenode',
52503             /**
52504             * @event expandnode
52505             * Fires when a node is expanded
52506             * @param {Node} node The node
52507             */
52508             'expandnode',
52509             /**
52510             * @event disabledchange
52511             * Fires when the disabled status of a node changes
52512             * @param {Node} node The node
52513             * @param {Boolean} disabled
52514             */
52515             'disabledchange',
52516             /**
52517             * @event collapsenode
52518             * Fires when a node is collapsed
52519             * @param {Node} node The node
52520             */
52521             'collapsenode',
52522             /**
52523             * @event beforeclick
52524             * Fires before click processing on a node. Return false to cancel the default action.
52525             * @param {Node} node The node
52526             * @param {Ext.EventObject} e The event object
52527             */
52528             'beforeclick',
52529             /**
52530             * @event click
52531             * Fires when a node is clicked
52532             * @param {Node} node The node
52533             * @param {Ext.EventObject} e The event object
52534             */
52535             'click',
52536             /**
52537             * @event containerclick
52538             * Fires when the tree container is clicked
52539             * @param {Tree} this
52540             * @param {Ext.EventObject} e The event object
52541             */
52542             'containerclick',
52543             /**
52544             * @event checkchange
52545             * Fires when a node with a checkbox's checked property changes
52546             * @param {Node} this This node
52547             * @param {Boolean} checked
52548             */
52549             'checkchange',
52550             /**
52551             * @event beforedblclick
52552             * Fires before double click processing on a node. Return false to cancel the default action.
52553             * @param {Node} node The node
52554             * @param {Ext.EventObject} e The event object
52555             */
52556             'beforedblclick',
52557             /**
52558             * @event dblclick
52559             * Fires when a node is double clicked
52560             * @param {Node} node The node
52561             * @param {Ext.EventObject} e The event object
52562             */
52563             'dblclick',
52564             /**
52565             * @event containerdblclick
52566             * Fires when the tree container is double clicked
52567             * @param {Tree} this
52568             * @param {Ext.EventObject} e The event object
52569             */
52570             'containerdblclick',
52571             /**
52572             * @event contextmenu
52573             * Fires when a node is right clicked. To display a context menu in response to this
52574             * event, first create a Menu object (see {@link Ext.menu.Menu} for details), then add
52575             * a handler for this event:<pre><code>
52576 new Ext.tree.TreePanel({
52577     title: 'My TreePanel',
52578     root: new Ext.tree.AsyncTreeNode({
52579         text: 'The Root',
52580         children: [
52581             { text: 'Child node 1', leaf: true },
52582             { text: 'Child node 2', leaf: true }
52583         ]
52584     }),
52585     contextMenu: new Ext.menu.Menu({
52586         items: [{
52587             id: 'delete-node',
52588             text: 'Delete Node'
52589         }],
52590         listeners: {
52591             itemclick: function(item) {
52592                 switch (item.id) {
52593                     case 'delete-node':
52594                         var n = item.parentMenu.contextNode;
52595                         if (n.parentNode) {
52596                             n.remove();
52597                         }
52598                         break;
52599                 }
52600             }
52601         }
52602     }),
52603     listeners: {
52604         contextmenu: function(node, e) {
52605 //          Register the context node with the menu so that a Menu Item's handler function can access
52606 //          it via its {@link Ext.menu.BaseItem#parentMenu parentMenu} property.
52607             node.select();
52608             var c = node.getOwnerTree().contextMenu;
52609             c.contextNode = node;
52610             c.showAt(e.getXY());
52611         }
52612     }
52613 });
52614 </code></pre>
52615             * @param {Node} node The node
52616             * @param {Ext.EventObject} e The event object
52617             */
52618             'contextmenu',
52619             /**
52620             * @event containercontextmenu
52621             * Fires when the tree container is right clicked
52622             * @param {Tree} this
52623             * @param {Ext.EventObject} e The event object
52624             */
52625             'containercontextmenu',
52626             /**
52627             * @event beforechildrenrendered
52628             * Fires right before the child nodes for a node are rendered
52629             * @param {Node} node The node
52630             */
52631             'beforechildrenrendered',
52632            /**
52633              * @event startdrag
52634              * Fires when a node starts being dragged
52635              * @param {Ext.tree.TreePanel} this
52636              * @param {Ext.tree.TreeNode} node
52637              * @param {event} e The raw browser event
52638              */
52639             'startdrag',
52640             /**
52641              * @event enddrag
52642              * Fires when a drag operation is complete
52643              * @param {Ext.tree.TreePanel} this
52644              * @param {Ext.tree.TreeNode} node
52645              * @param {event} e The raw browser event
52646              */
52647             'enddrag',
52648             /**
52649              * @event dragdrop
52650              * Fires when a dragged node is dropped on a valid DD target
52651              * @param {Ext.tree.TreePanel} this
52652              * @param {Ext.tree.TreeNode} node
52653              * @param {DD} dd The dd it was dropped on
52654              * @param {event} e The raw browser event
52655              */
52656             'dragdrop',
52657             /**
52658              * @event beforenodedrop
52659              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
52660              * passed to handlers has the following properties:<br />
52661              * <ul style="padding:5px;padding-left:16px;">
52662              * <li>tree - The TreePanel</li>
52663              * <li>target - The node being targeted for the drop</li>
52664              * <li>data - The drag data from the drag source</li>
52665              * <li>point - The point of the drop - append, above or below</li>
52666              * <li>source - The drag source</li>
52667              * <li>rawEvent - Raw mouse event</li>
52668              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
52669              * to be inserted by setting them on this object.</li>
52670              * <li>cancel - Set this to true to cancel the drop.</li>
52671              * <li>dropStatus - If the default drop action is cancelled but the drop is valid, setting this to true
52672              * will prevent the animated 'repair' from appearing.</li>
52673              * </ul>
52674              * @param {Object} dropEvent
52675              */
52676             'beforenodedrop',
52677             /**
52678              * @event nodedrop
52679              * Fires after a DD object is dropped on a node in this tree. The dropEvent
52680              * passed to handlers has the following properties:<br />
52681              * <ul style="padding:5px;padding-left:16px;">
52682              * <li>tree - The TreePanel</li>
52683              * <li>target - The node being targeted for the drop</li>
52684              * <li>data - The drag data from the drag source</li>
52685              * <li>point - The point of the drop - append, above or below</li>
52686              * <li>source - The drag source</li>
52687              * <li>rawEvent - Raw mouse event</li>
52688              * <li>dropNode - Dropped node(s).</li>
52689              * </ul>
52690              * @param {Object} dropEvent
52691              */
52692             'nodedrop',
52693              /**
52694              * @event nodedragover
52695              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
52696              * passed to handlers has the following properties:<br />
52697              * <ul style="padding:5px;padding-left:16px;">
52698              * <li>tree - The TreePanel</li>
52699              * <li>target - The node being targeted for the drop</li>
52700              * <li>data - The drag data from the drag source</li>
52701              * <li>point - The point of the drop - append, above or below</li>
52702              * <li>source - The drag source</li>
52703              * <li>rawEvent - Raw mouse event</li>
52704              * <li>dropNode - Drop node(s) provided by the source.</li>
52705              * <li>cancel - Set this to true to signal drop not allowed.</li>
52706              * </ul>
52707              * @param {Object} dragOverEvent
52708              */
52709             'nodedragover'
52710         );
52711         if(this.singleExpand){
52712             this.on('beforeexpandnode', this.restrictExpand, this);
52713         }
52714     },
52715
52716     // private
52717     proxyNodeEvent : function(ename, a1, a2, a3, a4, a5, a6){
52718         if(ename == 'collapse' || ename == 'expand' || ename == 'beforecollapse' || ename == 'beforeexpand' || ename == 'move' || ename == 'beforemove'){
52719             ename = ename+'node';
52720         }
52721         // args inline for performance while bubbling events
52722         return this.fireEvent(ename, a1, a2, a3, a4, a5, a6);
52723     },
52724
52725
52726     /**
52727      * Returns this root node for this tree
52728      * @return {Node}
52729      */
52730     getRootNode : function(){
52731         return this.root;
52732     },
52733
52734     /**
52735      * Sets the root node for this tree. If the TreePanel has already rendered a root node, the
52736      * previous root node (and all of its descendants) are destroyed before the new root node is rendered.
52737      * @param {Node} node
52738      * @return {Node}
52739      */
52740     setRootNode : function(node){
52741         this.destroyRoot();
52742         if(!node.render){ // attributes passed
52743             node = this.loader.createNode(node);
52744         }
52745         this.root = node;
52746         node.ownerTree = this;
52747         node.isRoot = true;
52748         this.registerNode(node);
52749         if(!this.rootVisible){
52750             var uiP = node.attributes.uiProvider;
52751             node.ui = uiP ? new uiP(node) : new Ext.tree.RootTreeNodeUI(node);
52752         }
52753         if(this.innerCt){
52754             this.clearInnerCt();
52755             this.renderRoot();
52756         }
52757         return node;
52758     },
52759     
52760     clearInnerCt : function(){
52761         this.innerCt.update('');    
52762     },
52763     
52764     // private
52765     renderRoot : function(){
52766         this.root.render();
52767         if(!this.rootVisible){
52768             this.root.renderChildren();
52769         }
52770     },
52771
52772     /**
52773      * Gets a node in this tree by its id
52774      * @param {String} id
52775      * @return {Node}
52776      */
52777     getNodeById : function(id){
52778         return this.nodeHash[id];
52779     },
52780
52781     // private
52782     registerNode : function(node){
52783         this.nodeHash[node.id] = node;
52784     },
52785
52786     // private
52787     unregisterNode : function(node){
52788         delete this.nodeHash[node.id];
52789     },
52790
52791     // private
52792     toString : function(){
52793         return '[Tree'+(this.id?' '+this.id:'')+']';
52794     },
52795
52796     // private
52797     restrictExpand : function(node){
52798         var p = node.parentNode;
52799         if(p){
52800             if(p.expandedChild && p.expandedChild.parentNode == p){
52801                 p.expandedChild.collapse();
52802             }
52803             p.expandedChild = node;
52804         }
52805     },
52806
52807     /**
52808      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. 'id')
52809      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
52810      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
52811      * @return {Array}
52812      */
52813     getChecked : function(a, startNode){
52814         startNode = startNode || this.root;
52815         var r = [];
52816         var f = function(){
52817             if(this.attributes.checked){
52818                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
52819             }
52820         };
52821         startNode.cascade(f);
52822         return r;
52823     },
52824
52825     /**
52826      * Returns the default {@link Ext.tree.TreeLoader} for this TreePanel.
52827      * @return {Ext.tree.TreeLoader} The TreeLoader for this TreePanel.
52828      */
52829     getLoader : function(){
52830         return this.loader;
52831     },
52832
52833     /**
52834      * Expand all nodes
52835      */
52836     expandAll : function(){
52837         this.root.expand(true);
52838     },
52839
52840     /**
52841      * Collapse all nodes
52842      */
52843     collapseAll : function(){
52844         this.root.collapse(true);
52845     },
52846
52847     /**
52848      * Returns the selection model used by this TreePanel.
52849      * @return {TreeSelectionModel} The selection model used by this TreePanel
52850      */
52851     getSelectionModel : function(){
52852         if(!this.selModel){
52853             this.selModel = new Ext.tree.DefaultSelectionModel();
52854         }
52855         return this.selModel;
52856     },
52857
52858     /**
52859      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Ext.data.Node#getPath}
52860      * @param {String} path
52861      * @param {String} attr (optional) The attribute used in the path (see {@link Ext.data.Node#getPath} for more info)
52862      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
52863      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
52864      */
52865     expandPath : function(path, attr, callback){
52866         if(Ext.isEmpty(path)){
52867             if(callback){
52868                 callback(false, undefined);
52869             }
52870             return;
52871         }
52872         attr = attr || 'id';
52873         var keys = path.split(this.pathSeparator);
52874         var curNode = this.root;
52875         if(curNode.attributes[attr] != keys[1]){ // invalid root
52876             if(callback){
52877                 callback(false, null);
52878             }
52879             return;
52880         }
52881         var index = 1;
52882         var f = function(){
52883             if(++index == keys.length){
52884                 if(callback){
52885                     callback(true, curNode);
52886                 }
52887                 return;
52888             }
52889             var c = curNode.findChild(attr, keys[index]);
52890             if(!c){
52891                 if(callback){
52892                     callback(false, curNode);
52893                 }
52894                 return;
52895             }
52896             curNode = c;
52897             c.expand(false, false, f);
52898         };
52899         curNode.expand(false, false, f);
52900     },
52901
52902     /**
52903      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Ext.data.Node#getPath}
52904      * @param {String} path
52905      * @param {String} attr (optional) The attribute used in the path (see {@link Ext.data.Node#getPath} for more info)
52906      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
52907      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
52908      */
52909     selectPath : function(path, attr, callback){
52910         if(Ext.isEmpty(path)){
52911             if(callback){
52912                 callback(false, undefined);
52913             }
52914             return;
52915         }
52916         attr = attr || 'id';
52917         var keys = path.split(this.pathSeparator),
52918             v = keys.pop();
52919         if(keys.length > 1){
52920             var f = function(success, node){
52921                 if(success && node){
52922                     var n = node.findChild(attr, v);
52923                     if(n){
52924                         n.select();
52925                         if(callback){
52926                             callback(true, n);
52927                         }
52928                     }else if(callback){
52929                         callback(false, n);
52930                     }
52931                 }else{
52932                     if(callback){
52933                         callback(false, n);
52934                     }
52935                 }
52936             };
52937             this.expandPath(keys.join(this.pathSeparator), attr, f);
52938         }else{
52939             this.root.select();
52940             if(callback){
52941                 callback(true, this.root);
52942             }
52943         }
52944     },
52945
52946     /**
52947      * Returns the underlying Element for this tree
52948      * @return {Ext.Element} The Element
52949      */
52950     getTreeEl : function(){
52951         return this.body;
52952     },
52953
52954     // private
52955     onRender : function(ct, position){
52956         Ext.tree.TreePanel.superclass.onRender.call(this, ct, position);
52957         this.el.addClass('x-tree');
52958         this.innerCt = this.body.createChild({tag:'ul',
52959                cls:'x-tree-root-ct ' +
52960                (this.useArrows ? 'x-tree-arrows' : this.lines ? 'x-tree-lines' : 'x-tree-no-lines')});
52961     },
52962
52963     // private
52964     initEvents : function(){
52965         Ext.tree.TreePanel.superclass.initEvents.call(this);
52966
52967         if(this.containerScroll){
52968             Ext.dd.ScrollManager.register(this.body);
52969         }
52970         if((this.enableDD || this.enableDrop) && !this.dropZone){
52971            /**
52972             * The dropZone used by this tree if drop is enabled (see {@link #enableDD} or {@link #enableDrop})
52973             * @property dropZone
52974             * @type Ext.tree.TreeDropZone
52975             */
52976              this.dropZone = new Ext.tree.TreeDropZone(this, this.dropConfig || {
52977                ddGroup: this.ddGroup || 'TreeDD', appendOnly: this.ddAppendOnly === true
52978            });
52979         }
52980         if((this.enableDD || this.enableDrag) && !this.dragZone){
52981            /**
52982             * The dragZone used by this tree if drag is enabled (see {@link #enableDD} or {@link #enableDrag})
52983             * @property dragZone
52984             * @type Ext.tree.TreeDragZone
52985             */
52986             this.dragZone = new Ext.tree.TreeDragZone(this, this.dragConfig || {
52987                ddGroup: this.ddGroup || 'TreeDD',
52988                scroll: this.ddScroll
52989            });
52990         }
52991         this.getSelectionModel().init(this);
52992     },
52993
52994     // private
52995     afterRender : function(){
52996         Ext.tree.TreePanel.superclass.afterRender.call(this);
52997         this.renderRoot();
52998     },
52999
53000     beforeDestroy : function(){
53001         if(this.rendered){
53002             Ext.dd.ScrollManager.unregister(this.body);
53003             Ext.destroy(this.dropZone, this.dragZone);
53004         }
53005         this.destroyRoot();
53006         Ext.destroy(this.loader);
53007         this.nodeHash = this.root = this.loader = null;
53008         Ext.tree.TreePanel.superclass.beforeDestroy.call(this);
53009     },
53010     
53011     /**
53012      * Destroy the root node. Not included by itself because we need to pass the silent parameter.
53013      * @private
53014      */
53015     destroyRoot : function(){
53016         if(this.root && this.root.destroy){
53017             this.root.destroy(true);
53018         }
53019     }
53020
53021     /**
53022      * @cfg {String/Number} activeItem
53023      * @hide
53024      */
53025     /**
53026      * @cfg {Boolean} autoDestroy
53027      * @hide
53028      */
53029     /**
53030      * @cfg {Object/String/Function} autoLoad
53031      * @hide
53032      */
53033     /**
53034      * @cfg {Boolean} autoWidth
53035      * @hide
53036      */
53037     /**
53038      * @cfg {Boolean/Number} bufferResize
53039      * @hide
53040      */
53041     /**
53042      * @cfg {String} defaultType
53043      * @hide
53044      */
53045     /**
53046      * @cfg {Object} defaults
53047      * @hide
53048      */
53049     /**
53050      * @cfg {Boolean} hideBorders
53051      * @hide
53052      */
53053     /**
53054      * @cfg {Mixed} items
53055      * @hide
53056      */
53057     /**
53058      * @cfg {String} layout
53059      * @hide
53060      */
53061     /**
53062      * @cfg {Object} layoutConfig
53063      * @hide
53064      */
53065     /**
53066      * @cfg {Boolean} monitorResize
53067      * @hide
53068      */
53069     /**
53070      * @property items
53071      * @hide
53072      */
53073     /**
53074      * @method cascade
53075      * @hide
53076      */
53077     /**
53078      * @method doLayout
53079      * @hide
53080      */
53081     /**
53082      * @method find
53083      * @hide
53084      */
53085     /**
53086      * @method findBy
53087      * @hide
53088      */
53089     /**
53090      * @method findById
53091      * @hide
53092      */
53093     /**
53094      * @method findByType
53095      * @hide
53096      */
53097     /**
53098      * @method getComponent
53099      * @hide
53100      */
53101     /**
53102      * @method getLayout
53103      * @hide
53104      */
53105     /**
53106      * @method getUpdater
53107      * @hide
53108      */
53109     /**
53110      * @method insert
53111      * @hide
53112      */
53113     /**
53114      * @method load
53115      * @hide
53116      */
53117     /**
53118      * @method remove
53119      * @hide
53120      */
53121     /**
53122      * @event add
53123      * @hide
53124      */
53125     /**
53126      * @method removeAll
53127      * @hide
53128      */
53129     /**
53130      * @event afterLayout
53131      * @hide
53132      */
53133     /**
53134      * @event beforeadd
53135      * @hide
53136      */
53137     /**
53138      * @event beforeremove
53139      * @hide
53140      */
53141     /**
53142      * @event remove
53143      * @hide
53144      */
53145
53146
53147
53148     /**
53149      * @cfg {String} allowDomMove  @hide
53150      */
53151     /**
53152      * @cfg {String} autoEl @hide
53153      */
53154     /**
53155      * @cfg {String} applyTo  @hide
53156      */
53157     /**
53158      * @cfg {String} contentEl  @hide
53159      */
53160     /**
53161      * @cfg {Mixed} data  @hide
53162      */
53163     /**
53164      * @cfg {Mixed} tpl  @hide
53165      */
53166     /**
53167      * @cfg {String} tplWriteMode  @hide
53168      */
53169     /**
53170      * @cfg {String} disabledClass  @hide
53171      */
53172     /**
53173      * @cfg {String} elements  @hide
53174      */
53175     /**
53176      * @cfg {String} html  @hide
53177      */
53178     /**
53179      * @cfg {Boolean} preventBodyReset
53180      * @hide
53181      */
53182     /**
53183      * @property disabled
53184      * @hide
53185      */
53186     /**
53187      * @method applyToMarkup
53188      * @hide
53189      */
53190     /**
53191      * @method enable
53192      * @hide
53193      */
53194     /**
53195      * @method disable
53196      * @hide
53197      */
53198     /**
53199      * @method setDisabled
53200      * @hide
53201      */
53202 });
53203
53204 Ext.tree.TreePanel.nodeTypes = {};
53205
53206 Ext.reg('treepanel', Ext.tree.TreePanel);Ext.tree.TreeEventModel = function(tree){
53207     this.tree = tree;
53208     this.tree.on('render', this.initEvents, this);
53209 };
53210
53211 Ext.tree.TreeEventModel.prototype = {
53212     initEvents : function(){
53213         var t = this.tree;
53214
53215         if(t.trackMouseOver !== false){
53216             t.mon(t.innerCt, {
53217                 scope: this,
53218                 mouseover: this.delegateOver,
53219                 mouseout: this.delegateOut
53220             });
53221         }
53222         t.mon(t.getTreeEl(), {
53223             scope: this,
53224             click: this.delegateClick,
53225             dblclick: this.delegateDblClick,
53226             contextmenu: this.delegateContextMenu
53227         });
53228     },
53229
53230     getNode : function(e){
53231         var t;
53232         if(t = e.getTarget('.x-tree-node-el', 10)){
53233             var id = Ext.fly(t, '_treeEvents').getAttribute('tree-node-id', 'ext');
53234             if(id){
53235                 return this.tree.getNodeById(id);
53236             }
53237         }
53238         return null;
53239     },
53240
53241     getNodeTarget : function(e){
53242         var t = e.getTarget('.x-tree-node-icon', 1);
53243         if(!t){
53244             t = e.getTarget('.x-tree-node-el', 6);
53245         }
53246         return t;
53247     },
53248
53249     delegateOut : function(e, t){
53250         if(!this.beforeEvent(e)){
53251             return;
53252         }
53253         if(e.getTarget('.x-tree-ec-icon', 1)){
53254             var n = this.getNode(e);
53255             this.onIconOut(e, n);
53256             if(n == this.lastEcOver){
53257                 delete this.lastEcOver;
53258             }
53259         }
53260         if((t = this.getNodeTarget(e)) && !e.within(t, true)){
53261             this.onNodeOut(e, this.getNode(e));
53262         }
53263     },
53264
53265     delegateOver : function(e, t){
53266         if(!this.beforeEvent(e)){
53267             return;
53268         }
53269         if(Ext.isGecko && !this.trackingDoc){ // prevent hanging in FF
53270             Ext.getBody().on('mouseover', this.trackExit, this);
53271             this.trackingDoc = true;
53272         }
53273         if(this.lastEcOver){ // prevent hung highlight
53274             this.onIconOut(e, this.lastEcOver);
53275             delete this.lastEcOver;
53276         }
53277         if(e.getTarget('.x-tree-ec-icon', 1)){
53278             this.lastEcOver = this.getNode(e);
53279             this.onIconOver(e, this.lastEcOver);
53280         }
53281         if(t = this.getNodeTarget(e)){
53282             this.onNodeOver(e, this.getNode(e));
53283         }
53284     },
53285
53286     trackExit : function(e){
53287         if(this.lastOverNode){
53288             if(this.lastOverNode.ui && !e.within(this.lastOverNode.ui.getEl())){
53289                 this.onNodeOut(e, this.lastOverNode);
53290             }
53291             delete this.lastOverNode;
53292             Ext.getBody().un('mouseover', this.trackExit, this);
53293             this.trackingDoc = false;
53294         }
53295
53296     },
53297
53298     delegateClick : function(e, t){
53299         if(this.beforeEvent(e)){
53300             if(e.getTarget('input[type=checkbox]', 1)){
53301                 this.onCheckboxClick(e, this.getNode(e));
53302             }else if(e.getTarget('.x-tree-ec-icon', 1)){
53303                 this.onIconClick(e, this.getNode(e));
53304             }else if(this.getNodeTarget(e)){
53305                 this.onNodeClick(e, this.getNode(e));
53306             }
53307         }else{
53308             this.checkContainerEvent(e, 'click');
53309         }
53310     },
53311
53312     delegateDblClick : function(e, t){
53313         if(this.beforeEvent(e)){
53314             if(this.getNodeTarget(e)){
53315                 this.onNodeDblClick(e, this.getNode(e));
53316             }
53317         }else{
53318             this.checkContainerEvent(e, 'dblclick');
53319         }
53320     },
53321
53322     delegateContextMenu : function(e, t){
53323         if(this.beforeEvent(e)){
53324             if(this.getNodeTarget(e)){
53325                 this.onNodeContextMenu(e, this.getNode(e));
53326             }
53327         }else{
53328             this.checkContainerEvent(e, 'contextmenu');
53329         }
53330     },
53331     
53332     checkContainerEvent: function(e, type){
53333         if(this.disabled){
53334             e.stopEvent();
53335             return false;
53336         }
53337         this.onContainerEvent(e, type);    
53338     },
53339
53340     onContainerEvent: function(e, type){
53341         this.tree.fireEvent('container' + type, this.tree, e);
53342     },
53343
53344     onNodeClick : function(e, node){
53345         node.ui.onClick(e);
53346     },
53347
53348     onNodeOver : function(e, node){
53349         this.lastOverNode = node;
53350         node.ui.onOver(e);
53351     },
53352
53353     onNodeOut : function(e, node){
53354         node.ui.onOut(e);
53355     },
53356
53357     onIconOver : function(e, node){
53358         node.ui.addClass('x-tree-ec-over');
53359     },
53360
53361     onIconOut : function(e, node){
53362         node.ui.removeClass('x-tree-ec-over');
53363     },
53364
53365     onIconClick : function(e, node){
53366         node.ui.ecClick(e);
53367     },
53368
53369     onCheckboxClick : function(e, node){
53370         node.ui.onCheckChange(e);
53371     },
53372
53373     onNodeDblClick : function(e, node){
53374         node.ui.onDblClick(e);
53375     },
53376
53377     onNodeContextMenu : function(e, node){
53378         node.ui.onContextMenu(e);
53379     },
53380
53381     beforeEvent : function(e){
53382         var node = this.getNode(e);
53383         if(this.disabled || !node || !node.ui){
53384             e.stopEvent();
53385             return false;
53386         }
53387         return true;
53388     },
53389
53390     disable: function(){
53391         this.disabled = true;
53392     },
53393
53394     enable: function(){
53395         this.disabled = false;
53396     }
53397 };/**
53398  * @class Ext.tree.DefaultSelectionModel
53399  * @extends Ext.util.Observable
53400  * The default single selection for a TreePanel.
53401  */
53402 Ext.tree.DefaultSelectionModel = Ext.extend(Ext.util.Observable, {
53403     
53404     constructor : function(config){
53405         this.selNode = null;
53406    
53407         this.addEvents(
53408             /**
53409              * @event selectionchange
53410              * Fires when the selected node changes
53411              * @param {DefaultSelectionModel} this
53412              * @param {TreeNode} node the new selection
53413              */
53414             'selectionchange',
53415
53416             /**
53417              * @event beforeselect
53418              * Fires before the selected node changes, return false to cancel the change
53419              * @param {DefaultSelectionModel} this
53420              * @param {TreeNode} node the new selection
53421              * @param {TreeNode} node the old selection
53422              */
53423             'beforeselect'
53424         );
53425
53426         Ext.apply(this, config);
53427         Ext.tree.DefaultSelectionModel.superclass.constructor.call(this);    
53428     },
53429     
53430     init : function(tree){
53431         this.tree = tree;
53432         tree.mon(tree.getTreeEl(), 'keydown', this.onKeyDown, this);
53433         tree.on('click', this.onNodeClick, this);
53434     },
53435     
53436     onNodeClick : function(node, e){
53437         this.select(node);
53438     },
53439     
53440     /**
53441      * Select a node.
53442      * @param {TreeNode} node The node to select
53443      * @return {TreeNode} The selected node
53444      */
53445     select : function(node, /* private*/ selectNextNode){
53446         // If node is hidden, select the next node in whatever direction was being moved in.
53447         if (!Ext.fly(node.ui.wrap).isVisible() && selectNextNode) {
53448             return selectNextNode.call(this, node);
53449         }
53450         var last = this.selNode;
53451         if(node == last){
53452             node.ui.onSelectedChange(true);
53453         }else if(this.fireEvent('beforeselect', this, node, last) !== false){
53454             if(last && last.ui){
53455                 last.ui.onSelectedChange(false);
53456             }
53457             this.selNode = node;
53458             node.ui.onSelectedChange(true);
53459             this.fireEvent('selectionchange', this, node, last);
53460         }
53461         return node;
53462     },
53463     
53464     /**
53465      * Deselect a node.
53466      * @param {TreeNode} node The node to unselect
53467      * @param {Boolean} silent True to stop the selectionchange event from firing.
53468      */
53469     unselect : function(node, silent){
53470         if(this.selNode == node){
53471             this.clearSelections(silent);
53472         }    
53473     },
53474     
53475     /**
53476      * Clear all selections
53477      * @param {Boolean} silent True to stop the selectionchange event from firing.
53478      */
53479     clearSelections : function(silent){
53480         var n = this.selNode;
53481         if(n){
53482             n.ui.onSelectedChange(false);
53483             this.selNode = null;
53484             if(silent !== true){
53485                 this.fireEvent('selectionchange', this, null);
53486             }
53487         }
53488         return n;
53489     },
53490     
53491     /**
53492      * Get the selected node
53493      * @return {TreeNode} The selected node
53494      */
53495     getSelectedNode : function(){
53496         return this.selNode;    
53497     },
53498     
53499     /**
53500      * Returns true if the node is selected
53501      * @param {TreeNode} node The node to check
53502      * @return {Boolean}
53503      */
53504     isSelected : function(node){
53505         return this.selNode == node;  
53506     },
53507
53508     /**
53509      * Selects the node above the selected node in the tree, intelligently walking the nodes
53510      * @return TreeNode The new selection
53511      */
53512     selectPrevious : function(/* private */ s){
53513         if(!(s = s || this.selNode || this.lastSelNode)){
53514             return null;
53515         }
53516         // Here we pass in the current function to select to indicate the direction we're moving
53517         var ps = s.previousSibling;
53518         if(ps){
53519             if(!ps.isExpanded() || ps.childNodes.length < 1){
53520                 return this.select(ps, this.selectPrevious);
53521             } else{
53522                 var lc = ps.lastChild;
53523                 while(lc && lc.isExpanded() && Ext.fly(lc.ui.wrap).isVisible() && lc.childNodes.length > 0){
53524                     lc = lc.lastChild;
53525                 }
53526                 return this.select(lc, this.selectPrevious);
53527             }
53528         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
53529             return this.select(s.parentNode, this.selectPrevious);
53530         }
53531         return null;
53532     },
53533
53534     /**
53535      * Selects the node above the selected node in the tree, intelligently walking the nodes
53536      * @return TreeNode The new selection
53537      */
53538     selectNext : function(/* private */ s){
53539         if(!(s = s || this.selNode || this.lastSelNode)){
53540             return null;
53541         }
53542         // Here we pass in the current function to select to indicate the direction we're moving
53543         if(s.firstChild && s.isExpanded() && Ext.fly(s.ui.wrap).isVisible()){
53544              return this.select(s.firstChild, this.selectNext);
53545          }else if(s.nextSibling){
53546              return this.select(s.nextSibling, this.selectNext);
53547          }else if(s.parentNode){
53548             var newS = null;
53549             s.parentNode.bubble(function(){
53550                 if(this.nextSibling){
53551                     newS = this.getOwnerTree().selModel.select(this.nextSibling, this.selectNext);
53552                     return false;
53553                 }
53554             });
53555             return newS;
53556          }
53557         return null;
53558     },
53559
53560     onKeyDown : function(e){
53561         var s = this.selNode || this.lastSelNode;
53562         // undesirable, but required
53563         var sm = this;
53564         if(!s){
53565             return;
53566         }
53567         var k = e.getKey();
53568         switch(k){
53569              case e.DOWN:
53570                  e.stopEvent();
53571                  this.selectNext();
53572              break;
53573              case e.UP:
53574                  e.stopEvent();
53575                  this.selectPrevious();
53576              break;
53577              case e.RIGHT:
53578                  e.preventDefault();
53579                  if(s.hasChildNodes()){
53580                      if(!s.isExpanded()){
53581                          s.expand();
53582                      }else if(s.firstChild){
53583                          this.select(s.firstChild, e);
53584                      }
53585                  }
53586              break;
53587              case e.LEFT:
53588                  e.preventDefault();
53589                  if(s.hasChildNodes() && s.isExpanded()){
53590                      s.collapse();
53591                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
53592                      this.select(s.parentNode, e);
53593                  }
53594              break;
53595         };
53596     }
53597 });
53598
53599 /**
53600  * @class Ext.tree.MultiSelectionModel
53601  * @extends Ext.util.Observable
53602  * Multi selection for a TreePanel.
53603  */
53604 Ext.tree.MultiSelectionModel = Ext.extend(Ext.util.Observable, {
53605     
53606     constructor : function(config){
53607         this.selNodes = [];
53608         this.selMap = {};
53609         this.addEvents(
53610             /**
53611              * @event selectionchange
53612              * Fires when the selected nodes change
53613              * @param {MultiSelectionModel} this
53614              * @param {Array} nodes Array of the selected nodes
53615              */
53616             'selectionchange'
53617         );
53618         Ext.apply(this, config);
53619         Ext.tree.MultiSelectionModel.superclass.constructor.call(this);    
53620     },
53621     
53622     init : function(tree){
53623         this.tree = tree;
53624         tree.mon(tree.getTreeEl(), 'keydown', this.onKeyDown, this);
53625         tree.on('click', this.onNodeClick, this);
53626     },
53627     
53628     onNodeClick : function(node, e){
53629         if(e.ctrlKey && this.isSelected(node)){
53630             this.unselect(node);
53631         }else{
53632             this.select(node, e, e.ctrlKey);
53633         }
53634     },
53635     
53636     /**
53637      * Select a node.
53638      * @param {TreeNode} node The node to select
53639      * @param {EventObject} e (optional) An event associated with the selection
53640      * @param {Boolean} keepExisting True to retain existing selections
53641      * @return {TreeNode} The selected node
53642      */
53643     select : function(node, e, keepExisting){
53644         if(keepExisting !== true){
53645             this.clearSelections(true);
53646         }
53647         if(this.isSelected(node)){
53648             this.lastSelNode = node;
53649             return node;
53650         }
53651         this.selNodes.push(node);
53652         this.selMap[node.id] = node;
53653         this.lastSelNode = node;
53654         node.ui.onSelectedChange(true);
53655         this.fireEvent('selectionchange', this, this.selNodes);
53656         return node;
53657     },
53658     
53659     /**
53660      * Deselect a node.
53661      * @param {TreeNode} node The node to unselect
53662      */
53663     unselect : function(node){
53664         if(this.selMap[node.id]){
53665             node.ui.onSelectedChange(false);
53666             var sn = this.selNodes;
53667             var index = sn.indexOf(node);
53668             if(index != -1){
53669                 this.selNodes.splice(index, 1);
53670             }
53671             delete this.selMap[node.id];
53672             this.fireEvent('selectionchange', this, this.selNodes);
53673         }
53674     },
53675     
53676     /**
53677      * Clear all selections
53678      */
53679     clearSelections : function(suppressEvent){
53680         var sn = this.selNodes;
53681         if(sn.length > 0){
53682             for(var i = 0, len = sn.length; i < len; i++){
53683                 sn[i].ui.onSelectedChange(false);
53684             }
53685             this.selNodes = [];
53686             this.selMap = {};
53687             if(suppressEvent !== true){
53688                 this.fireEvent('selectionchange', this, this.selNodes);
53689             }
53690         }
53691     },
53692     
53693     /**
53694      * Returns true if the node is selected
53695      * @param {TreeNode} node The node to check
53696      * @return {Boolean}
53697      */
53698     isSelected : function(node){
53699         return this.selMap[node.id] ? true : false;  
53700     },
53701     
53702     /**
53703      * Returns an array of the selected nodes
53704      * @return {Array}
53705      */
53706     getSelectedNodes : function(){
53707         return this.selNodes.concat([]);
53708     },
53709
53710     onKeyDown : Ext.tree.DefaultSelectionModel.prototype.onKeyDown,
53711
53712     selectNext : Ext.tree.DefaultSelectionModel.prototype.selectNext,
53713
53714     selectPrevious : Ext.tree.DefaultSelectionModel.prototype.selectPrevious
53715 });/**
53716  * @class Ext.data.Tree
53717  * @extends Ext.util.Observable
53718  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
53719  * in the tree have most standard DOM functionality.
53720  * @constructor
53721  * @param {Node} root (optional) The root node
53722  */
53723 Ext.data.Tree = Ext.extend(Ext.util.Observable, {
53724     
53725     constructor: function(root){
53726         this.nodeHash = {};
53727         /**
53728          * The root node for this tree
53729          * @type Node
53730          */
53731         this.root = null;
53732         if(root){
53733             this.setRootNode(root);
53734         }
53735         this.addEvents(
53736             /**
53737              * @event append
53738              * Fires when a new child node is appended to a node in this tree.
53739              * @param {Tree} tree The owner tree
53740              * @param {Node} parent The parent node
53741              * @param {Node} node The newly appended node
53742              * @param {Number} index The index of the newly appended node
53743              */
53744             "append",
53745             /**
53746              * @event remove
53747              * Fires when a child node is removed from a node in this tree.
53748              * @param {Tree} tree The owner tree
53749              * @param {Node} parent The parent node
53750              * @param {Node} node The child node removed
53751              */
53752             "remove",
53753             /**
53754              * @event move
53755              * Fires when a node is moved to a new location in the tree
53756              * @param {Tree} tree The owner tree
53757              * @param {Node} node The node moved
53758              * @param {Node} oldParent The old parent of this node
53759              * @param {Node} newParent The new parent of this node
53760              * @param {Number} index The index it was moved to
53761              */
53762             "move",
53763             /**
53764              * @event insert
53765              * Fires when a new child node is inserted in a node in this tree.
53766              * @param {Tree} tree The owner tree
53767              * @param {Node} parent The parent node
53768              * @param {Node} node The child node inserted
53769              * @param {Node} refNode The child node the node was inserted before
53770              */
53771             "insert",
53772             /**
53773              * @event beforeappend
53774              * Fires before a new child is appended to a node in this tree, return false to cancel the append.
53775              * @param {Tree} tree The owner tree
53776              * @param {Node} parent The parent node
53777              * @param {Node} node The child node to be appended
53778              */
53779             "beforeappend",
53780             /**
53781              * @event beforeremove
53782              * Fires before a child is removed from a node in this tree, return false to cancel the remove.
53783              * @param {Tree} tree The owner tree
53784              * @param {Node} parent The parent node
53785              * @param {Node} node The child node to be removed
53786              */
53787             "beforeremove",
53788             /**
53789              * @event beforemove
53790              * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
53791              * @param {Tree} tree The owner tree
53792              * @param {Node} node The node being moved
53793              * @param {Node} oldParent The parent of the node
53794              * @param {Node} newParent The new parent the node is moving to
53795              * @param {Number} index The index it is being moved to
53796              */
53797             "beforemove",
53798             /**
53799              * @event beforeinsert
53800              * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
53801              * @param {Tree} tree The owner tree
53802              * @param {Node} parent The parent node
53803              * @param {Node} node The child node to be inserted
53804              * @param {Node} refNode The child node the node is being inserted before
53805              */
53806             "beforeinsert"
53807         );
53808         Ext.data.Tree.superclass.constructor.call(this);        
53809     },
53810     
53811     /**
53812      * @cfg {String} pathSeparator
53813      * The token used to separate paths in node ids (defaults to '/').
53814      */
53815     pathSeparator: "/",
53816
53817     // private
53818     proxyNodeEvent : function(){
53819         return this.fireEvent.apply(this, arguments);
53820     },
53821
53822     /**
53823      * Returns the root node for this tree.
53824      * @return {Node}
53825      */
53826     getRootNode : function(){
53827         return this.root;
53828     },
53829
53830     /**
53831      * Sets the root node for this tree.
53832      * @param {Node} node
53833      * @return {Node}
53834      */
53835     setRootNode : function(node){
53836         this.root = node;
53837         node.ownerTree = this;
53838         node.isRoot = true;
53839         this.registerNode(node);
53840         return node;
53841     },
53842
53843     /**
53844      * Gets a node in this tree by its id.
53845      * @param {String} id
53846      * @return {Node}
53847      */
53848     getNodeById : function(id){
53849         return this.nodeHash[id];
53850     },
53851
53852     // private
53853     registerNode : function(node){
53854         this.nodeHash[node.id] = node;
53855     },
53856
53857     // private
53858     unregisterNode : function(node){
53859         delete this.nodeHash[node.id];
53860     },
53861
53862     toString : function(){
53863         return "[Tree"+(this.id?" "+this.id:"")+"]";
53864     }
53865 });
53866
53867 /**
53868  * @class Ext.data.Node
53869  * @extends Ext.util.Observable
53870  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
53871  * @cfg {String} id The id for this node. If one is not specified, one is generated.
53872  * @constructor
53873  * @param {Object} attributes The attributes/config for the node
53874  */
53875 Ext.data.Node = Ext.extend(Ext.util.Observable, {
53876     
53877     constructor: function(attributes){
53878         /**
53879          * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
53880          * @type {Object}
53881          */
53882         this.attributes = attributes || {};
53883         this.leaf = this.attributes.leaf;
53884         /**
53885          * The node id. @type String
53886          */
53887         this.id = this.attributes.id;
53888         if(!this.id){
53889             this.id = Ext.id(null, "xnode-");
53890             this.attributes.id = this.id;
53891         }
53892         /**
53893          * All child nodes of this node. @type Array
53894          */
53895         this.childNodes = [];
53896         /**
53897          * The parent node for this node. @type Node
53898          */
53899         this.parentNode = null;
53900         /**
53901          * The first direct child node of this node, or null if this node has no child nodes. @type Node
53902          */
53903         this.firstChild = null;
53904         /**
53905          * The last direct child node of this node, or null if this node has no child nodes. @type Node
53906          */
53907         this.lastChild = null;
53908         /**
53909          * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
53910          */
53911         this.previousSibling = null;
53912         /**
53913          * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
53914          */
53915         this.nextSibling = null;
53916
53917         this.addEvents({
53918             /**
53919              * @event append
53920              * Fires when a new child node is appended
53921              * @param {Tree} tree The owner tree
53922              * @param {Node} this This node
53923              * @param {Node} node The newly appended node
53924              * @param {Number} index The index of the newly appended node
53925              */
53926             "append" : true,
53927             /**
53928              * @event remove
53929              * Fires when a child node is removed
53930              * @param {Tree} tree The owner tree
53931              * @param {Node} this This node
53932              * @param {Node} node The removed node
53933              */
53934             "remove" : true,
53935             /**
53936              * @event move
53937              * Fires when this node is moved to a new location in the tree
53938              * @param {Tree} tree The owner tree
53939              * @param {Node} this This node
53940              * @param {Node} oldParent The old parent of this node
53941              * @param {Node} newParent The new parent of this node
53942              * @param {Number} index The index it was moved to
53943              */
53944             "move" : true,
53945             /**
53946              * @event insert
53947              * Fires when a new child node is inserted.
53948              * @param {Tree} tree The owner tree
53949              * @param {Node} this This node
53950              * @param {Node} node The child node inserted
53951              * @param {Node} refNode The child node the node was inserted before
53952              */
53953             "insert" : true,
53954             /**
53955              * @event beforeappend
53956              * Fires before a new child is appended, return false to cancel the append.
53957              * @param {Tree} tree The owner tree
53958              * @param {Node} this This node
53959              * @param {Node} node The child node to be appended
53960              */
53961             "beforeappend" : true,
53962             /**
53963              * @event beforeremove
53964              * Fires before a child is removed, return false to cancel the remove.
53965              * @param {Tree} tree The owner tree
53966              * @param {Node} this This node
53967              * @param {Node} node The child node to be removed
53968              */
53969             "beforeremove" : true,
53970             /**
53971              * @event beforemove
53972              * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
53973              * @param {Tree} tree The owner tree
53974              * @param {Node} this This node
53975              * @param {Node} oldParent The parent of this node
53976              * @param {Node} newParent The new parent this node is moving to
53977              * @param {Number} index The index it is being moved to
53978              */
53979             "beforemove" : true,
53980              /**
53981               * @event beforeinsert
53982               * Fires before a new child is inserted, return false to cancel the insert.
53983               * @param {Tree} tree The owner tree
53984               * @param {Node} this This node
53985               * @param {Node} node The child node to be inserted
53986               * @param {Node} refNode The child node the node is being inserted before
53987               */
53988             "beforeinsert" : true
53989         });
53990         this.listeners = this.attributes.listeners;
53991         Ext.data.Node.superclass.constructor.call(this);    
53992     },
53993     
53994     // private
53995     fireEvent : function(evtName){
53996         // first do standard event for this node
53997         if(Ext.data.Node.superclass.fireEvent.apply(this, arguments) === false){
53998             return false;
53999         }
54000         // then bubble it up to the tree if the event wasn't cancelled
54001         var ot = this.getOwnerTree();
54002         if(ot){
54003             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
54004                 return false;
54005             }
54006         }
54007         return true;
54008     },
54009
54010     /**
54011      * Returns true if this node is a leaf
54012      * @return {Boolean}
54013      */
54014     isLeaf : function(){
54015         return this.leaf === true;
54016     },
54017
54018     // private
54019     setFirstChild : function(node){
54020         this.firstChild = node;
54021     },
54022
54023     //private
54024     setLastChild : function(node){
54025         this.lastChild = node;
54026     },
54027
54028
54029     /**
54030      * Returns true if this node is the last child of its parent
54031      * @return {Boolean}
54032      */
54033     isLast : function(){
54034        return (!this.parentNode ? true : this.parentNode.lastChild == this);
54035     },
54036
54037     /**
54038      * Returns true if this node is the first child of its parent
54039      * @return {Boolean}
54040      */
54041     isFirst : function(){
54042        return (!this.parentNode ? true : this.parentNode.firstChild == this);
54043     },
54044
54045     /**
54046      * Returns true if this node has one or more child nodes, else false.
54047      * @return {Boolean}
54048      */
54049     hasChildNodes : function(){
54050         return !this.isLeaf() && this.childNodes.length > 0;
54051     },
54052
54053     /**
54054      * Returns true if this node has one or more child nodes, or if the <tt>expandable</tt>
54055      * node attribute is explicitly specified as true (see {@link #attributes}), otherwise returns false.
54056      * @return {Boolean}
54057      */
54058     isExpandable : function(){
54059         return this.attributes.expandable || this.hasChildNodes();
54060     },
54061
54062     /**
54063      * Insert node(s) as the last child node of this node.
54064      * @param {Node/Array} node The node or Array of nodes to append
54065      * @return {Node} The appended node if single append, or null if an array was passed
54066      */
54067     appendChild : function(node){
54068         var multi = false;
54069         if(Ext.isArray(node)){
54070             multi = node;
54071         }else if(arguments.length > 1){
54072             multi = arguments;
54073         }
54074         // if passed an array or multiple args do them one by one
54075         if(multi){
54076             for(var i = 0, len = multi.length; i < len; i++) {
54077                 this.appendChild(multi[i]);
54078             }
54079         }else{
54080             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
54081                 return false;
54082             }
54083             var index = this.childNodes.length;
54084             var oldParent = node.parentNode;
54085             // it's a move, make sure we move it cleanly
54086             if(oldParent){
54087                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
54088                     return false;
54089                 }
54090                 oldParent.removeChild(node);
54091             }
54092             index = this.childNodes.length;
54093             if(index === 0){
54094                 this.setFirstChild(node);
54095             }
54096             this.childNodes.push(node);
54097             node.parentNode = this;
54098             var ps = this.childNodes[index-1];
54099             if(ps){
54100                 node.previousSibling = ps;
54101                 ps.nextSibling = node;
54102             }else{
54103                 node.previousSibling = null;
54104             }
54105             node.nextSibling = null;
54106             this.setLastChild(node);
54107             node.setOwnerTree(this.getOwnerTree());
54108             this.fireEvent("append", this.ownerTree, this, node, index);
54109             if(oldParent){
54110                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
54111             }
54112             return node;
54113         }
54114     },
54115
54116     /**
54117      * Removes a child node from this node.
54118      * @param {Node} node The node to remove
54119      * @param {Boolean} destroy <tt>true</tt> to destroy the node upon removal. Defaults to <tt>false</tt>.
54120      * @return {Node} The removed node
54121      */
54122     removeChild : function(node, destroy){
54123         var index = this.childNodes.indexOf(node);
54124         if(index == -1){
54125             return false;
54126         }
54127         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
54128             return false;
54129         }
54130
54131         // remove it from childNodes collection
54132         this.childNodes.splice(index, 1);
54133
54134         // update siblings
54135         if(node.previousSibling){
54136             node.previousSibling.nextSibling = node.nextSibling;
54137         }
54138         if(node.nextSibling){
54139             node.nextSibling.previousSibling = node.previousSibling;
54140         }
54141
54142         // update child refs
54143         if(this.firstChild == node){
54144             this.setFirstChild(node.nextSibling);
54145         }
54146         if(this.lastChild == node){
54147             this.setLastChild(node.previousSibling);
54148         }
54149
54150         this.fireEvent("remove", this.ownerTree, this, node);
54151         if(destroy){
54152             node.destroy(true);
54153         }else{
54154             node.clear();
54155         }
54156         return node;
54157     },
54158
54159     // private
54160     clear : function(destroy){
54161         // clear any references from the node
54162         this.setOwnerTree(null, destroy);
54163         this.parentNode = this.previousSibling = this.nextSibling = null;
54164         if(destroy){
54165             this.firstChild = this.lastChild = null;
54166         }
54167     },
54168
54169     /**
54170      * Destroys the node.
54171      */
54172     destroy : function(/* private */ silent){
54173         /*
54174          * Silent is to be used in a number of cases
54175          * 1) When setRootNode is called.
54176          * 2) When destroy on the tree is called
54177          * 3) For destroying child nodes on a node
54178          */
54179         if(silent === true){
54180             this.purgeListeners();
54181             this.clear(true);
54182             Ext.each(this.childNodes, function(n){
54183                 n.destroy(true);
54184             });
54185             this.childNodes = null;
54186         }else{
54187             this.remove(true);
54188         }
54189     },
54190
54191     /**
54192      * Inserts the first node before the second node in this nodes childNodes collection.
54193      * @param {Node} node The node to insert
54194      * @param {Node} refNode The node to insert before (if null the node is appended)
54195      * @return {Node} The inserted node
54196      */
54197     insertBefore : function(node, refNode){
54198         if(!refNode){ // like standard Dom, refNode can be null for append
54199             return this.appendChild(node);
54200         }
54201         // nothing to do
54202         if(node == refNode){
54203             return false;
54204         }
54205
54206         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
54207             return false;
54208         }
54209         var index = this.childNodes.indexOf(refNode);
54210         var oldParent = node.parentNode;
54211         var refIndex = index;
54212
54213         // when moving internally, indexes will change after remove
54214         if(oldParent == this && this.childNodes.indexOf(node) < index){
54215             refIndex--;
54216         }
54217
54218         // it's a move, make sure we move it cleanly
54219         if(oldParent){
54220             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
54221                 return false;
54222             }
54223             oldParent.removeChild(node);
54224         }
54225         if(refIndex === 0){
54226             this.setFirstChild(node);
54227         }
54228         this.childNodes.splice(refIndex, 0, node);
54229         node.parentNode = this;
54230         var ps = this.childNodes[refIndex-1];
54231         if(ps){
54232             node.previousSibling = ps;
54233             ps.nextSibling = node;
54234         }else{
54235             node.previousSibling = null;
54236         }
54237         node.nextSibling = refNode;
54238         refNode.previousSibling = node;
54239         node.setOwnerTree(this.getOwnerTree());
54240         this.fireEvent("insert", this.ownerTree, this, node, refNode);
54241         if(oldParent){
54242             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
54243         }
54244         return node;
54245     },
54246
54247     /**
54248      * Removes this node from its parent
54249      * @param {Boolean} destroy <tt>true</tt> to destroy the node upon removal. Defaults to <tt>false</tt>.
54250      * @return {Node} this
54251      */
54252     remove : function(destroy){
54253         if (this.parentNode) {
54254             this.parentNode.removeChild(this, destroy);
54255         }
54256         return this;
54257     },
54258
54259     /**
54260      * Removes all child nodes from this node.
54261      * @param {Boolean} destroy <tt>true</tt> to destroy the node upon removal. Defaults to <tt>false</tt>.
54262      * @return {Node} this
54263      */
54264     removeAll : function(destroy){
54265         var cn = this.childNodes,
54266             n;
54267         while((n = cn[0])){
54268             this.removeChild(n, destroy);
54269         }
54270         return this;
54271     },
54272
54273     /**
54274      * Returns the child node at the specified index.
54275      * @param {Number} index
54276      * @return {Node}
54277      */
54278     item : function(index){
54279         return this.childNodes[index];
54280     },
54281
54282     /**
54283      * Replaces one child node in this node with another.
54284      * @param {Node} newChild The replacement node
54285      * @param {Node} oldChild The node to replace
54286      * @return {Node} The replaced node
54287      */
54288     replaceChild : function(newChild, oldChild){
54289         var s = oldChild ? oldChild.nextSibling : null;
54290         this.removeChild(oldChild);
54291         this.insertBefore(newChild, s);
54292         return oldChild;
54293     },
54294
54295     /**
54296      * Returns the index of a child node
54297      * @param {Node} node
54298      * @return {Number} The index of the node or -1 if it was not found
54299      */
54300     indexOf : function(child){
54301         return this.childNodes.indexOf(child);
54302     },
54303
54304     /**
54305      * Returns the tree this node is in.
54306      * @return {Tree}
54307      */
54308     getOwnerTree : function(){
54309         // if it doesn't have one, look for one
54310         if(!this.ownerTree){
54311             var p = this;
54312             while(p){
54313                 if(p.ownerTree){
54314                     this.ownerTree = p.ownerTree;
54315                     break;
54316                 }
54317                 p = p.parentNode;
54318             }
54319         }
54320         return this.ownerTree;
54321     },
54322
54323     /**
54324      * Returns depth of this node (the root node has a depth of 0)
54325      * @return {Number}
54326      */
54327     getDepth : function(){
54328         var depth = 0;
54329         var p = this;
54330         while(p.parentNode){
54331             ++depth;
54332             p = p.parentNode;
54333         }
54334         return depth;
54335     },
54336
54337     // private
54338     setOwnerTree : function(tree, destroy){
54339         // if it is a move, we need to update everyone
54340         if(tree != this.ownerTree){
54341             if(this.ownerTree){
54342                 this.ownerTree.unregisterNode(this);
54343             }
54344             this.ownerTree = tree;
54345             // If we're destroying, we don't need to recurse since it will be called on each child node
54346             if(destroy !== true){
54347                 Ext.each(this.childNodes, function(n){
54348                     n.setOwnerTree(tree);
54349                 });
54350             }
54351             if(tree){
54352                 tree.registerNode(this);
54353             }
54354         }
54355     },
54356
54357     /**
54358      * Changes the id of this node.
54359      * @param {String} id The new id for the node.
54360      */
54361     setId: function(id){
54362         if(id !== this.id){
54363             var t = this.ownerTree;
54364             if(t){
54365                 t.unregisterNode(this);
54366             }
54367             this.id = this.attributes.id = id;
54368             if(t){
54369                 t.registerNode(this);
54370             }
54371             this.onIdChange(id);
54372         }
54373     },
54374
54375     // private
54376     onIdChange: Ext.emptyFn,
54377
54378     /**
54379      * Returns the path for this node. The path can be used to expand or select this node programmatically.
54380      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
54381      * @return {String} The path
54382      */
54383     getPath : function(attr){
54384         attr = attr || "id";
54385         var p = this.parentNode;
54386         var b = [this.attributes[attr]];
54387         while(p){
54388             b.unshift(p.attributes[attr]);
54389             p = p.parentNode;
54390         }
54391         var sep = this.getOwnerTree().pathSeparator;
54392         return sep + b.join(sep);
54393     },
54394
54395     /**
54396      * Bubbles up the tree from this node, calling the specified function with each node. The arguments to the function
54397      * will be the args provided or the current node. If the function returns false at any point,
54398      * the bubble is stopped.
54399      * @param {Function} fn The function to call
54400      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the current Node.
54401      * @param {Array} args (optional) The args to call the function with (default to passing the current Node)
54402      */
54403     bubble : function(fn, scope, args){
54404         var p = this;
54405         while(p){
54406             if(fn.apply(scope || p, args || [p]) === false){
54407                 break;
54408             }
54409             p = p.parentNode;
54410         }
54411     },
54412
54413     /**
54414      * Cascades down the tree from this node, calling the specified function with each node. The arguments to the function
54415      * will be the args provided or the current node. If the function returns false at any point,
54416      * the cascade is stopped on that branch.
54417      * @param {Function} fn The function to call
54418      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the current Node.
54419      * @param {Array} args (optional) The args to call the function with (default to passing the current Node)
54420      */
54421     cascade : function(fn, scope, args){
54422         if(fn.apply(scope || this, args || [this]) !== false){
54423             var cs = this.childNodes;
54424             for(var i = 0, len = cs.length; i < len; i++) {
54425                 cs[i].cascade(fn, scope, args);
54426             }
54427         }
54428     },
54429
54430     /**
54431      * Interates the child nodes of this node, calling the specified function with each node. The arguments to the function
54432      * will be the args provided or the current node. If the function returns false at any point,
54433      * the iteration stops.
54434      * @param {Function} fn The function to call
54435      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the current Node in the iteration.
54436      * @param {Array} args (optional) The args to call the function with (default to passing the current Node)
54437      */
54438     eachChild : function(fn, scope, args){
54439         var cs = this.childNodes;
54440         for(var i = 0, len = cs.length; i < len; i++) {
54441             if(fn.apply(scope || cs[i], args || [cs[i]]) === false){
54442                 break;
54443             }
54444         }
54445     },
54446
54447     /**
54448      * Finds the first child that has the attribute with the specified value.
54449      * @param {String} attribute The attribute name
54450      * @param {Mixed} value The value to search for
54451      * @param {Boolean} deep (Optional) True to search through nodes deeper than the immediate children
54452      * @return {Node} The found child or null if none was found
54453      */
54454     findChild : function(attribute, value, deep){
54455         return this.findChildBy(function(){
54456             return this.attributes[attribute] == value;
54457         }, null, deep);
54458     },
54459
54460     /**
54461      * Finds the first child by a custom function. The child matches if the function passed returns <code>true</code>.
54462      * @param {Function} fn A function which must return <code>true</code> if the passed Node is the required Node.
54463      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the Node being tested.
54464      * @param {Boolean} deep (Optional) True to search through nodes deeper than the immediate children
54465      * @return {Node} The found child or null if none was found
54466      */
54467     findChildBy : function(fn, scope, deep){
54468         var cs = this.childNodes,
54469             len = cs.length,
54470             i = 0,
54471             n,
54472             res;
54473         for(; i < len; i++){
54474             n = cs[i];
54475             if(fn.call(scope || n, n) === true){
54476                 return n;
54477             }else if (deep){
54478                 res = n.findChildBy(fn, scope, deep);
54479                 if(res != null){
54480                     return res;
54481                 }
54482             }
54483             
54484         }
54485         return null;
54486     },
54487
54488     /**
54489      * Sorts this nodes children using the supplied sort function.
54490      * @param {Function} fn A function which, when passed two Nodes, returns -1, 0 or 1 depending upon required sort order.
54491      * @param {Object} scope (optional)The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
54492      */
54493     sort : function(fn, scope){
54494         var cs = this.childNodes;
54495         var len = cs.length;
54496         if(len > 0){
54497             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
54498             cs.sort(sortFn);
54499             for(var i = 0; i < len; i++){
54500                 var n = cs[i];
54501                 n.previousSibling = cs[i-1];
54502                 n.nextSibling = cs[i+1];
54503                 if(i === 0){
54504                     this.setFirstChild(n);
54505                 }
54506                 if(i == len-1){
54507                     this.setLastChild(n);
54508                 }
54509             }
54510         }
54511     },
54512
54513     /**
54514      * Returns true if this node is an ancestor (at any point) of the passed node.
54515      * @param {Node} node
54516      * @return {Boolean}
54517      */
54518     contains : function(node){
54519         return node.isAncestor(this);
54520     },
54521
54522     /**
54523      * Returns true if the passed node is an ancestor (at any point) of this node.
54524      * @param {Node} node
54525      * @return {Boolean}
54526      */
54527     isAncestor : function(node){
54528         var p = this.parentNode;
54529         while(p){
54530             if(p == node){
54531                 return true;
54532             }
54533             p = p.parentNode;
54534         }
54535         return false;
54536     },
54537
54538     toString : function(){
54539         return "[Node"+(this.id?" "+this.id:"")+"]";
54540     }
54541 });/**
54542  * @class Ext.tree.TreeNode
54543  * @extends Ext.data.Node
54544  * @cfg {String} text The text for this node
54545  * @cfg {Boolean} expanded true to start the node expanded
54546  * @cfg {Boolean} allowDrag False to make this node undraggable if {@link #draggable} = true (defaults to true)
54547  * @cfg {Boolean} allowDrop False if this node cannot have child nodes dropped on it (defaults to true)
54548  * @cfg {Boolean} disabled true to start the node disabled
54549  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
54550  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
54551  * @cfg {String} cls A css class to be added to the node
54552  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
54553  * @cfg {String} href URL of the link used for the node (defaults to #)
54554  * @cfg {String} hrefTarget target frame for the link
54555  * @cfg {Boolean} hidden True to render hidden. (Defaults to false).
54556  * @cfg {String} qtip An Ext QuickTip for the node
54557  * @cfg {Boolean} expandable If set to true, the node will always show a plus/minus icon, even when empty
54558  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
54559  * @cfg {Boolean} singleClickExpand True for single click expand on this node
54560  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Ext.tree.TreeNodeUI)
54561  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
54562  * (defaults to undefined with no checkbox rendered)
54563  * @cfg {Boolean} draggable True to make this node draggable (defaults to false)
54564  * @cfg {Boolean} isTarget False to not allow this node to act as a drop target (defaults to true)
54565  * @cfg {Boolean} allowChildren False to not allow this node to have child nodes (defaults to true)
54566  * @cfg {Boolean} editable False to not allow this node to be edited by an {@link Ext.tree.TreeEditor} (defaults to true)
54567  * @constructor
54568  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
54569  */
54570 Ext.tree.TreeNode = Ext.extend(Ext.data.Node, {
54571     
54572     constructor : function(attributes){
54573         attributes = attributes || {};
54574         if(Ext.isString(attributes)){
54575             attributes = {text: attributes};
54576         }
54577         this.childrenRendered = false;
54578         this.rendered = false;
54579         Ext.tree.TreeNode.superclass.constructor.call(this, attributes);
54580         this.expanded = attributes.expanded === true;
54581         this.isTarget = attributes.isTarget !== false;
54582         this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
54583         this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
54584
54585         /**
54586          * Read-only. The text for this node. To change it use <code>{@link #setText}</code>.
54587          * @type String
54588          */
54589         this.text = attributes.text;
54590         /**
54591          * True if this node is disabled.
54592          * @type Boolean
54593          */
54594         this.disabled = attributes.disabled === true;
54595         /**
54596          * True if this node is hidden.
54597          * @type Boolean
54598          */
54599         this.hidden = attributes.hidden === true;
54600     
54601         this.addEvents(
54602             /**
54603             * @event textchange
54604             * Fires when the text for this node is changed
54605             * @param {Node} this This node
54606             * @param {String} text The new text
54607             * @param {String} oldText The old text
54608             */
54609             'textchange',
54610             /**
54611             * @event beforeexpand
54612             * Fires before this node is expanded, return false to cancel.
54613             * @param {Node} this This node
54614             * @param {Boolean} deep
54615             * @param {Boolean} anim
54616             */
54617             'beforeexpand',
54618             /**
54619             * @event beforecollapse
54620             * Fires before this node is collapsed, return false to cancel.
54621             * @param {Node} this This node
54622             * @param {Boolean} deep
54623             * @param {Boolean} anim
54624             */
54625             'beforecollapse',
54626             /**
54627             * @event expand
54628             * Fires when this node is expanded
54629             * @param {Node} this This node
54630             */
54631             'expand',
54632             /**
54633             * @event disabledchange
54634             * Fires when the disabled status of this node changes
54635             * @param {Node} this This node
54636             * @param {Boolean} disabled
54637             */
54638             'disabledchange',
54639             /**
54640             * @event collapse
54641             * Fires when this node is collapsed
54642             * @param {Node} this This node
54643             */
54644             'collapse',
54645             /**
54646             * @event beforeclick
54647             * Fires before click processing. Return false to cancel the default action.
54648             * @param {Node} this This node
54649             * @param {Ext.EventObject} e The event object
54650             */
54651             'beforeclick',
54652             /**
54653             * @event click
54654             * Fires when this node is clicked
54655             * @param {Node} this This node
54656             * @param {Ext.EventObject} e The event object
54657             */
54658             'click',
54659             /**
54660             * @event checkchange
54661             * Fires when a node with a checkbox's checked property changes
54662             * @param {Node} this This node
54663             * @param {Boolean} checked
54664             */
54665             'checkchange',
54666             /**
54667             * @event beforedblclick
54668             * Fires before double click processing. Return false to cancel the default action.
54669             * @param {Node} this This node
54670             * @param {Ext.EventObject} e The event object
54671             */
54672             'beforedblclick',
54673             /**
54674             * @event dblclick
54675             * Fires when this node is double clicked
54676             * @param {Node} this This node
54677             * @param {Ext.EventObject} e The event object
54678             */
54679             'dblclick',
54680             /**
54681             * @event contextmenu
54682             * Fires when this node is right clicked
54683             * @param {Node} this This node
54684             * @param {Ext.EventObject} e The event object
54685             */
54686             'contextmenu',
54687             /**
54688             * @event beforechildrenrendered
54689             * Fires right before the child nodes for this node are rendered
54690             * @param {Node} this This node
54691             */
54692             'beforechildrenrendered'
54693         );
54694     
54695         var uiClass = this.attributes.uiProvider || this.defaultUI || Ext.tree.TreeNodeUI;
54696     
54697         /**
54698          * Read-only. The UI for this node
54699          * @type TreeNodeUI
54700          */
54701         this.ui = new uiClass(this);    
54702     },
54703     
54704     preventHScroll : true,
54705     /**
54706      * Returns true if this node is expanded
54707      * @return {Boolean}
54708      */
54709     isExpanded : function(){
54710         return this.expanded;
54711     },
54712
54713 /**
54714  * Returns the UI object for this node.
54715  * @return {TreeNodeUI} The object which is providing the user interface for this tree
54716  * node. Unless otherwise specified in the {@link #uiProvider}, this will be an instance
54717  * of {@link Ext.tree.TreeNodeUI}
54718  */
54719     getUI : function(){
54720         return this.ui;
54721     },
54722
54723     getLoader : function(){
54724         var owner;
54725         return this.loader || ((owner = this.getOwnerTree()) && owner.loader ? owner.loader : (this.loader = new Ext.tree.TreeLoader()));
54726     },
54727
54728     // private override
54729     setFirstChild : function(node){
54730         var of = this.firstChild;
54731         Ext.tree.TreeNode.superclass.setFirstChild.call(this, node);
54732         if(this.childrenRendered && of && node != of){
54733             of.renderIndent(true, true);
54734         }
54735         if(this.rendered){
54736             this.renderIndent(true, true);
54737         }
54738     },
54739
54740     // private override
54741     setLastChild : function(node){
54742         var ol = this.lastChild;
54743         Ext.tree.TreeNode.superclass.setLastChild.call(this, node);
54744         if(this.childrenRendered && ol && node != ol){
54745             ol.renderIndent(true, true);
54746         }
54747         if(this.rendered){
54748             this.renderIndent(true, true);
54749         }
54750     },
54751
54752     // these methods are overridden to provide lazy rendering support
54753     // private override
54754     appendChild : function(n){
54755         if(!n.render && !Ext.isArray(n)){
54756             n = this.getLoader().createNode(n);
54757         }
54758         var node = Ext.tree.TreeNode.superclass.appendChild.call(this, n);
54759         if(node && this.childrenRendered){
54760             node.render();
54761         }
54762         this.ui.updateExpandIcon();
54763         return node;
54764     },
54765
54766     // private override
54767     removeChild : function(node, destroy){
54768         this.ownerTree.getSelectionModel().unselect(node);
54769         Ext.tree.TreeNode.superclass.removeChild.apply(this, arguments);
54770         // only update the ui if we're not destroying
54771         if(!destroy){
54772             var rendered = node.ui.rendered;
54773             // if it's been rendered remove dom node
54774             if(rendered){
54775                 node.ui.remove();
54776             }
54777             if(rendered && this.childNodes.length < 1){
54778                 this.collapse(false, false);
54779             }else{
54780                 this.ui.updateExpandIcon();
54781             }
54782             if(!this.firstChild && !this.isHiddenRoot()){
54783                 this.childrenRendered = false;
54784             }
54785         }
54786         return node;
54787     },
54788
54789     // private override
54790     insertBefore : function(node, refNode){
54791         if(!node.render){
54792             node = this.getLoader().createNode(node);
54793         }
54794         var newNode = Ext.tree.TreeNode.superclass.insertBefore.call(this, node, refNode);
54795         if(newNode && refNode && this.childrenRendered){
54796             node.render();
54797         }
54798         this.ui.updateExpandIcon();
54799         return newNode;
54800     },
54801
54802     /**
54803      * Sets the text for this node
54804      * @param {String} text
54805      */
54806     setText : function(text){
54807         var oldText = this.text;
54808         this.text = this.attributes.text = text;
54809         if(this.rendered){ // event without subscribing
54810             this.ui.onTextChange(this, text, oldText);
54811         }
54812         this.fireEvent('textchange', this, text, oldText);
54813     },
54814     
54815     /**
54816      * Sets the icon class for this node.
54817      * @param {String} cls
54818      */
54819     setIconCls : function(cls){
54820         var old = this.attributes.iconCls;
54821         this.attributes.iconCls = cls;
54822         if(this.rendered){
54823             this.ui.onIconClsChange(this, cls, old);
54824         }
54825     },
54826     
54827     /**
54828      * Sets the tooltip for this node.
54829      * @param {String} tip The text for the tip
54830      * @param {String} title (Optional) The title for the tip
54831      */
54832     setTooltip : function(tip, title){
54833         this.attributes.qtip = tip;
54834         this.attributes.qtipTitle = title;
54835         if(this.rendered){
54836             this.ui.onTipChange(this, tip, title);
54837         }
54838     },
54839     
54840     /**
54841      * Sets the icon for this node.
54842      * @param {String} icon
54843      */
54844     setIcon : function(icon){
54845         this.attributes.icon = icon;
54846         if(this.rendered){
54847             this.ui.onIconChange(this, icon);
54848         }
54849     },
54850     
54851     /**
54852      * Sets the href for the node.
54853      * @param {String} href The href to set
54854      * @param {String} (Optional) target The target of the href
54855      */
54856     setHref : function(href, target){
54857         this.attributes.href = href;
54858         this.attributes.hrefTarget = target;
54859         if(this.rendered){
54860             this.ui.onHrefChange(this, href, target);
54861         }
54862     },
54863     
54864     /**
54865      * Sets the class on this node.
54866      * @param {String} cls
54867      */
54868     setCls : function(cls){
54869         var old = this.attributes.cls;
54870         this.attributes.cls = cls;
54871         if(this.rendered){
54872             this.ui.onClsChange(this, cls, old);
54873         }
54874     },
54875
54876     /**
54877      * Triggers selection of this node
54878      */
54879     select : function(){
54880         var t = this.getOwnerTree();
54881         if(t){
54882             t.getSelectionModel().select(this);
54883         }
54884     },
54885
54886     /**
54887      * Triggers deselection of this node
54888      * @param {Boolean} silent (optional) True to stop selection change events from firing.
54889      */
54890     unselect : function(silent){
54891         var t = this.getOwnerTree();
54892         if(t){
54893             t.getSelectionModel().unselect(this, silent);
54894         }
54895     },
54896
54897     /**
54898      * Returns true if this node is selected
54899      * @return {Boolean}
54900      */
54901     isSelected : function(){
54902         var t = this.getOwnerTree();
54903         return t ? t.getSelectionModel().isSelected(this) : false;
54904     },
54905
54906     /**
54907      * Expand this node.
54908      * @param {Boolean} deep (optional) True to expand all children as well
54909      * @param {Boolean} anim (optional) false to cancel the default animation
54910      * @param {Function} callback (optional) A callback to be called when
54911      * expanding this node completes (does not wait for deep expand to complete).
54912      * Called with 1 parameter, this node.
54913      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to this TreeNode.
54914      */
54915     expand : function(deep, anim, callback, scope){
54916         if(!this.expanded){
54917             if(this.fireEvent('beforeexpand', this, deep, anim) === false){
54918                 return;
54919             }
54920             if(!this.childrenRendered){
54921                 this.renderChildren();
54922             }
54923             this.expanded = true;
54924             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
54925                 this.ui.animExpand(function(){
54926                     this.fireEvent('expand', this);
54927                     this.runCallback(callback, scope || this, [this]);
54928                     if(deep === true){
54929                         this.expandChildNodes(true, true);
54930                     }
54931                 }.createDelegate(this));
54932                 return;
54933             }else{
54934                 this.ui.expand();
54935                 this.fireEvent('expand', this);
54936                 this.runCallback(callback, scope || this, [this]);
54937             }
54938         }else{
54939            this.runCallback(callback, scope || this, [this]);
54940         }
54941         if(deep === true){
54942             this.expandChildNodes(true);
54943         }
54944     },
54945
54946     runCallback : function(cb, scope, args){
54947         if(Ext.isFunction(cb)){
54948             cb.apply(scope, args);
54949         }
54950     },
54951
54952     isHiddenRoot : function(){
54953         return this.isRoot && !this.getOwnerTree().rootVisible;
54954     },
54955
54956     /**
54957      * Collapse this node.
54958      * @param {Boolean} deep (optional) True to collapse all children as well
54959      * @param {Boolean} anim (optional) false to cancel the default animation
54960      * @param {Function} callback (optional) A callback to be called when
54961      * expanding this node completes (does not wait for deep expand to complete).
54962      * Called with 1 parameter, this node.
54963      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to this TreeNode.
54964      */
54965     collapse : function(deep, anim, callback, scope){
54966         if(this.expanded && !this.isHiddenRoot()){
54967             if(this.fireEvent('beforecollapse', this, deep, anim) === false){
54968                 return;
54969             }
54970             this.expanded = false;
54971             if((this.getOwnerTree().animate && anim !== false) || anim){
54972                 this.ui.animCollapse(function(){
54973                     this.fireEvent('collapse', this);
54974                     this.runCallback(callback, scope || this, [this]);
54975                     if(deep === true){
54976                         this.collapseChildNodes(true);
54977                     }
54978                 }.createDelegate(this));
54979                 return;
54980             }else{
54981                 this.ui.collapse();
54982                 this.fireEvent('collapse', this);
54983                 this.runCallback(callback, scope || this, [this]);
54984             }
54985         }else if(!this.expanded){
54986             this.runCallback(callback, scope || this, [this]);
54987         }
54988         if(deep === true){
54989             var cs = this.childNodes;
54990             for(var i = 0, len = cs.length; i < len; i++) {
54991                 cs[i].collapse(true, false);
54992             }
54993         }
54994     },
54995
54996     // private
54997     delayedExpand : function(delay){
54998         if(!this.expandProcId){
54999             this.expandProcId = this.expand.defer(delay, this);
55000         }
55001     },
55002
55003     // private
55004     cancelExpand : function(){
55005         if(this.expandProcId){
55006             clearTimeout(this.expandProcId);
55007         }
55008         this.expandProcId = false;
55009     },
55010
55011     /**
55012      * Toggles expanded/collapsed state of the node
55013      */
55014     toggle : function(){
55015         if(this.expanded){
55016             this.collapse();
55017         }else{
55018             this.expand();
55019         }
55020     },
55021
55022     /**
55023      * Ensures all parent nodes are expanded, and if necessary, scrolls
55024      * the node into view.
55025      * @param {Function} callback (optional) A function to call when the node has been made visible.
55026      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to this TreeNode.
55027      */
55028     ensureVisible : function(callback, scope){
55029         var tree = this.getOwnerTree();
55030         tree.expandPath(this.parentNode ? this.parentNode.getPath() : this.getPath(), false, function(){
55031             var node = tree.getNodeById(this.id);  // Somehow if we don't do this, we lose changes that happened to node in the meantime
55032             tree.getTreeEl().scrollChildIntoView(node.ui.anchor);
55033             this.runCallback(callback, scope || this, [this]);
55034         }.createDelegate(this));
55035     },
55036
55037     /**
55038      * Expand all child nodes
55039      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
55040      */
55041     expandChildNodes : function(deep, anim) {
55042         var cs = this.childNodes,
55043             i,
55044             len = cs.length;
55045         for (i = 0; i < len; i++) {
55046                 cs[i].expand(deep, anim);
55047         }
55048     },
55049
55050     /**
55051      * Collapse all child nodes
55052      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
55053      */
55054     collapseChildNodes : function(deep){
55055         var cs = this.childNodes;
55056         for(var i = 0, len = cs.length; i < len; i++) {
55057                 cs[i].collapse(deep);
55058         }
55059     },
55060
55061     /**
55062      * Disables this node
55063      */
55064     disable : function(){
55065         this.disabled = true;
55066         this.unselect();
55067         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
55068             this.ui.onDisableChange(this, true);
55069         }
55070         this.fireEvent('disabledchange', this, true);
55071     },
55072
55073     /**
55074      * Enables this node
55075      */
55076     enable : function(){
55077         this.disabled = false;
55078         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
55079             this.ui.onDisableChange(this, false);
55080         }
55081         this.fireEvent('disabledchange', this, false);
55082     },
55083
55084     // private
55085     renderChildren : function(suppressEvent){
55086         if(suppressEvent !== false){
55087             this.fireEvent('beforechildrenrendered', this);
55088         }
55089         var cs = this.childNodes;
55090         for(var i = 0, len = cs.length; i < len; i++){
55091             cs[i].render(true);
55092         }
55093         this.childrenRendered = true;
55094     },
55095
55096     // private
55097     sort : function(fn, scope){
55098         Ext.tree.TreeNode.superclass.sort.apply(this, arguments);
55099         if(this.childrenRendered){
55100             var cs = this.childNodes;
55101             for(var i = 0, len = cs.length; i < len; i++){
55102                 cs[i].render(true);
55103             }
55104         }
55105     },
55106
55107     // private
55108     render : function(bulkRender){
55109         this.ui.render(bulkRender);
55110         if(!this.rendered){
55111             // make sure it is registered
55112             this.getOwnerTree().registerNode(this);
55113             this.rendered = true;
55114             if(this.expanded){
55115                 this.expanded = false;
55116                 this.expand(false, false);
55117             }
55118         }
55119     },
55120
55121     // private
55122     renderIndent : function(deep, refresh){
55123         if(refresh){
55124             this.ui.childIndent = null;
55125         }
55126         this.ui.renderIndent();
55127         if(deep === true && this.childrenRendered){
55128             var cs = this.childNodes;
55129             for(var i = 0, len = cs.length; i < len; i++){
55130                 cs[i].renderIndent(true, refresh);
55131             }
55132         }
55133     },
55134
55135     beginUpdate : function(){
55136         this.childrenRendered = false;
55137     },
55138
55139     endUpdate : function(){
55140         if(this.expanded && this.rendered){
55141             this.renderChildren();
55142         }
55143     },
55144
55145     //inherit docs
55146     destroy : function(silent){
55147         if(silent === true){
55148             this.unselect(true);
55149         }
55150         Ext.tree.TreeNode.superclass.destroy.call(this, silent);
55151         Ext.destroy(this.ui, this.loader);
55152         this.ui = this.loader = null;
55153     },
55154
55155     // private
55156     onIdChange : function(id){
55157         this.ui.onIdChange(id);
55158     }
55159 });
55160
55161 Ext.tree.TreePanel.nodeTypes.node = Ext.tree.TreeNode;/**
55162  * @class Ext.tree.AsyncTreeNode
55163  * @extends Ext.tree.TreeNode
55164  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
55165  * @constructor
55166  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
55167  */
55168  Ext.tree.AsyncTreeNode = function(config){
55169     this.loaded = config && config.loaded === true;
55170     this.loading = false;
55171     Ext.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
55172     /**
55173     * @event beforeload
55174     * Fires before this node is loaded, return false to cancel
55175     * @param {Node} this This node
55176     */
55177     this.addEvents('beforeload', 'load');
55178     /**
55179     * @event load
55180     * Fires when this node is loaded
55181     * @param {Node} this This node
55182     */
55183     /**
55184      * The loader used by this node (defaults to using the tree's defined loader)
55185      * @type TreeLoader
55186      * @property loader
55187      */
55188 };
55189 Ext.extend(Ext.tree.AsyncTreeNode, Ext.tree.TreeNode, {
55190     expand : function(deep, anim, callback, scope){
55191         if(this.loading){ // if an async load is already running, waiting til it's done
55192             var timer;
55193             var f = function(){
55194                 if(!this.loading){ // done loading
55195                     clearInterval(timer);
55196                     this.expand(deep, anim, callback, scope);
55197                 }
55198             }.createDelegate(this);
55199             timer = setInterval(f, 200);
55200             return;
55201         }
55202         if(!this.loaded){
55203             if(this.fireEvent("beforeload", this) === false){
55204                 return;
55205             }
55206             this.loading = true;
55207             this.ui.beforeLoad(this);
55208             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
55209             if(loader){
55210                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback, scope]), this);
55211                 return;
55212             }
55213         }
55214         Ext.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback, scope);
55215     },
55216     
55217     /**
55218      * Returns true if this node is currently loading
55219      * @return {Boolean}
55220      */
55221     isLoading : function(){
55222         return this.loading;  
55223     },
55224     
55225     loadComplete : function(deep, anim, callback, scope){
55226         this.loading = false;
55227         this.loaded = true;
55228         this.ui.afterLoad(this);
55229         this.fireEvent("load", this);
55230         this.expand(deep, anim, callback, scope);
55231     },
55232     
55233     /**
55234      * Returns true if this node has been loaded
55235      * @return {Boolean}
55236      */
55237     isLoaded : function(){
55238         return this.loaded;
55239     },
55240     
55241     hasChildNodes : function(){
55242         if(!this.isLeaf() && !this.loaded){
55243             return true;
55244         }else{
55245             return Ext.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
55246         }
55247     },
55248
55249     /**
55250      * Trigger a reload for this node
55251      * @param {Function} callback
55252      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to this Node.
55253      */
55254     reload : function(callback, scope){
55255         this.collapse(false, false);
55256         while(this.firstChild){
55257             this.removeChild(this.firstChild).destroy();
55258         }
55259         this.childrenRendered = false;
55260         this.loaded = false;
55261         if(this.isHiddenRoot()){
55262             this.expanded = false;
55263         }
55264         this.expand(false, false, callback, scope);
55265     }
55266 });
55267
55268 Ext.tree.TreePanel.nodeTypes.async = Ext.tree.AsyncTreeNode;/**
55269  * @class Ext.tree.TreeNodeUI
55270  * This class provides the default UI implementation for Ext TreeNodes.
55271  * The TreeNode UI implementation is separate from the
55272  * tree implementation, and allows customizing of the appearance of
55273  * tree nodes.<br>
55274  * <p>
55275  * If you are customizing the Tree's user interface, you
55276  * may need to extend this class, but you should never need to instantiate this class.<br>
55277  * <p>
55278  * This class provides access to the user interface components of an Ext TreeNode, through
55279  * {@link Ext.tree.TreeNode#getUI}
55280  */
55281 Ext.tree.TreeNodeUI = Ext.extend(Object, {
55282     
55283     constructor : function(node){
55284         Ext.apply(this, {
55285             node: node,
55286             rendered: false,
55287             animating: false,
55288             wasLeaf: true,
55289             ecc: 'x-tree-ec-icon x-tree-elbow',
55290             emptyIcon: Ext.BLANK_IMAGE_URL    
55291         });
55292     },
55293     
55294     // private
55295     removeChild : function(node){
55296         if(this.rendered){
55297             this.ctNode.removeChild(node.ui.getEl());
55298         }
55299     },
55300
55301     // private
55302     beforeLoad : function(){
55303          this.addClass("x-tree-node-loading");
55304     },
55305
55306     // private
55307     afterLoad : function(){
55308          this.removeClass("x-tree-node-loading");
55309     },
55310
55311     // private
55312     onTextChange : function(node, text, oldText){
55313         if(this.rendered){
55314             this.textNode.innerHTML = text;
55315         }
55316     },
55317     
55318     // private
55319     onIconClsChange : function(node, cls, oldCls){
55320         if(this.rendered){
55321             Ext.fly(this.iconNode).replaceClass(oldCls, cls);
55322         }
55323     },
55324     
55325     // private
55326     onIconChange : function(node, icon){
55327         if(this.rendered){
55328             //'<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
55329             var empty = Ext.isEmpty(icon);
55330             this.iconNode.src = empty ? this.emptyIcon : icon;
55331             Ext.fly(this.iconNode)[empty ? 'removeClass' : 'addClass']('x-tree-node-inline-icon');
55332         }
55333     },
55334     
55335     // private
55336     onTipChange : function(node, tip, title){
55337         if(this.rendered){
55338             var hasTitle = Ext.isDefined(title);
55339             if(this.textNode.setAttributeNS){
55340                 this.textNode.setAttributeNS("ext", "qtip", tip);
55341                 if(hasTitle){
55342                     this.textNode.setAttributeNS("ext", "qtitle", title);
55343                 }
55344             }else{
55345                 this.textNode.setAttribute("ext:qtip", tip);
55346                 if(hasTitle){
55347                     this.textNode.setAttribute("ext:qtitle", title);
55348                 }
55349             }
55350         }
55351     },
55352     
55353     // private
55354     onHrefChange : function(node, href, target){
55355         if(this.rendered){
55356             this.anchor.href = this.getHref(href);
55357             if(Ext.isDefined(target)){
55358                 this.anchor.target = target;
55359             }
55360         }
55361     },
55362     
55363     // private
55364     onClsChange : function(node, cls, oldCls){
55365         if(this.rendered){
55366             Ext.fly(this.elNode).replaceClass(oldCls, cls);
55367         }    
55368     },
55369
55370     // private
55371     onDisableChange : function(node, state){
55372         this.disabled = state;
55373         if (this.checkbox) {
55374             this.checkbox.disabled = state;
55375         }
55376         this[state ? 'addClass' : 'removeClass']('x-tree-node-disabled');
55377     },
55378
55379     // private
55380     onSelectedChange : function(state){
55381         if(state){
55382             this.focus();
55383             this.addClass("x-tree-selected");
55384         }else{
55385             //this.blur();
55386             this.removeClass("x-tree-selected");
55387         }
55388     },
55389
55390     // private
55391     onMove : function(tree, node, oldParent, newParent, index, refNode){
55392         this.childIndent = null;
55393         if(this.rendered){
55394             var targetNode = newParent.ui.getContainer();
55395             if(!targetNode){//target not rendered
55396                 this.holder = document.createElement("div");
55397                 this.holder.appendChild(this.wrap);
55398                 return;
55399             }
55400             var insertBefore = refNode ? refNode.ui.getEl() : null;
55401             if(insertBefore){
55402                 targetNode.insertBefore(this.wrap, insertBefore);
55403             }else{
55404                 targetNode.appendChild(this.wrap);
55405             }
55406             this.node.renderIndent(true, oldParent != newParent);
55407         }
55408     },
55409
55410 /**
55411  * Adds one or more CSS classes to the node's UI element.
55412  * Duplicate classes are automatically filtered out.
55413  * @param {String/Array} className The CSS class to add, or an array of classes
55414  */
55415     addClass : function(cls){
55416         if(this.elNode){
55417             Ext.fly(this.elNode).addClass(cls);
55418         }
55419     },
55420
55421 /**
55422  * Removes one or more CSS classes from the node's UI element.
55423  * @param {String/Array} className The CSS class to remove, or an array of classes
55424  */
55425     removeClass : function(cls){
55426         if(this.elNode){
55427             Ext.fly(this.elNode).removeClass(cls);
55428         }
55429     },
55430
55431     // private
55432     remove : function(){
55433         if(this.rendered){
55434             this.holder = document.createElement("div");
55435             this.holder.appendChild(this.wrap);
55436         }
55437     },
55438
55439     // private
55440     fireEvent : function(){
55441         return this.node.fireEvent.apply(this.node, arguments);
55442     },
55443
55444     // private
55445     initEvents : function(){
55446         this.node.on("move", this.onMove, this);
55447
55448         if(this.node.disabled){
55449             this.onDisableChange(this.node, true);
55450         }
55451         if(this.node.hidden){
55452             this.hide();
55453         }
55454         var ot = this.node.getOwnerTree();
55455         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
55456         if(dd && (!this.node.isRoot || ot.rootVisible)){
55457             Ext.dd.Registry.register(this.elNode, {
55458                 node: this.node,
55459                 handles: this.getDDHandles(),
55460                 isHandle: false
55461             });
55462         }
55463     },
55464
55465     // private
55466     getDDHandles : function(){
55467         return [this.iconNode, this.textNode, this.elNode];
55468     },
55469
55470 /**
55471  * Hides this node.
55472  */
55473     hide : function(){
55474         this.node.hidden = true;
55475         if(this.wrap){
55476             this.wrap.style.display = "none";
55477         }
55478     },
55479
55480 /**
55481  * Shows this node.
55482  */
55483     show : function(){
55484         this.node.hidden = false;
55485         if(this.wrap){
55486             this.wrap.style.display = "";
55487         }
55488     },
55489
55490     // private
55491     onContextMenu : function(e){
55492         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
55493             e.preventDefault();
55494             this.focus();
55495             this.fireEvent("contextmenu", this.node, e);
55496         }
55497     },
55498
55499     // private
55500     onClick : function(e){
55501         if(this.dropping){
55502             e.stopEvent();
55503             return;
55504         }
55505         if(this.fireEvent("beforeclick", this.node, e) !== false){
55506             var a = e.getTarget('a');
55507             if(!this.disabled && this.node.attributes.href && a){
55508                 this.fireEvent("click", this.node, e);
55509                 return;
55510             }else if(a && e.ctrlKey){
55511                 e.stopEvent();
55512             }
55513             e.preventDefault();
55514             if(this.disabled){
55515                 return;
55516             }
55517
55518             if(this.node.attributes.singleClickExpand && !this.animating && this.node.isExpandable()){
55519                 this.node.toggle();
55520             }
55521
55522             this.fireEvent("click", this.node, e);
55523         }else{
55524             e.stopEvent();
55525         }
55526     },
55527
55528     // private
55529     onDblClick : function(e){
55530         e.preventDefault();
55531         if(this.disabled){
55532             return;
55533         }
55534         if(this.fireEvent("beforedblclick", this.node, e) !== false){
55535             if(this.checkbox){
55536                 this.toggleCheck();
55537             }
55538             if(!this.animating && this.node.isExpandable()){
55539                 this.node.toggle();
55540             }
55541             this.fireEvent("dblclick", this.node, e);
55542         }
55543     },
55544
55545     onOver : function(e){
55546         this.addClass('x-tree-node-over');
55547     },
55548
55549     onOut : function(e){
55550         this.removeClass('x-tree-node-over');
55551     },
55552
55553     // private
55554     onCheckChange : function(){
55555         var checked = this.checkbox.checked;
55556         // fix for IE6
55557         this.checkbox.defaultChecked = checked;
55558         this.node.attributes.checked = checked;
55559         this.fireEvent('checkchange', this.node, checked);
55560     },
55561
55562     // private
55563     ecClick : function(e){
55564         if(!this.animating && this.node.isExpandable()){
55565             this.node.toggle();
55566         }
55567     },
55568
55569     // private
55570     startDrop : function(){
55571         this.dropping = true;
55572     },
55573
55574     // delayed drop so the click event doesn't get fired on a drop
55575     endDrop : function(){
55576        setTimeout(function(){
55577            this.dropping = false;
55578        }.createDelegate(this), 50);
55579     },
55580
55581     // private
55582     expand : function(){
55583         this.updateExpandIcon();
55584         this.ctNode.style.display = "";
55585     },
55586
55587     // private
55588     focus : function(){
55589         if(!this.node.preventHScroll){
55590             try{this.anchor.focus();
55591             }catch(e){}
55592         }else{
55593             try{
55594                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
55595                 var l = noscroll.scrollLeft;
55596                 this.anchor.focus();
55597                 noscroll.scrollLeft = l;
55598             }catch(e){}
55599         }
55600     },
55601
55602 /**
55603  * Sets the checked status of the tree node to the passed value, or, if no value was passed,
55604  * toggles the checked status. If the node was rendered with no checkbox, this has no effect.
55605  * @param {Boolean} value (optional) The new checked status.
55606  */
55607     toggleCheck : function(value){
55608         var cb = this.checkbox;
55609         if(cb){
55610             cb.checked = (value === undefined ? !cb.checked : value);
55611             this.onCheckChange();
55612         }
55613     },
55614
55615     // private
55616     blur : function(){
55617         try{
55618             this.anchor.blur();
55619         }catch(e){}
55620     },
55621
55622     // private
55623     animExpand : function(callback){
55624         var ct = Ext.get(this.ctNode);
55625         ct.stopFx();
55626         if(!this.node.isExpandable()){
55627             this.updateExpandIcon();
55628             this.ctNode.style.display = "";
55629             Ext.callback(callback);
55630             return;
55631         }
55632         this.animating = true;
55633         this.updateExpandIcon();
55634
55635         ct.slideIn('t', {
55636            callback : function(){
55637                this.animating = false;
55638                Ext.callback(callback);
55639             },
55640             scope: this,
55641             duration: this.node.ownerTree.duration || .25
55642         });
55643     },
55644
55645     // private
55646     highlight : function(){
55647         var tree = this.node.getOwnerTree();
55648         Ext.fly(this.wrap).highlight(
55649             tree.hlColor || "C3DAF9",
55650             {endColor: tree.hlBaseColor}
55651         );
55652     },
55653
55654     // private
55655     collapse : function(){
55656         this.updateExpandIcon();
55657         this.ctNode.style.display = "none";
55658     },
55659
55660     // private
55661     animCollapse : function(callback){
55662         var ct = Ext.get(this.ctNode);
55663         ct.enableDisplayMode('block');
55664         ct.stopFx();
55665
55666         this.animating = true;
55667         this.updateExpandIcon();
55668
55669         ct.slideOut('t', {
55670             callback : function(){
55671                this.animating = false;
55672                Ext.callback(callback);
55673             },
55674             scope: this,
55675             duration: this.node.ownerTree.duration || .25
55676         });
55677     },
55678
55679     // private
55680     getContainer : function(){
55681         return this.ctNode;
55682     },
55683
55684 /**
55685  * Returns the element which encapsulates this node.
55686  * @return {HtmlElement} The DOM element. The default implementation uses a <code>&lt;li></code>.
55687  */
55688     getEl : function(){
55689         return this.wrap;
55690     },
55691
55692     // private
55693     appendDDGhost : function(ghostNode){
55694         ghostNode.appendChild(this.elNode.cloneNode(true));
55695     },
55696
55697     // private
55698     getDDRepairXY : function(){
55699         return Ext.lib.Dom.getXY(this.iconNode);
55700     },
55701
55702     // private
55703     onRender : function(){
55704         this.render();
55705     },
55706
55707     // private
55708     render : function(bulkRender){
55709         var n = this.node, a = n.attributes;
55710         var targetNode = n.parentNode ?
55711               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
55712
55713         if(!this.rendered){
55714             this.rendered = true;
55715
55716             this.renderElements(n, a, targetNode, bulkRender);
55717
55718             if(a.qtip){
55719                 this.onTipChange(n, a.qtip, a.qtipTitle);
55720             }else if(a.qtipCfg){
55721                 a.qtipCfg.target = Ext.id(this.textNode);
55722                 Ext.QuickTips.register(a.qtipCfg);
55723             }
55724             this.initEvents();
55725             if(!this.node.expanded){
55726                 this.updateExpandIcon(true);
55727             }
55728         }else{
55729             if(bulkRender === true) {
55730                 targetNode.appendChild(this.wrap);
55731             }
55732         }
55733     },
55734
55735     // private
55736     renderElements : function(n, a, targetNode, bulkRender){
55737         // add some indent caching, this helps performance when rendering a large tree
55738         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
55739
55740         var cb = Ext.isBoolean(a.checked),
55741             nel,
55742             href = this.getHref(a.href),
55743             buf = ['<li class="x-tree-node"><div ext:tree-node-id="',n.id,'" class="x-tree-node-el x-tree-node-leaf x-unselectable ', a.cls,'" unselectable="on">',
55744             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
55745             '<img alt="" src="', this.emptyIcon, '" class="x-tree-ec-icon x-tree-elbow" />',
55746             '<img alt="" src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
55747             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : '/>')) : '',
55748             '<a hidefocus="on" class="x-tree-node-anchor" href="',href,'" tabIndex="1" ',
55749              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", '><span unselectable="on">',n.text,"</span></a></div>",
55750             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
55751             "</li>"].join('');
55752
55753         if(bulkRender !== true && n.nextSibling && (nel = n.nextSibling.ui.getEl())){
55754             this.wrap = Ext.DomHelper.insertHtml("beforeBegin", nel, buf);
55755         }else{
55756             this.wrap = Ext.DomHelper.insertHtml("beforeEnd", targetNode, buf);
55757         }
55758
55759         this.elNode = this.wrap.childNodes[0];
55760         this.ctNode = this.wrap.childNodes[1];
55761         var cs = this.elNode.childNodes;
55762         this.indentNode = cs[0];
55763         this.ecNode = cs[1];
55764         this.iconNode = cs[2];
55765         var index = 3;
55766         if(cb){
55767             this.checkbox = cs[3];
55768             // fix for IE6
55769             this.checkbox.defaultChecked = this.checkbox.checked;
55770             index++;
55771         }
55772         this.anchor = cs[index];
55773         this.textNode = cs[index].firstChild;
55774     },
55775     
55776     /**
55777      * @private Gets a normalized href for the node.
55778      * @param {String} href
55779      */
55780     getHref : function(href){
55781         return Ext.isEmpty(href) ? (Ext.isGecko ? '' : '#') : href;
55782     },
55783
55784 /**
55785  * Returns the &lt;a> element that provides focus for the node's UI.
55786  * @return {HtmlElement} The DOM anchor element.
55787  */
55788     getAnchor : function(){
55789         return this.anchor;
55790     },
55791
55792 /**
55793  * Returns the text node.
55794  * @return {HtmlNode} The DOM text node.
55795  */
55796     getTextEl : function(){
55797         return this.textNode;
55798     },
55799
55800 /**
55801  * Returns the icon &lt;img> element.
55802  * @return {HtmlElement} The DOM image element.
55803  */
55804     getIconEl : function(){
55805         return this.iconNode;
55806     },
55807
55808 /**
55809  * Returns the checked status of the node. If the node was rendered with no
55810  * checkbox, it returns false.
55811  * @return {Boolean} The checked flag.
55812  */
55813     isChecked : function(){
55814         return this.checkbox ? this.checkbox.checked : false;
55815     },
55816
55817     // private
55818     updateExpandIcon : function(){
55819         if(this.rendered){
55820             var n = this.node,
55821                 c1,
55822                 c2,
55823                 cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow",
55824                 hasChild = n.hasChildNodes();
55825             if(hasChild || n.attributes.expandable){
55826                 if(n.expanded){
55827                     cls += "-minus";
55828                     c1 = "x-tree-node-collapsed";
55829                     c2 = "x-tree-node-expanded";
55830                 }else{
55831                     cls += "-plus";
55832                     c1 = "x-tree-node-expanded";
55833                     c2 = "x-tree-node-collapsed";
55834                 }
55835                 if(this.wasLeaf){
55836                     this.removeClass("x-tree-node-leaf");
55837                     this.wasLeaf = false;
55838                 }
55839                 if(this.c1 != c1 || this.c2 != c2){
55840                     Ext.fly(this.elNode).replaceClass(c1, c2);
55841                     this.c1 = c1; this.c2 = c2;
55842                 }
55843             }else{
55844                 if(!this.wasLeaf){
55845                     Ext.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-collapsed");
55846                     delete this.c1;
55847                     delete this.c2;
55848                     this.wasLeaf = true;
55849                 }
55850             }
55851             var ecc = "x-tree-ec-icon "+cls;
55852             if(this.ecc != ecc){
55853                 this.ecNode.className = ecc;
55854                 this.ecc = ecc;
55855             }
55856         }
55857     },
55858
55859     // private
55860     onIdChange: function(id){
55861         if(this.rendered){
55862             this.elNode.setAttribute('ext:tree-node-id', id);
55863         }
55864     },
55865
55866     // private
55867     getChildIndent : function(){
55868         if(!this.childIndent){
55869             var buf = [],
55870                 p = this.node;
55871             while(p){
55872                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
55873                     if(!p.isLast()) {
55874                         buf.unshift('<img alt="" src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
55875                     } else {
55876                         buf.unshift('<img alt="" src="'+this.emptyIcon+'" class="x-tree-icon" />');
55877                     }
55878                 }
55879                 p = p.parentNode;
55880             }
55881             this.childIndent = buf.join("");
55882         }
55883         return this.childIndent;
55884     },
55885
55886     // private
55887     renderIndent : function(){
55888         if(this.rendered){
55889             var indent = "",
55890                 p = this.node.parentNode;
55891             if(p){
55892                 indent = p.ui.getChildIndent();
55893             }
55894             if(this.indentMarkup != indent){ // don't rerender if not required
55895                 this.indentNode.innerHTML = indent;
55896                 this.indentMarkup = indent;
55897             }
55898             this.updateExpandIcon();
55899         }
55900     },
55901
55902     destroy : function(){
55903         if(this.elNode){
55904             Ext.dd.Registry.unregister(this.elNode.id);
55905         }
55906
55907         Ext.each(['textnode', 'anchor', 'checkbox', 'indentNode', 'ecNode', 'iconNode', 'elNode', 'ctNode', 'wrap', 'holder'], function(el){
55908             if(this[el]){
55909                 Ext.fly(this[el]).remove();
55910                 delete this[el];
55911             }
55912         }, this);
55913         delete this.node;
55914     }
55915 });
55916
55917 /**
55918  * @class Ext.tree.RootTreeNodeUI
55919  * This class provides the default UI implementation for <b>root</b> Ext TreeNodes.
55920  * The RootTreeNode UI implementation allows customizing the appearance of the root tree node.<br>
55921  * <p>
55922  * If you are customizing the Tree's user interface, you
55923  * may need to extend this class, but you should never need to instantiate this class.<br>
55924  */
55925 Ext.tree.RootTreeNodeUI = Ext.extend(Ext.tree.TreeNodeUI, {
55926     // private
55927     render : function(){
55928         if(!this.rendered){
55929             var targetNode = this.node.ownerTree.innerCt.dom;
55930             this.node.expanded = true;
55931             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
55932             this.wrap = this.ctNode = targetNode.firstChild;
55933         }
55934     },
55935     collapse : Ext.emptyFn,
55936     expand : Ext.emptyFn
55937 });/**
55938  * @class Ext.tree.TreeLoader
55939  * @extends Ext.util.Observable
55940  * A TreeLoader provides for lazy loading of an {@link Ext.tree.TreeNode}'s child
55941  * nodes from a specified URL. The response must be a JavaScript Array definition
55942  * whose elements are node definition objects. e.g.:
55943  * <pre><code>
55944     [{
55945         id: 1,
55946         text: 'A leaf Node',
55947         leaf: true
55948     },{
55949         id: 2,
55950         text: 'A folder Node',
55951         children: [{
55952             id: 3,
55953             text: 'A child Node',
55954             leaf: true
55955         }]
55956    }]
55957 </code></pre>
55958  * <br><br>
55959  * A server request is sent, and child nodes are loaded only when a node is expanded.
55960  * The loading node's id is passed to the server under the parameter name "node" to
55961  * enable the server to produce the correct child nodes.
55962  * <br><br>
55963  * To pass extra parameters, an event handler may be attached to the "beforeload"
55964  * event, and the parameters specified in the TreeLoader's baseParams property:
55965  * <pre><code>
55966     myTreeLoader.on("beforeload", function(treeLoader, node) {
55967         this.baseParams.category = node.attributes.category;
55968     }, this);
55969 </code></pre>
55970  * This would pass an HTTP parameter called "category" to the server containing
55971  * the value of the Node's "category" attribute.
55972  * @constructor
55973  * Creates a new Treeloader.
55974  * @param {Object} config A config object containing config properties.
55975  */
55976 Ext.tree.TreeLoader = function(config){
55977     this.baseParams = {};
55978     Ext.apply(this, config);
55979
55980     this.addEvents(
55981         /**
55982          * @event beforeload
55983          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
55984          * @param {Object} This TreeLoader object.
55985          * @param {Object} node The {@link Ext.tree.TreeNode} object being loaded.
55986          * @param {Object} callback The callback function specified in the {@link #load} call.
55987          */
55988         "beforeload",
55989         /**
55990          * @event load
55991          * Fires when the node has been successfuly loaded.
55992          * @param {Object} This TreeLoader object.
55993          * @param {Object} node The {@link Ext.tree.TreeNode} object being loaded.
55994          * @param {Object} response The response object containing the data from the server.
55995          */
55996         "load",
55997         /**
55998          * @event loadexception
55999          * Fires if the network request failed.
56000          * @param {Object} This TreeLoader object.
56001          * @param {Object} node The {@link Ext.tree.TreeNode} object being loaded.
56002          * @param {Object} response The response object containing the data from the server.
56003          */
56004         "loadexception"
56005     );
56006     Ext.tree.TreeLoader.superclass.constructor.call(this);
56007     if(Ext.isString(this.paramOrder)){
56008         this.paramOrder = this.paramOrder.split(/[\s,|]/);
56009     }
56010 };
56011
56012 Ext.extend(Ext.tree.TreeLoader, Ext.util.Observable, {
56013     /**
56014     * @cfg {String} dataUrl The URL from which to request a Json string which
56015     * specifies an array of node definition objects representing the child nodes
56016     * to be loaded.
56017     */
56018     /**
56019      * @cfg {String} requestMethod The HTTP request method for loading data (defaults to the value of {@link Ext.Ajax#method}).
56020      */
56021     /**
56022      * @cfg {String} url Equivalent to {@link #dataUrl}.
56023      */
56024     /**
56025      * @cfg {Boolean} preloadChildren If set to true, the loader recursively loads "children" attributes when doing the first load on nodes.
56026      */
56027     /**
56028     * @cfg {Object} baseParams (optional) An object containing properties which
56029     * specify HTTP parameters to be passed to each request for child nodes.
56030     */
56031     /**
56032     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
56033     * created by this loader. If the attributes sent by the server have an attribute in this object,
56034     * they take priority.
56035     */
56036     /**
56037     * @cfg {Object} uiProviders (optional) An object containing properties which
56038     * specify custom {@link Ext.tree.TreeNodeUI} implementations. If the optional
56039     * <i>uiProvider</i> attribute of a returned child node is a string rather
56040     * than a reference to a TreeNodeUI implementation, then that string value
56041     * is used as a property name in the uiProviders object.
56042     */
56043     uiProviders : {},
56044
56045     /**
56046     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
56047     * child nodes before loading.
56048     */
56049     clearOnLoad : true,
56050
56051     /**
56052      * @cfg {Array/String} paramOrder Defaults to <tt>undefined</tt>. Only used when using directFn.
56053      * Specifies the params in the order in which they must be passed to the server-side Direct method
56054      * as either (1) an Array of String values, or (2) a String of params delimited by either whitespace,
56055      * comma, or pipe. For example,
56056      * any of the following would be acceptable:<pre><code>
56057 nodeParameter: 'node',
56058 paramOrder: ['param1','param2','param3']
56059 paramOrder: 'node param1 param2 param3'
56060 paramOrder: 'param1,node,param2,param3'
56061 paramOrder: 'param1|param2|param|node'
56062      </code></pre>
56063      */
56064     paramOrder: undefined,
56065
56066     /**
56067      * @cfg {Boolean} paramsAsHash Only used when using directFn.
56068      * Send parameters as a collection of named arguments (defaults to <tt>false</tt>). Providing a
56069      * <tt>{@link #paramOrder}</tt> nullifies this configuration.
56070      */
56071     paramsAsHash: false,
56072
56073     /**
56074      * @cfg {String} nodeParameter The name of the parameter sent to the server which contains
56075      * the identifier of the node. Defaults to <tt>'node'</tt>.
56076      */
56077     nodeParameter: 'node',
56078
56079     /**
56080      * @cfg {Function} directFn
56081      * Function to call when executing a request.
56082      */
56083     directFn : undefined,
56084
56085     /**
56086      * Load an {@link Ext.tree.TreeNode} from the URL specified in the constructor.
56087      * This is called automatically when a node is expanded, but may be used to reload
56088      * a node (or append new children if the {@link #clearOnLoad} option is false.)
56089      * @param {Ext.tree.TreeNode} node
56090      * @param {Function} callback Function to call after the node has been loaded. The
56091      * function is passed the TreeNode which was requested to be loaded.
56092      * @param {Object} scope The scope (<code>this</code> reference) in which the callback is executed.
56093      * defaults to the loaded TreeNode.
56094      */
56095     load : function(node, callback, scope){
56096         if(this.clearOnLoad){
56097             while(node.firstChild){
56098                 node.removeChild(node.firstChild);
56099             }
56100         }
56101         if(this.doPreload(node)){ // preloaded json children
56102             this.runCallback(callback, scope || node, [node]);
56103         }else if(this.directFn || this.dataUrl || this.url){
56104             this.requestData(node, callback, scope || node);
56105         }
56106     },
56107
56108     doPreload : function(node){
56109         if(node.attributes.children){
56110             if(node.childNodes.length < 1){ // preloaded?
56111                 var cs = node.attributes.children;
56112                 node.beginUpdate();
56113                 for(var i = 0, len = cs.length; i < len; i++){
56114                     var cn = node.appendChild(this.createNode(cs[i]));
56115                     if(this.preloadChildren){
56116                         this.doPreload(cn);
56117                     }
56118                 }
56119                 node.endUpdate();
56120             }
56121             return true;
56122         }
56123         return false;
56124     },
56125
56126     getParams: function(node){
56127         var bp = Ext.apply({}, this.baseParams),
56128             np = this.nodeParameter,
56129             po = this.paramOrder;
56130
56131         np && (bp[ np ] = node.id);
56132
56133         if(this.directFn){
56134             var buf = [node.id];
56135             if(po){
56136                 // reset 'buf' if the nodeParameter was included in paramOrder
56137                 if(np && po.indexOf(np) > -1){
56138                     buf = [];
56139                 }
56140
56141                 for(var i = 0, len = po.length; i < len; i++){
56142                     buf.push(bp[ po[i] ]);
56143                 }
56144             }else if(this.paramsAsHash){
56145                 buf = [bp];
56146             }
56147             return buf;
56148         }else{
56149             return bp;
56150         }
56151     },
56152
56153     requestData : function(node, callback, scope){
56154         if(this.fireEvent("beforeload", this, node, callback) !== false){
56155             if(this.directFn){
56156                 var args = this.getParams(node);
56157                 args.push(this.processDirectResponse.createDelegate(this, [{callback: callback, node: node, scope: scope}], true));
56158                 this.directFn.apply(window, args);
56159             }else{
56160                 this.transId = Ext.Ajax.request({
56161                     method:this.requestMethod,
56162                     url: this.dataUrl||this.url,
56163                     success: this.handleResponse,
56164                     failure: this.handleFailure,
56165                     scope: this,
56166                     argument: {callback: callback, node: node, scope: scope},
56167                     params: this.getParams(node)
56168                 });
56169             }
56170         }else{
56171             // if the load is cancelled, make sure we notify
56172             // the node that we are done
56173             this.runCallback(callback, scope || node, []);
56174         }
56175     },
56176
56177     processDirectResponse: function(result, response, args){
56178         if(response.status){
56179             this.handleResponse({
56180                 responseData: Ext.isArray(result) ? result : null,
56181                 responseText: result,
56182                 argument: args
56183             });
56184         }else{
56185             this.handleFailure({
56186                 argument: args
56187             });
56188         }
56189     },
56190
56191     // private
56192     runCallback: function(cb, scope, args){
56193         if(Ext.isFunction(cb)){
56194             cb.apply(scope, args);
56195         }
56196     },
56197
56198     isLoading : function(){
56199         return !!this.transId;
56200     },
56201
56202     abort : function(){
56203         if(this.isLoading()){
56204             Ext.Ajax.abort(this.transId);
56205         }
56206     },
56207
56208     /**
56209     * <p>Override this function for custom TreeNode node implementation, or to
56210     * modify the attributes at creation time.</p>
56211     * Example:<pre><code>
56212 new Ext.tree.TreePanel({
56213     ...
56214     loader: new Ext.tree.TreeLoader({
56215         url: 'dataUrl',
56216         createNode: function(attr) {
56217 //          Allow consolidation consignments to have
56218 //          consignments dropped into them.
56219             if (attr.isConsolidation) {
56220                 attr.iconCls = 'x-consol',
56221                 attr.allowDrop = true;
56222             }
56223             return Ext.tree.TreeLoader.prototype.createNode.call(this, attr);
56224         }
56225     }),
56226     ...
56227 });
56228 </code></pre>
56229     * @param attr {Object} The attributes from which to create the new node.
56230     */
56231     createNode : function(attr){
56232         // apply baseAttrs, nice idea Corey!
56233         if(this.baseAttrs){
56234             Ext.applyIf(attr, this.baseAttrs);
56235         }
56236         if(this.applyLoader !== false && !attr.loader){
56237             attr.loader = this;
56238         }
56239         if(Ext.isString(attr.uiProvider)){
56240            attr.uiProvider = this.uiProviders[attr.uiProvider] || eval(attr.uiProvider);
56241         }
56242         if(attr.nodeType){
56243             return new Ext.tree.TreePanel.nodeTypes[attr.nodeType](attr);
56244         }else{
56245             return attr.leaf ?
56246                         new Ext.tree.TreeNode(attr) :
56247                         new Ext.tree.AsyncTreeNode(attr);
56248         }
56249     },
56250
56251     processResponse : function(response, node, callback, scope){
56252         var json = response.responseText;
56253         try {
56254             var o = response.responseData || Ext.decode(json);
56255             node.beginUpdate();
56256             for(var i = 0, len = o.length; i < len; i++){
56257                 var n = this.createNode(o[i]);
56258                 if(n){
56259                     node.appendChild(n);
56260                 }
56261             }
56262             node.endUpdate();
56263             this.runCallback(callback, scope || node, [node]);
56264         }catch(e){
56265             this.handleFailure(response);
56266         }
56267     },
56268
56269     handleResponse : function(response){
56270         this.transId = false;
56271         var a = response.argument;
56272         this.processResponse(response, a.node, a.callback, a.scope);
56273         this.fireEvent("load", this, a.node, response);
56274     },
56275
56276     handleFailure : function(response){
56277         this.transId = false;
56278         var a = response.argument;
56279         this.fireEvent("loadexception", this, a.node, response);
56280         this.runCallback(a.callback, a.scope || a.node, [a.node]);
56281     },
56282
56283     destroy : function(){
56284         this.abort();
56285         this.purgeListeners();
56286     }
56287 });/**
56288  * @class Ext.tree.TreeFilter
56289  * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
56290  * @param {TreePanel} tree
56291  * @param {Object} config (optional)
56292  */
56293 Ext.tree.TreeFilter = function(tree, config){
56294     this.tree = tree;
56295     this.filtered = {};
56296     Ext.apply(this, config);
56297 };
56298
56299 Ext.tree.TreeFilter.prototype = {
56300     clearBlank:false,
56301     reverse:false,
56302     autoClear:false,
56303     remove:false,
56304
56305      /**
56306      * Filter the data by a specific attribute.
56307      * @param {String/RegExp} value Either string that the attribute value
56308      * should start with or a RegExp to test against the attribute
56309      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
56310      * @param {TreeNode} startNode (optional) The node to start the filter at.
56311      */
56312     filter : function(value, attr, startNode){
56313         attr = attr || "text";
56314         var f;
56315         if(typeof value == "string"){
56316             var vlen = value.length;
56317             // auto clear empty filter
56318             if(vlen == 0 && this.clearBlank){
56319                 this.clear();
56320                 return;
56321             }
56322             value = value.toLowerCase();
56323             f = function(n){
56324                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
56325             };
56326         }else if(value.exec){ // regex?
56327             f = function(n){
56328                 return value.test(n.attributes[attr]);
56329             };
56330         }else{
56331             throw 'Illegal filter type, must be string or regex';
56332         }
56333         this.filterBy(f, null, startNode);
56334         },
56335
56336     /**
56337      * Filter by a function. The passed function will be called with each
56338      * node in the tree (or from the startNode). If the function returns true, the node is kept
56339      * otherwise it is filtered. If a node is filtered, its children are also filtered.
56340      * @param {Function} fn The filter function
56341      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the current Node.
56342      */
56343     filterBy : function(fn, scope, startNode){
56344         startNode = startNode || this.tree.root;
56345         if(this.autoClear){
56346             this.clear();
56347         }
56348         var af = this.filtered, rv = this.reverse;
56349         var f = function(n){
56350             if(n == startNode){
56351                 return true;
56352             }
56353             if(af[n.id]){
56354                 return false;
56355             }
56356             var m = fn.call(scope || n, n);
56357             if(!m || rv){
56358                 af[n.id] = n;
56359                 n.ui.hide();
56360                 return false;
56361             }
56362             return true;
56363         };
56364         startNode.cascade(f);
56365         if(this.remove){
56366            for(var id in af){
56367                if(typeof id != "function"){
56368                    var n = af[id];
56369                    if(n && n.parentNode){
56370                        n.parentNode.removeChild(n);
56371                    }
56372                }
56373            }
56374         }
56375     },
56376
56377     /**
56378      * Clears the current filter. Note: with the "remove" option
56379      * set a filter cannot be cleared.
56380      */
56381     clear : function(){
56382         var t = this.tree;
56383         var af = this.filtered;
56384         for(var id in af){
56385             if(typeof id != "function"){
56386                 var n = af[id];
56387                 if(n){
56388                     n.ui.show();
56389                 }
56390             }
56391         }
56392         this.filtered = {};
56393     }
56394 };
56395 /**
56396  * @class Ext.tree.TreeSorter
56397  * Provides sorting of nodes in a {@link Ext.tree.TreePanel}.  The TreeSorter automatically monitors events on the
56398  * associated TreePanel that might affect the tree's sort order (beforechildrenrendered, append, insert and textchange).
56399  * Example usage:<br />
56400  * <pre><code>
56401 new Ext.tree.TreeSorter(myTree, {
56402     folderSort: true,
56403     dir: "desc",
56404     sortType: function(node) {
56405         // sort by a custom, typed attribute:
56406         return parseInt(node.id, 10);
56407     }
56408 });
56409 </code></pre>
56410  * @constructor
56411  * @param {TreePanel} tree
56412  * @param {Object} config
56413  */
56414 Ext.tree.TreeSorter = Ext.extend(Object, {
56415     
56416     constructor: function(tree, config){
56417         /**
56418      * @cfg {Boolean} folderSort True to sort leaf nodes under non-leaf nodes (defaults to false)
56419      */
56420     /**
56421      * @cfg {String} property The named attribute on the node to sort by (defaults to "text").  Note that this
56422      * property is only used if no {@link #sortType} function is specified, otherwise it is ignored.
56423      */
56424     /**
56425      * @cfg {String} dir The direction to sort ("asc" or "desc," case-insensitive, defaults to "asc")
56426      */
56427     /**
56428      * @cfg {String} leafAttr The attribute used to determine leaf nodes when {@link #folderSort} = true (defaults to "leaf")
56429      */
56430     /**
56431      * @cfg {Boolean} caseSensitive true for case-sensitive sort (defaults to false)
56432      */
56433     /**
56434      * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting.  The function
56435      * will be called with a single parameter (the {@link Ext.tree.TreeNode} being evaluated) and is expected to return
56436      * the node's sort value cast to the specific data type required for sorting.  This could be used, for example, when
56437      * a node's text (or other attribute) should be sorted as a date or numeric value.  See the class description for
56438      * example usage.  Note that if a sortType is specified, any {@link #property} config will be ignored.
56439      */
56440
56441     Ext.apply(this, config);
56442     tree.on({
56443         scope: this,
56444         beforechildrenrendered: this.doSort,
56445         append: this.updateSort,
56446         insert: this.updateSort,
56447         textchange: this.updateSortParent
56448     });
56449
56450     var desc = this.dir && this.dir.toLowerCase() == 'desc',
56451         prop = this.property || 'text';
56452         sortType = this.sortType;
56453         folderSort = this.folderSort;
56454         caseSensitive = this.caseSensitive === true;
56455         leafAttr = this.leafAttr || 'leaf';
56456
56457     if(Ext.isString(sortType)){
56458         sortType = Ext.data.SortTypes[sortType];
56459     }
56460     this.sortFn = function(n1, n2){
56461         var attr1 = n1.attributes,
56462             attr2 = n2.attributes;
56463             
56464         if(folderSort){
56465             if(attr1[leafAttr] && !attr2[leafAttr]){
56466                 return 1;
56467             }
56468             if(!attr1[leafAttr] && attr2[leafAttr]){
56469                 return -1;
56470             }
56471         }
56472         var prop1 = attr1[prop],
56473             prop2 = attr2[prop],
56474             v1 = sortType ? sortType(prop1) : (caseSensitive ? prop1 : prop1.toUpperCase());
56475             v2 = sortType ? sortType(prop2) : (caseSensitive ? prop2 : prop2.toUpperCase());
56476             
56477         if(v1 < v2){
56478             return desc ? 1 : -1;
56479         }else if(v1 > v2){
56480             return desc ? -1 : 1;
56481         }
56482         return 0;
56483     };
56484     },
56485     
56486     doSort : function(node){
56487         node.sort(this.sortFn);
56488     },
56489
56490     updateSort : function(tree, node){
56491         if(node.childrenRendered){
56492             this.doSort.defer(1, this, [node]);
56493         }
56494     },
56495
56496     updateSortParent : function(node){
56497         var p = node.parentNode;
56498         if(p && p.childrenRendered){
56499             this.doSort.defer(1, this, [p]);
56500         }
56501     }    
56502 });/**
56503  * @class Ext.tree.TreeDropZone
56504  * @extends Ext.dd.DropZone
56505  * @constructor
56506  * @param {String/HTMLElement/Element} tree The {@link Ext.tree.TreePanel} for which to enable dropping
56507  * @param {Object} config
56508  */
56509 if(Ext.dd.DropZone){
56510     
56511 Ext.tree.TreeDropZone = function(tree, config){
56512     /**
56513      * @cfg {Boolean} allowParentInsert
56514      * Allow inserting a dragged node between an expanded parent node and its first child that will become a
56515      * sibling of the parent when dropped (defaults to false)
56516      */
56517     this.allowParentInsert = config.allowParentInsert || false;
56518     /**
56519      * @cfg {String} allowContainerDrop
56520      * True if drops on the tree container (outside of a specific tree node) are allowed (defaults to false)
56521      */
56522     this.allowContainerDrop = config.allowContainerDrop || false;
56523     /**
56524      * @cfg {String} appendOnly
56525      * True if the tree should only allow append drops (use for trees which are sorted, defaults to false)
56526      */
56527     this.appendOnly = config.appendOnly || false;
56528
56529     Ext.tree.TreeDropZone.superclass.constructor.call(this, tree.getTreeEl(), config);
56530     /**
56531     * The TreePanel for this drop zone
56532     * @type Ext.tree.TreePanel
56533     * @property
56534     */
56535     this.tree = tree;
56536     /**
56537     * Arbitrary data that can be associated with this tree and will be included in the event object that gets
56538     * passed to any nodedragover event handler (defaults to {})
56539     * @type Ext.tree.TreePanel
56540     * @property
56541     */
56542     this.dragOverData = {};
56543     // private
56544     this.lastInsertClass = "x-tree-no-status";
56545 };
56546
56547 Ext.extend(Ext.tree.TreeDropZone, Ext.dd.DropZone, {
56548     /**
56549      * @cfg {String} ddGroup
56550      * A named drag drop group to which this object belongs.  If a group is specified, then this object will only
56551      * interact with other drag drop objects in the same group (defaults to 'TreeDD').
56552      */
56553     ddGroup : "TreeDD",
56554
56555     /**
56556      * @cfg {String} expandDelay
56557      * The delay in milliseconds to wait before expanding a target tree node while dragging a droppable node
56558      * over the target (defaults to 1000)
56559      */
56560     expandDelay : 1000,
56561
56562     // private
56563     expandNode : function(node){
56564         if(node.hasChildNodes() && !node.isExpanded()){
56565             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
56566         }
56567     },
56568
56569     // private
56570     queueExpand : function(node){
56571         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
56572     },
56573
56574     // private
56575     cancelExpand : function(){
56576         if(this.expandProcId){
56577             clearTimeout(this.expandProcId);
56578             this.expandProcId = false;
56579         }
56580     },
56581
56582     // private
56583     isValidDropPoint : function(n, pt, dd, e, data){
56584         if(!n || !data){ return false; }
56585         var targetNode = n.node;
56586         var dropNode = data.node;
56587         // default drop rules
56588         if(!(targetNode && targetNode.isTarget && pt)){
56589             return false;
56590         }
56591         if(pt == "append" && targetNode.allowChildren === false){
56592             return false;
56593         }
56594         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
56595             return false;
56596         }
56597         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
56598             return false;
56599         }
56600         // reuse the object
56601         var overEvent = this.dragOverData;
56602         overEvent.tree = this.tree;
56603         overEvent.target = targetNode;
56604         overEvent.data = data;
56605         overEvent.point = pt;
56606         overEvent.source = dd;
56607         overEvent.rawEvent = e;
56608         overEvent.dropNode = dropNode;
56609         overEvent.cancel = false;  
56610         var result = this.tree.fireEvent("nodedragover", overEvent);
56611         return overEvent.cancel === false && result !== false;
56612     },
56613
56614     // private
56615     getDropPoint : function(e, n, dd){
56616         var tn = n.node;
56617         if(tn.isRoot){
56618             return tn.allowChildren !== false ? "append" : false; // always append for root
56619         }
56620         var dragEl = n.ddel;
56621         var t = Ext.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
56622         var y = Ext.lib.Event.getPageY(e);
56623         var noAppend = tn.allowChildren === false || tn.isLeaf();
56624         if(this.appendOnly || tn.parentNode.allowChildren === false){
56625             return noAppend ? false : "append";
56626         }
56627         var noBelow = false;
56628         if(!this.allowParentInsert){
56629             noBelow = tn.hasChildNodes() && tn.isExpanded();
56630         }
56631         var q = (b - t) / (noAppend ? 2 : 3);
56632         if(y >= t && y < (t + q)){
56633             return "above";
56634         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
56635             return "below";
56636         }else{
56637             return "append";
56638         }
56639     },
56640
56641     // private
56642     onNodeEnter : function(n, dd, e, data){
56643         this.cancelExpand();
56644     },
56645     
56646     onContainerOver : function(dd, e, data) {
56647         if (this.allowContainerDrop && this.isValidDropPoint({ ddel: this.tree.getRootNode().ui.elNode, node: this.tree.getRootNode() }, "append", dd, e, data)) {
56648             return this.dropAllowed;
56649         }
56650         return this.dropNotAllowed;
56651     },
56652
56653     // private
56654     onNodeOver : function(n, dd, e, data){
56655         var pt = this.getDropPoint(e, n, dd);
56656         var node = n.node;
56657         
56658         // auto node expand check
56659         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
56660             this.queueExpand(node);
56661         }else if(pt != "append"){
56662             this.cancelExpand();
56663         }
56664         
56665         // set the insert point style on the target node
56666         var returnCls = this.dropNotAllowed;
56667         if(this.isValidDropPoint(n, pt, dd, e, data)){
56668            if(pt){
56669                var el = n.ddel;
56670                var cls;
56671                if(pt == "above"){
56672                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
56673                    cls = "x-tree-drag-insert-above";
56674                }else if(pt == "below"){
56675                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
56676                    cls = "x-tree-drag-insert-below";
56677                }else{
56678                    returnCls = "x-tree-drop-ok-append";
56679                    cls = "x-tree-drag-append";
56680                }
56681                if(this.lastInsertClass != cls){
56682                    Ext.fly(el).replaceClass(this.lastInsertClass, cls);
56683                    this.lastInsertClass = cls;
56684                }
56685            }
56686        }
56687        return returnCls;
56688     },
56689
56690     // private
56691     onNodeOut : function(n, dd, e, data){
56692         this.cancelExpand();
56693         this.removeDropIndicators(n);
56694     },
56695
56696     // private
56697     onNodeDrop : function(n, dd, e, data){
56698         var point = this.getDropPoint(e, n, dd);
56699         var targetNode = n.node;
56700         targetNode.ui.startDrop();
56701         if(!this.isValidDropPoint(n, point, dd, e, data)){
56702             targetNode.ui.endDrop();
56703             return false;
56704         }
56705         // first try to find the drop node
56706         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
56707         return this.processDrop(targetNode, data, point, dd, e, dropNode);
56708     },
56709     
56710     onContainerDrop : function(dd, e, data){
56711         if (this.allowContainerDrop && this.isValidDropPoint({ ddel: this.tree.getRootNode().ui.elNode, node: this.tree.getRootNode() }, "append", dd, e, data)) {
56712             var targetNode = this.tree.getRootNode();       
56713             targetNode.ui.startDrop();
56714             var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, 'append', e) : null);
56715             return this.processDrop(targetNode, data, 'append', dd, e, dropNode);
56716         }
56717         return false;
56718     },
56719     
56720     // private
56721     processDrop: function(target, data, point, dd, e, dropNode){
56722         var dropEvent = {
56723             tree : this.tree,
56724             target: target,
56725             data: data,
56726             point: point,
56727             source: dd,
56728             rawEvent: e,
56729             dropNode: dropNode,
56730             cancel: !dropNode,
56731             dropStatus: false
56732         };
56733         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
56734         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
56735             target.ui.endDrop();
56736             return dropEvent.dropStatus;
56737         }
56738     
56739         target = dropEvent.target;
56740         if(point == 'append' && !target.isExpanded()){
56741             target.expand(false, null, function(){
56742                 this.completeDrop(dropEvent);
56743             }.createDelegate(this));
56744         }else{
56745             this.completeDrop(dropEvent);
56746         }
56747         return true;
56748     },
56749
56750     // private
56751     completeDrop : function(de){
56752         var ns = de.dropNode, p = de.point, t = de.target;
56753         if(!Ext.isArray(ns)){
56754             ns = [ns];
56755         }
56756         var n;
56757         for(var i = 0, len = ns.length; i < len; i++){
56758             n = ns[i];
56759             if(p == "above"){
56760                 t.parentNode.insertBefore(n, t);
56761             }else if(p == "below"){
56762                 t.parentNode.insertBefore(n, t.nextSibling);
56763             }else{
56764                 t.appendChild(n);
56765             }
56766         }
56767         n.ui.focus();
56768         if(Ext.enableFx && this.tree.hlDrop){
56769             n.ui.highlight();
56770         }
56771         t.ui.endDrop();
56772         this.tree.fireEvent("nodedrop", de);
56773     },
56774
56775     // private
56776     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
56777         if(Ext.enableFx && this.tree.hlDrop){
56778             dropNode.ui.focus();
56779             dropNode.ui.highlight();
56780         }
56781         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
56782     },
56783
56784     // private
56785     getTree : function(){
56786         return this.tree;
56787     },
56788
56789     // private
56790     removeDropIndicators : function(n){
56791         if(n && n.ddel){
56792             var el = n.ddel;
56793             Ext.fly(el).removeClass([
56794                     "x-tree-drag-insert-above",
56795                     "x-tree-drag-insert-below",
56796                     "x-tree-drag-append"]);
56797             this.lastInsertClass = "_noclass";
56798         }
56799     },
56800
56801     // private
56802     beforeDragDrop : function(target, e, id){
56803         this.cancelExpand();
56804         return true;
56805     },
56806
56807     // private
56808     afterRepair : function(data){
56809         if(data && Ext.enableFx){
56810             data.node.ui.highlight();
56811         }
56812         this.hideProxy();
56813     }    
56814 });
56815
56816 }/**
56817  * @class Ext.tree.TreeDragZone
56818  * @extends Ext.dd.DragZone
56819  * @constructor
56820  * @param {String/HTMLElement/Element} tree The {@link Ext.tree.TreePanel} for which to enable dragging
56821  * @param {Object} config
56822  */
56823 if(Ext.dd.DragZone){
56824 Ext.tree.TreeDragZone = function(tree, config){
56825     Ext.tree.TreeDragZone.superclass.constructor.call(this, tree.innerCt, config);
56826     /**
56827     * The TreePanel for this drag zone
56828     * @type Ext.tree.TreePanel
56829     * @property
56830     */
56831     this.tree = tree;
56832 };
56833
56834 Ext.extend(Ext.tree.TreeDragZone, Ext.dd.DragZone, {
56835     /**
56836      * @cfg {String} ddGroup
56837      * A named drag drop group to which this object belongs.  If a group is specified, then this object will only
56838      * interact with other drag drop objects in the same group (defaults to 'TreeDD').
56839      */
56840     ddGroup : "TreeDD",
56841
56842     // private
56843     onBeforeDrag : function(data, e){
56844         var n = data.node;
56845         return n && n.draggable && !n.disabled;
56846     },
56847
56848     // private
56849     onInitDrag : function(e){
56850         var data = this.dragData;
56851         this.tree.getSelectionModel().select(data.node);
56852         this.tree.eventModel.disable();
56853         this.proxy.update("");
56854         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
56855         this.tree.fireEvent("startdrag", this.tree, data.node, e);
56856     },
56857
56858     // private
56859     getRepairXY : function(e, data){
56860         return data.node.ui.getDDRepairXY();
56861     },
56862
56863     // private
56864     onEndDrag : function(data, e){
56865         this.tree.eventModel.enable.defer(100, this.tree.eventModel);
56866         this.tree.fireEvent("enddrag", this.tree, data.node, e);
56867     },
56868
56869     // private
56870     onValidDrop : function(dd, e, id){
56871         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
56872         this.hideProxy();
56873     },
56874
56875     // private
56876     beforeInvalidDrop : function(e, id){
56877         // this scrolls the original position back into view
56878         var sm = this.tree.getSelectionModel();
56879         sm.clearSelections();
56880         sm.select(this.dragData.node);
56881     },
56882     
56883     // private
56884     afterRepair : function(){
56885         if (Ext.enableFx && this.tree.hlDrop) {
56886             Ext.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
56887         }
56888         this.dragging = false;
56889     }
56890 });
56891 }/**
56892  * @class Ext.tree.TreeEditor
56893  * @extends Ext.Editor
56894  * Provides editor functionality for inline tree node editing.  Any valid {@link Ext.form.Field} subclass can be used
56895  * as the editor field.
56896  * @constructor
56897  * @param {TreePanel} tree
56898  * @param {Object} fieldConfig (optional) Either a prebuilt {@link Ext.form.Field} instance or a Field config object
56899  * that will be applied to the default field instance (defaults to a {@link Ext.form.TextField}).
56900  * @param {Object} config (optional) A TreeEditor config object
56901  */
56902 Ext.tree.TreeEditor = function(tree, fc, config){
56903     fc = fc || {};
56904     var field = fc.events ? fc : new Ext.form.TextField(fc);
56905     
56906     Ext.tree.TreeEditor.superclass.constructor.call(this, field, config);
56907
56908     this.tree = tree;
56909
56910     if(!tree.rendered){
56911         tree.on('render', this.initEditor, this);
56912     }else{
56913         this.initEditor(tree);
56914     }
56915 };
56916
56917 Ext.extend(Ext.tree.TreeEditor, Ext.Editor, {
56918     /**
56919      * @cfg {String} alignment
56920      * The position to align to (see {@link Ext.Element#alignTo} for more details, defaults to "l-l").
56921      */
56922     alignment: "l-l",
56923     // inherit
56924     autoSize: false,
56925     /**
56926      * @cfg {Boolean} hideEl
56927      * True to hide the bound element while the editor is displayed (defaults to false)
56928      */
56929     hideEl : false,
56930     /**
56931      * @cfg {String} cls
56932      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
56933      */
56934     cls: "x-small-editor x-tree-editor",
56935     /**
56936      * @cfg {Boolean} shim
56937      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
56938      */
56939     shim:false,
56940     // inherit
56941     shadow:"frame",
56942     /**
56943      * @cfg {Number} maxWidth
56944      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
56945      * the containing tree element's size, it will be automatically limited for you to the container width, taking
56946      * scroll and client offsets into account prior to each edit.
56947      */
56948     maxWidth: 250,
56949     /**
56950      * @cfg {Number} editDelay The number of milliseconds between clicks to register a double-click that will trigger
56951      * editing on the current node (defaults to 350).  If two clicks occur on the same node within this time span,
56952      * the editor for the node will display, otherwise it will be processed as a regular click.
56953      */
56954     editDelay : 350,
56955
56956     initEditor : function(tree){
56957         tree.on({
56958             scope      : this,
56959             beforeclick: this.beforeNodeClick,
56960             dblclick   : this.onNodeDblClick
56961         });
56962         
56963         this.on({
56964             scope          : this,
56965             complete       : this.updateNode,
56966             beforestartedit: this.fitToTree,
56967             specialkey     : this.onSpecialKey
56968         });
56969         
56970         this.on('startedit', this.bindScroll, this, {delay:10});
56971     },
56972
56973     // private
56974     fitToTree : function(ed, el){
56975         var td = this.tree.getTreeEl().dom, nd = el.dom;
56976         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
56977             td.scrollLeft = nd.offsetLeft;
56978         }
56979         var w = Math.min(
56980                 this.maxWidth,
56981                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
56982         this.setSize(w, '');
56983     },
56984
56985     /**
56986      * Edit the text of the passed {@link Ext.tree.TreeNode TreeNode}.
56987      * @param node {Ext.tree.TreeNode} The TreeNode to edit. The TreeNode must be {@link Ext.tree.TreeNode#editable editable}.
56988      */
56989     triggerEdit : function(node, defer){
56990         this.completeEdit();
56991                 if(node.attributes.editable !== false){
56992            /**
56993             * The {@link Ext.tree.TreeNode TreeNode} this editor is bound to. Read-only.
56994             * @type Ext.tree.TreeNode
56995             * @property editNode
56996             */
56997                         this.editNode = node;
56998             if(this.tree.autoScroll){
56999                 Ext.fly(node.ui.getEl()).scrollIntoView(this.tree.body);
57000             }
57001             var value = node.text || '';
57002             if (!Ext.isGecko && Ext.isEmpty(node.text)){
57003                 node.setText('&#160;');
57004             }
57005             this.autoEditTimer = this.startEdit.defer(this.editDelay, this, [node.ui.textNode, value]);
57006             return false;
57007         }
57008     },
57009
57010     // private
57011     bindScroll : function(){
57012         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
57013     },
57014
57015     // private
57016     beforeNodeClick : function(node, e){
57017         clearTimeout(this.autoEditTimer);
57018         if(this.tree.getSelectionModel().isSelected(node)){
57019             e.stopEvent();
57020             return this.triggerEdit(node);
57021         }
57022     },
57023
57024     onNodeDblClick : function(node, e){
57025         clearTimeout(this.autoEditTimer);
57026     },
57027
57028     // private
57029     updateNode : function(ed, value){
57030         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
57031         this.editNode.setText(value);
57032     },
57033
57034     // private
57035     onHide : function(){
57036         Ext.tree.TreeEditor.superclass.onHide.call(this);
57037         if(this.editNode){
57038             this.editNode.ui.focus.defer(50, this.editNode.ui);
57039         }
57040     },
57041
57042     // private
57043     onSpecialKey : function(field, e){
57044         var k = e.getKey();
57045         if(k == e.ESC){
57046             e.stopEvent();
57047             this.cancelEdit();
57048         }else if(k == e.ENTER && !e.hasModifier()){
57049             e.stopEvent();
57050             this.completeEdit();
57051         }
57052     },
57053     
57054     onDestroy : function(){
57055         clearTimeout(this.autoEditTimer);
57056         Ext.tree.TreeEditor.superclass.onDestroy.call(this);
57057         var tree = this.tree;
57058         tree.un('beforeclick', this.beforeNodeClick, this);
57059         tree.un('dblclick', this.onNodeDblClick, this);
57060     }
57061 });/*! SWFObject v2.2 <http://code.google.com/p/swfobject/> 
57062     is released under the MIT License <http://www.opensource.org/licenses/mit-license.php> 
57063 */
57064
57065 var swfobject = function() {
57066     
57067     var UNDEF = "undefined",
57068         OBJECT = "object",
57069         SHOCKWAVE_FLASH = "Shockwave Flash",
57070         SHOCKWAVE_FLASH_AX = "ShockwaveFlash.ShockwaveFlash",
57071         FLASH_MIME_TYPE = "application/x-shockwave-flash",
57072         EXPRESS_INSTALL_ID = "SWFObjectExprInst",
57073         ON_READY_STATE_CHANGE = "onreadystatechange",
57074         
57075         win = window,
57076         doc = document,
57077         nav = navigator,
57078         
57079         plugin = false,
57080         domLoadFnArr = [main],
57081         regObjArr = [],
57082         objIdArr = [],
57083         listenersArr = [],
57084         storedAltContent,
57085         storedAltContentId,
57086         storedCallbackFn,
57087         storedCallbackObj,
57088         isDomLoaded = false,
57089         isExpressInstallActive = false,
57090         dynamicStylesheet,
57091         dynamicStylesheetMedia,
57092         autoHideShow = true,
57093     
57094     /* Centralized function for browser feature detection
57095         - User agent string detection is only used when no good alternative is possible
57096         - Is executed directly for optimal performance
57097     */  
57098     ua = function() {
57099         var w3cdom = typeof doc.getElementById != UNDEF && typeof doc.getElementsByTagName != UNDEF && typeof doc.createElement != UNDEF,
57100             u = nav.userAgent.toLowerCase(),
57101             p = nav.platform.toLowerCase(),
57102             windows = p ? (/win/).test(p) : /win/.test(u),
57103             mac = p ? (/mac/).test(p) : /mac/.test(u),
57104             webkit = /webkit/.test(u) ? parseFloat(u.replace(/^.*webkit\/(\d+(\.\d+)?).*$/, "$1")) : false, // returns either the webkit version or false if not webkit
57105             ie = !+"\v1", // feature detection based on Andrea Giammarchi's solution: http://webreflection.blogspot.com/2009/01/32-bytes-to-know-if-your-browser-is-ie.html
57106             playerVersion = [0,0,0],
57107             d = null;
57108         if (typeof nav.plugins != UNDEF && typeof nav.plugins[SHOCKWAVE_FLASH] == OBJECT) {
57109             d = nav.plugins[SHOCKWAVE_FLASH].description;
57110             if (d && !(typeof nav.mimeTypes != UNDEF && nav.mimeTypes[FLASH_MIME_TYPE] && !nav.mimeTypes[FLASH_MIME_TYPE].enabledPlugin)) { // navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin indicates whether plug-ins are enabled or disabled in Safari 3+
57111                 plugin = true;
57112                 ie = false; // cascaded feature detection for Internet Explorer
57113                 d = d.replace(/^.*\s+(\S+\s+\S+$)/, "$1");
57114                 playerVersion[0] = parseInt(d.replace(/^(.*)\..*$/, "$1"), 10);
57115                 playerVersion[1] = parseInt(d.replace(/^.*\.(.*)\s.*$/, "$1"), 10);
57116                 playerVersion[2] = /[a-zA-Z]/.test(d) ? parseInt(d.replace(/^.*[a-zA-Z]+(.*)$/, "$1"), 10) : 0;
57117             }
57118         }
57119         else if (typeof win.ActiveXObject != UNDEF) {
57120             try {
57121                 var a = new ActiveXObject(SHOCKWAVE_FLASH_AX);
57122                 if (a) { // a will return null when ActiveX is disabled
57123                     d = a.GetVariable("$version");
57124                     if (d) {
57125                         ie = true; // cascaded feature detection for Internet Explorer
57126                         d = d.split(" ")[1].split(",");
57127                         playerVersion = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];
57128                     }
57129                 }
57130             }
57131             catch(e) {}
57132         }
57133         return { w3:w3cdom, pv:playerVersion, wk:webkit, ie:ie, win:windows, mac:mac };
57134     }(),
57135     
57136     /* Cross-browser onDomLoad
57137         - Will fire an event as soon as the DOM of a web page is loaded
57138         - Internet Explorer workaround based on Diego Perini's solution: http://javascript.nwbox.com/IEContentLoaded/
57139         - Regular onload serves as fallback
57140     */ 
57141     onDomLoad = function() {
57142         if (!ua.w3) { return; }
57143         if ((typeof doc.readyState != UNDEF && doc.readyState == "complete") || (typeof doc.readyState == UNDEF && (doc.getElementsByTagName("body")[0] || doc.body))) { // function is fired after onload, e.g. when script is inserted dynamically 
57144             callDomLoadFunctions();
57145         }
57146         if (!isDomLoaded) {
57147             if (typeof doc.addEventListener != UNDEF) {
57148                 doc.addEventListener("DOMContentLoaded", callDomLoadFunctions, false);
57149             }       
57150             if (ua.ie && ua.win) {
57151                 doc.attachEvent(ON_READY_STATE_CHANGE, function() {
57152                     if (doc.readyState == "complete") {
57153                         doc.detachEvent(ON_READY_STATE_CHANGE, arguments.callee);
57154                         callDomLoadFunctions();
57155                     }
57156                 });
57157                 if (win == top) { // if not inside an iframe
57158                     (function(){
57159                         if (isDomLoaded) { return; }
57160                         try {
57161                             doc.documentElement.doScroll("left");
57162                         }
57163                         catch(e) {
57164                             setTimeout(arguments.callee, 0);
57165                             return;
57166                         }
57167                         callDomLoadFunctions();
57168                     })();
57169                 }
57170             }
57171             if (ua.wk) {
57172                 (function(){
57173                     if (isDomLoaded) { return; }
57174                     if (!(/loaded|complete/).test(doc.readyState)) {
57175                         setTimeout(arguments.callee, 0);
57176                         return;
57177                     }
57178                     callDomLoadFunctions();
57179                 })();
57180             }
57181             addLoadEvent(callDomLoadFunctions);
57182         }
57183     }();
57184     
57185     function callDomLoadFunctions() {
57186         if (isDomLoaded) { return; }
57187         try { // test if we can really add/remove elements to/from the DOM; we don't want to fire it too early
57188             var t = doc.getElementsByTagName("body")[0].appendChild(createElement("span"));
57189             t.parentNode.removeChild(t);
57190         }
57191         catch (e) { return; }
57192         isDomLoaded = true;
57193         var dl = domLoadFnArr.length;
57194         for (var i = 0; i < dl; i++) {
57195             domLoadFnArr[i]();
57196         }
57197     }
57198     
57199     function addDomLoadEvent(fn) {
57200         if (isDomLoaded) {
57201             fn();
57202         }
57203         else { 
57204             domLoadFnArr[domLoadFnArr.length] = fn; // Array.push() is only available in IE5.5+
57205         }
57206     }
57207     
57208     /* Cross-browser onload
57209         - Based on James Edwards' solution: http://brothercake.com/site/resources/scripts/onload/
57210         - Will fire an event as soon as a web page including all of its assets are loaded 
57211      */
57212     function addLoadEvent(fn) {
57213         if (typeof win.addEventListener != UNDEF) {
57214             win.addEventListener("load", fn, false);
57215         }
57216         else if (typeof doc.addEventListener != UNDEF) {
57217             doc.addEventListener("load", fn, false);
57218         }
57219         else if (typeof win.attachEvent != UNDEF) {
57220             addListener(win, "onload", fn);
57221         }
57222         else if (typeof win.onload == "function") {
57223             var fnOld = win.onload;
57224             win.onload = function() {
57225                 fnOld();
57226                 fn();
57227             };
57228         }
57229         else {
57230             win.onload = fn;
57231         }
57232     }
57233     
57234     /* Main function
57235         - Will preferably execute onDomLoad, otherwise onload (as a fallback)
57236     */
57237     function main() { 
57238         if (plugin) {
57239             testPlayerVersion();
57240         }
57241         else {
57242             matchVersions();
57243         }
57244     }
57245     
57246     /* Detect the Flash Player version for non-Internet Explorer browsers
57247         - Detecting the plug-in version via the object element is more precise than using the plugins collection item's description:
57248           a. Both release and build numbers can be detected
57249           b. Avoid wrong descriptions by corrupt installers provided by Adobe
57250           c. Avoid wrong descriptions by multiple Flash Player entries in the plugin Array, caused by incorrect browser imports
57251         - Disadvantage of this method is that it depends on the availability of the DOM, while the plugins collection is immediately available
57252     */
57253     function testPlayerVersion() {
57254         var b = doc.getElementsByTagName("body")[0];
57255         var o = createElement(OBJECT);
57256         o.setAttribute("type", FLASH_MIME_TYPE);
57257         var t = b.appendChild(o);
57258         if (t) {
57259             var counter = 0;
57260             (function(){
57261                 if (typeof t.GetVariable != UNDEF) {
57262                     var d = t.GetVariable("$version");
57263                     if (d) {
57264                         d = d.split(" ")[1].split(",");
57265                         ua.pv = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];
57266                     }
57267                 }
57268                 else if (counter < 10) {
57269                     counter++;
57270                     setTimeout(arguments.callee, 10);
57271                     return;
57272                 }
57273                 b.removeChild(o);
57274                 t = null;
57275                 matchVersions();
57276             })();
57277         }
57278         else {
57279             matchVersions();
57280         }
57281     }
57282     
57283     /* Perform Flash Player and SWF version matching; static publishing only
57284     */
57285     function matchVersions() {
57286         var rl = regObjArr.length;
57287         if (rl > 0) {
57288             for (var i = 0; i < rl; i++) { // for each registered object element
57289                 var id = regObjArr[i].id;
57290                 var cb = regObjArr[i].callbackFn;
57291                 var cbObj = {success:false, id:id};
57292                 if (ua.pv[0] > 0) {
57293                     var obj = getElementById(id);
57294                     if (obj) {
57295                         if (hasPlayerVersion(regObjArr[i].swfVersion) && !(ua.wk && ua.wk < 312)) { // Flash Player version >= published SWF version: Houston, we have a match!
57296                             setVisibility(id, true);
57297                             if (cb) {
57298                                 cbObj.success = true;
57299                                 cbObj.ref = getObjectById(id);
57300                                 cb(cbObj);
57301                             }
57302                         }
57303                         else if (regObjArr[i].expressInstall && canExpressInstall()) { // show the Adobe Express Install dialog if set by the web page author and if supported
57304                             var att = {};
57305                             att.data = regObjArr[i].expressInstall;
57306                             att.width = obj.getAttribute("width") || "0";
57307                             att.height = obj.getAttribute("height") || "0";
57308                             if (obj.getAttribute("class")) { att.styleclass = obj.getAttribute("class"); }
57309                             if (obj.getAttribute("align")) { att.align = obj.getAttribute("align"); }
57310                             // parse HTML object param element's name-value pairs
57311                             var par = {};
57312                             var p = obj.getElementsByTagName("param");
57313                             var pl = p.length;
57314                             for (var j = 0; j < pl; j++) {
57315                                 if (p[j].getAttribute("name").toLowerCase() != "movie") {
57316                                     par[p[j].getAttribute("name")] = p[j].getAttribute("value");
57317                                 }
57318                             }
57319                             showExpressInstall(att, par, id, cb);
57320                         }
57321                         else { // Flash Player and SWF version mismatch or an older Webkit engine that ignores the HTML object element's nested param elements: display alternative content instead of SWF
57322                             displayAltContent(obj);
57323                             if (cb) { cb(cbObj); }
57324                         }
57325                     }
57326                 }
57327                 else {  // if no Flash Player is installed or the fp version cannot be detected we let the HTML object element do its job (either show a SWF or alternative content)
57328                     setVisibility(id, true);
57329                     if (cb) {
57330                         var o = getObjectById(id); // test whether there is an HTML object element or not
57331                         if (o && typeof o.SetVariable != UNDEF) { 
57332                             cbObj.success = true;
57333                             cbObj.ref = o;
57334                         }
57335                         cb(cbObj);
57336                     }
57337                 }
57338             }
57339         }
57340     }
57341     
57342     function getObjectById(objectIdStr) {
57343         var r = null;
57344         var o = getElementById(objectIdStr);
57345         if (o && o.nodeName == "OBJECT") {
57346             if (typeof o.SetVariable != UNDEF) {
57347                 r = o;
57348             }
57349             else {
57350                 var n = o.getElementsByTagName(OBJECT)[0];
57351                 if (n) {
57352                     r = n;
57353                 }
57354             }
57355         }
57356         return r;
57357     }
57358     
57359     /* Requirements for Adobe Express Install
57360         - only one instance can be active at a time
57361         - fp 6.0.65 or higher
57362         - Win/Mac OS only
57363         - no Webkit engines older than version 312
57364     */
57365     function canExpressInstall() {
57366         return !isExpressInstallActive && hasPlayerVersion("6.0.65") && (ua.win || ua.mac) && !(ua.wk && ua.wk < 312);
57367     }
57368     
57369     /* Show the Adobe Express Install dialog
57370         - Reference: http://www.adobe.com/cfusion/knowledgebase/index.cfm?id=6a253b75
57371     */
57372     function showExpressInstall(att, par, replaceElemIdStr, callbackFn) {
57373         isExpressInstallActive = true;
57374         storedCallbackFn = callbackFn || null;
57375         storedCallbackObj = {success:false, id:replaceElemIdStr};
57376         var obj = getElementById(replaceElemIdStr);
57377         if (obj) {
57378             if (obj.nodeName == "OBJECT") { // static publishing
57379                 storedAltContent = abstractAltContent(obj);
57380                 storedAltContentId = null;
57381             }
57382             else { // dynamic publishing
57383                 storedAltContent = obj;
57384                 storedAltContentId = replaceElemIdStr;
57385             }
57386             att.id = EXPRESS_INSTALL_ID;
57387             if (typeof att.width == UNDEF || (!(/%$/).test(att.width) && parseInt(att.width, 10) < 310)) {
57388                 att.width = "310";
57389             }
57390             
57391             if (typeof att.height == UNDEF || (!(/%$/).test(att.height) && parseInt(att.height, 10) < 137)) {
57392                 att.height = "137";
57393             }
57394             doc.title = doc.title.slice(0, 47) + " - Flash Player Installation";
57395             var pt = ua.ie && ua.win ? "ActiveX" : "PlugIn",
57396                 fv = "MMredirectURL=" + win.location.toString().replace(/&/g,"%26") + "&MMplayerType=" + pt + "&MMdoctitle=" + doc.title;
57397             if (typeof par.flashvars != UNDEF) {
57398                 par.flashvars += "&" + fv;
57399             }
57400             else {
57401                 par.flashvars = fv;
57402             }
57403             // IE only: when a SWF is loading (AND: not available in cache) wait for the readyState of the object element to become 4 before removing it,
57404             // because you cannot properly cancel a loading SWF file without breaking browser load references, also obj.onreadystatechange doesn't work
57405             if (ua.ie && ua.win && obj.readyState != 4) {
57406                 var newObj = createElement("div");
57407                 replaceElemIdStr += "SWFObjectNew";
57408                 newObj.setAttribute("id", replaceElemIdStr);
57409                 obj.parentNode.insertBefore(newObj, obj); // insert placeholder div that will be replaced by the object element that loads expressinstall.swf
57410                 obj.style.display = "none";
57411                 (function(){
57412                     if (obj.readyState == 4) {
57413                         obj.parentNode.removeChild(obj);
57414                     }
57415                     else {
57416                         setTimeout(arguments.callee, 10);
57417                     }
57418                 })();
57419             }
57420             createSWF(att, par, replaceElemIdStr);
57421         }
57422     }
57423     
57424     /* Functions to abstract and display alternative content
57425     */
57426     function displayAltContent(obj) {
57427         if (ua.ie && ua.win && obj.readyState != 4) {
57428             // IE only: when a SWF is loading (AND: not available in cache) wait for the readyState of the object element to become 4 before removing it,
57429             // because you cannot properly cancel a loading SWF file without breaking browser load references, also obj.onreadystatechange doesn't work
57430             var el = createElement("div");
57431             obj.parentNode.insertBefore(el, obj); // insert placeholder div that will be replaced by the alternative content
57432             el.parentNode.replaceChild(abstractAltContent(obj), el);
57433             obj.style.display = "none";
57434             (function(){
57435                 if (obj.readyState == 4) {
57436                     obj.parentNode.removeChild(obj);
57437                 }
57438                 else {
57439                     setTimeout(arguments.callee, 10);
57440                 }
57441             })();
57442         }
57443         else {
57444             obj.parentNode.replaceChild(abstractAltContent(obj), obj);
57445         }
57446     } 
57447
57448     function abstractAltContent(obj) {
57449         var ac = createElement("div");
57450         if (ua.win && ua.ie) {
57451             ac.innerHTML = obj.innerHTML;
57452         }
57453         else {
57454             var nestedObj = obj.getElementsByTagName(OBJECT)[0];
57455             if (nestedObj) {
57456                 var c = nestedObj.childNodes;
57457                 if (c) {
57458                     var cl = c.length;
57459                     for (var i = 0; i < cl; i++) {
57460                         if (!(c[i].nodeType == 1 && c[i].nodeName == "PARAM") && !(c[i].nodeType == 8)) {
57461                             ac.appendChild(c[i].cloneNode(true));
57462                         }
57463                     }
57464                 }
57465             }
57466         }
57467         return ac;
57468     }
57469     
57470     /* Cross-browser dynamic SWF creation
57471     */
57472     function createSWF(attObj, parObj, id) {
57473         var r, el = getElementById(id);
57474         if (ua.wk && ua.wk < 312) { return r; }
57475         if (el) {
57476             if (typeof attObj.id == UNDEF) { // if no 'id' is defined for the object element, it will inherit the 'id' from the alternative content
57477                 attObj.id = id;
57478             }
57479             if (ua.ie && ua.win) { // Internet Explorer + the HTML object element + W3C DOM methods do not combine: fall back to outerHTML
57480                 var att = "";
57481                 for (var i in attObj) {
57482                     if (attObj[i] != Object.prototype[i]) { // filter out prototype additions from other potential libraries
57483                         if (i.toLowerCase() == "data") {
57484                             parObj.movie = attObj[i];
57485                         }
57486                         else if (i.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword
57487                             att += ' class="' + attObj[i] + '"';
57488                         }
57489                         else if (i.toLowerCase() != "classid") {
57490                             att += ' ' + i + '="' + attObj[i] + '"';
57491                         }
57492                     }
57493                 }
57494                 var par = "";
57495                 for (var j in parObj) {
57496                     if (parObj[j] != Object.prototype[j]) { // filter out prototype additions from other potential libraries
57497                         par += '<param name="' + j + '" value="' + parObj[j] + '" />';
57498                     }
57499                 }
57500                 el.outerHTML = '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"' + att + '>' + par + '</object>';
57501                 objIdArr[objIdArr.length] = attObj.id; // stored to fix object 'leaks' on unload (dynamic publishing only)
57502                 r = getElementById(attObj.id);  
57503             }
57504             else { // well-behaving browsers
57505                 var o = createElement(OBJECT);
57506                 o.setAttribute("type", FLASH_MIME_TYPE);
57507                 for (var m in attObj) {
57508                     if (attObj[m] != Object.prototype[m]) { // filter out prototype additions from other potential libraries
57509                         if (m.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword
57510                             o.setAttribute("class", attObj[m]);
57511                         }
57512                         else if (m.toLowerCase() != "classid") { // filter out IE specific attribute
57513                             o.setAttribute(m, attObj[m]);
57514                         }
57515                     }
57516                 }
57517                 for (var n in parObj) {
57518                     if (parObj[n] != Object.prototype[n] && n.toLowerCase() != "movie") { // filter out prototype additions from other potential libraries and IE specific param element
57519                         createObjParam(o, n, parObj[n]);
57520                     }
57521                 }
57522                 el.parentNode.replaceChild(o, el);
57523                 r = o;
57524             }
57525         }
57526         return r;
57527     }
57528     
57529     function createObjParam(el, pName, pValue) {
57530         var p = createElement("param");
57531         p.setAttribute("name", pName);  
57532         p.setAttribute("value", pValue);
57533         el.appendChild(p);
57534     }
57535     
57536     /* Cross-browser SWF removal
57537         - Especially needed to safely and completely remove a SWF in Internet Explorer
57538     */
57539     function removeSWF(id) {
57540         var obj = getElementById(id);
57541         if (obj && obj.nodeName == "OBJECT") {
57542             if (ua.ie && ua.win) {
57543                 obj.style.display = "none";
57544                 (function(){
57545                     if (obj.readyState == 4) {
57546                         removeObjectInIE(id);
57547                     }
57548                     else {
57549                         setTimeout(arguments.callee, 10);
57550                     }
57551                 })();
57552             }
57553             else {
57554                 obj.parentNode.removeChild(obj);
57555             }
57556         }
57557     }
57558     
57559     function removeObjectInIE(id) {
57560         var obj = getElementById(id);
57561         if (obj) {
57562             for (var i in obj) {
57563                 if (typeof obj[i] == "function") {
57564                     obj[i] = null;
57565                 }
57566             }
57567             obj.parentNode.removeChild(obj);
57568         }
57569     }
57570     
57571     /* Functions to optimize JavaScript compression
57572     */
57573     function getElementById(id) {
57574         var el = null;
57575         try {
57576             el = doc.getElementById(id);
57577         }
57578         catch (e) {}
57579         return el;
57580     }
57581     
57582     function createElement(el) {
57583         return doc.createElement(el);
57584     }
57585     
57586     /* Updated attachEvent function for Internet Explorer
57587         - Stores attachEvent information in an Array, so on unload the detachEvent functions can be called to avoid memory leaks
57588     */  
57589     function addListener(target, eventType, fn) {
57590         target.attachEvent(eventType, fn);
57591         listenersArr[listenersArr.length] = [target, eventType, fn];
57592     }
57593     
57594     /* Flash Player and SWF content version matching
57595     */
57596     function hasPlayerVersion(rv) {
57597         var pv = ua.pv, v = rv.split(".");
57598         v[0] = parseInt(v[0], 10);
57599         v[1] = parseInt(v[1], 10) || 0; // supports short notation, e.g. "9" instead of "9.0.0"
57600         v[2] = parseInt(v[2], 10) || 0;
57601         return (pv[0] > v[0] || (pv[0] == v[0] && pv[1] > v[1]) || (pv[0] == v[0] && pv[1] == v[1] && pv[2] >= v[2])) ? true : false;
57602     }
57603     
57604     /* Cross-browser dynamic CSS creation
57605         - Based on Bobby van der Sluis' solution: http://www.bobbyvandersluis.com/articles/dynamicCSS.php
57606     */  
57607     function createCSS(sel, decl, media, newStyle) {
57608         if (ua.ie && ua.mac) { return; }
57609         var h = doc.getElementsByTagName("head")[0];
57610         if (!h) { return; } // to also support badly authored HTML pages that lack a head element
57611         var m = (media && typeof media == "string") ? media : "screen";
57612         if (newStyle) {
57613             dynamicStylesheet = null;
57614             dynamicStylesheetMedia = null;
57615         }
57616         if (!dynamicStylesheet || dynamicStylesheetMedia != m) { 
57617             // create dynamic stylesheet + get a global reference to it
57618             var s = createElement("style");
57619             s.setAttribute("type", "text/css");
57620             s.setAttribute("media", m);
57621             dynamicStylesheet = h.appendChild(s);
57622             if (ua.ie && ua.win && typeof doc.styleSheets != UNDEF && doc.styleSheets.length > 0) {
57623                 dynamicStylesheet = doc.styleSheets[doc.styleSheets.length - 1];
57624             }
57625             dynamicStylesheetMedia = m;
57626         }
57627         // add style rule
57628         if (ua.ie && ua.win) {
57629             if (dynamicStylesheet && typeof dynamicStylesheet.addRule == OBJECT) {
57630                 dynamicStylesheet.addRule(sel, decl);
57631             }
57632         }
57633         else {
57634             if (dynamicStylesheet && typeof doc.createTextNode != UNDEF) {
57635                 dynamicStylesheet.appendChild(doc.createTextNode(sel + " {" + decl + "}"));
57636             }
57637         }
57638     }
57639     
57640     function setVisibility(id, isVisible) {
57641         if (!autoHideShow) { return; }
57642         var v = isVisible ? "visible" : "hidden";
57643         if (isDomLoaded && getElementById(id)) {
57644             getElementById(id).style.visibility = v;
57645         }
57646         else {
57647             createCSS("#" + id, "visibility:" + v);
57648         }
57649     }
57650
57651     /* Filter to avoid XSS attacks
57652     */
57653     function urlEncodeIfNecessary(s) {
57654         var regex = /[\\\"<>\.;]/;
57655         var hasBadChars = regex.exec(s) != null;
57656         return hasBadChars && typeof encodeURIComponent != UNDEF ? encodeURIComponent(s) : s;
57657     }
57658     
57659     /* Release memory to avoid memory leaks caused by closures, fix hanging audio/video threads and force open sockets/NetConnections to disconnect (Internet Explorer only)
57660     */
57661     var cleanup = function() {
57662         if (ua.ie && ua.win) {
57663             window.attachEvent("onunload", function() {
57664                 // remove listeners to avoid memory leaks
57665                 var ll = listenersArr.length;
57666                 for (var i = 0; i < ll; i++) {
57667                     listenersArr[i][0].detachEvent(listenersArr[i][1], listenersArr[i][2]);
57668                 }
57669                 // cleanup dynamically embedded objects to fix audio/video threads and force open sockets and NetConnections to disconnect
57670                 var il = objIdArr.length;
57671                 for (var j = 0; j < il; j++) {
57672                     removeSWF(objIdArr[j]);
57673                 }
57674                 // cleanup library's main closures to avoid memory leaks
57675                 for (var k in ua) {
57676                     ua[k] = null;
57677                 }
57678                 ua = null;
57679                 for (var l in swfobject) {
57680                     swfobject[l] = null;
57681                 }
57682                 swfobject = null;
57683             });
57684         }
57685     }();
57686     
57687     return {
57688         /* Public API
57689             - Reference: http://code.google.com/p/swfobject/wiki/documentation
57690         */ 
57691         registerObject: function(objectIdStr, swfVersionStr, xiSwfUrlStr, callbackFn) {
57692             if (ua.w3 && objectIdStr && swfVersionStr) {
57693                 var regObj = {};
57694                 regObj.id = objectIdStr;
57695                 regObj.swfVersion = swfVersionStr;
57696                 regObj.expressInstall = xiSwfUrlStr;
57697                 regObj.callbackFn = callbackFn;
57698                 regObjArr[regObjArr.length] = regObj;
57699                 setVisibility(objectIdStr, false);
57700             }
57701             else if (callbackFn) {
57702                 callbackFn({success:false, id:objectIdStr});
57703             }
57704         },
57705         
57706         getObjectById: function(objectIdStr) {
57707             if (ua.w3) {
57708                 return getObjectById(objectIdStr);
57709             }
57710         },
57711         
57712         embedSWF: function(swfUrlStr, replaceElemIdStr, widthStr, heightStr, swfVersionStr, xiSwfUrlStr, flashvarsObj, parObj, attObj, callbackFn) {
57713             var callbackObj = {success:false, id:replaceElemIdStr};
57714             if (ua.w3 && !(ua.wk && ua.wk < 312) && swfUrlStr && replaceElemIdStr && widthStr && heightStr && swfVersionStr) {
57715                 setVisibility(replaceElemIdStr, false);
57716                 addDomLoadEvent(function() {
57717                     widthStr += ""; // auto-convert to string
57718                     heightStr += "";
57719                     var att = {};
57720                     if (attObj && typeof attObj === OBJECT) {
57721                         for (var i in attObj) { // copy object to avoid the use of references, because web authors often reuse attObj for multiple SWFs
57722                             att[i] = attObj[i];
57723                         }
57724                     }
57725                     att.data = swfUrlStr;
57726                     att.width = widthStr;
57727                     att.height = heightStr;
57728                     var par = {}; 
57729                     if (parObj && typeof parObj === OBJECT) {
57730                         for (var j in parObj) { // copy object to avoid the use of references, because web authors often reuse parObj for multiple SWFs
57731                             par[j] = parObj[j];
57732                         }
57733                     }
57734                     if (flashvarsObj && typeof flashvarsObj === OBJECT) {
57735                         for (var k in flashvarsObj) { // copy object to avoid the use of references, because web authors often reuse flashvarsObj for multiple SWFs
57736                             if (typeof par.flashvars != UNDEF) {
57737                                 par.flashvars += "&" + k + "=" + flashvarsObj[k];
57738                             }
57739                             else {
57740                                 par.flashvars = k + "=" + flashvarsObj[k];
57741                             }
57742                         }
57743                     }
57744                     if (hasPlayerVersion(swfVersionStr)) { // create SWF
57745                         var obj = createSWF(att, par, replaceElemIdStr);
57746                         if (att.id == replaceElemIdStr) {
57747                             setVisibility(replaceElemIdStr, true);
57748                         }
57749                         callbackObj.success = true;
57750                         callbackObj.ref = obj;
57751                     }
57752                     else if (xiSwfUrlStr && canExpressInstall()) { // show Adobe Express Install
57753                         att.data = xiSwfUrlStr;
57754                         showExpressInstall(att, par, replaceElemIdStr, callbackFn);
57755                         return;
57756                     }
57757                     else { // show alternative content
57758                         setVisibility(replaceElemIdStr, true);
57759                     }
57760                     if (callbackFn) { callbackFn(callbackObj); }
57761                 });
57762             }
57763             else if (callbackFn) { callbackFn(callbackObj); }
57764         },
57765         
57766         switchOffAutoHideShow: function() {
57767             autoHideShow = false;
57768         },
57769         
57770         ua: ua,
57771         
57772         getFlashPlayerVersion: function() {
57773             return { major:ua.pv[0], minor:ua.pv[1], release:ua.pv[2] };
57774         },
57775         
57776         hasFlashPlayerVersion: hasPlayerVersion,
57777         
57778         createSWF: function(attObj, parObj, replaceElemIdStr) {
57779             if (ua.w3) {
57780                 return createSWF(attObj, parObj, replaceElemIdStr);
57781             }
57782             else {
57783                 return undefined;
57784             }
57785         },
57786         
57787         showExpressInstall: function(att, par, replaceElemIdStr, callbackFn) {
57788             if (ua.w3 && canExpressInstall()) {
57789                 showExpressInstall(att, par, replaceElemIdStr, callbackFn);
57790             }
57791         },
57792         
57793         removeSWF: function(objElemIdStr) {
57794             if (ua.w3) {
57795                 removeSWF(objElemIdStr);
57796             }
57797         },
57798         
57799         createCSS: function(selStr, declStr, mediaStr, newStyleBoolean) {
57800             if (ua.w3) {
57801                 createCSS(selStr, declStr, mediaStr, newStyleBoolean);
57802             }
57803         },
57804         
57805         addDomLoadEvent: addDomLoadEvent,
57806         
57807         addLoadEvent: addLoadEvent,
57808         
57809         getQueryParamValue: function(param) {
57810             var q = doc.location.search || doc.location.hash;
57811             if (q) {
57812                 if (/\?/.test(q)) { q = q.split("?")[1]; } // strip question mark
57813                 if (param == null) {
57814                     return urlEncodeIfNecessary(q);
57815                 }
57816                 var pairs = q.split("&");
57817                 for (var i = 0; i < pairs.length; i++) {
57818                     if (pairs[i].substring(0, pairs[i].indexOf("=")) == param) {
57819                         return urlEncodeIfNecessary(pairs[i].substring((pairs[i].indexOf("=") + 1)));
57820                     }
57821                 }
57822             }
57823             return "";
57824         },
57825         
57826         // For internal usage only
57827         expressInstallCallback: function() {
57828             if (isExpressInstallActive) {
57829                 var obj = getElementById(EXPRESS_INSTALL_ID);
57830                 if (obj && storedAltContent) {
57831                     obj.parentNode.replaceChild(storedAltContent, obj);
57832                     if (storedAltContentId) {
57833                         setVisibility(storedAltContentId, true);
57834                         if (ua.ie && ua.win) { storedAltContent.style.display = "block"; }
57835                     }
57836                     if (storedCallbackFn) { storedCallbackFn(storedCallbackObj); }
57837                 }
57838                 isExpressInstallActive = false;
57839             } 
57840         }
57841     };
57842 }();
57843 /**
57844  * @class Ext.FlashComponent
57845  * @extends Ext.BoxComponent
57846  * @constructor
57847  * @xtype flash
57848  */
57849 Ext.FlashComponent = Ext.extend(Ext.BoxComponent, {
57850     /**
57851      * @cfg {String} flashVersion
57852      * Indicates the version the flash content was published for. Defaults to <tt>'9.0.115'</tt>.
57853      */
57854     flashVersion : '9.0.115',
57855
57856     /**
57857      * @cfg {String} backgroundColor
57858      * The background color of the chart. Defaults to <tt>'#ffffff'</tt>.
57859      */
57860     backgroundColor: '#ffffff',
57861
57862     /**
57863      * @cfg {String} wmode
57864      * The wmode of the flash object. This can be used to control layering. Defaults to <tt>'opaque'</tt>.
57865      */
57866     wmode: 'opaque',
57867
57868     /**
57869      * @cfg {Object} flashVars
57870      * A set of key value pairs to be passed to the flash object as flash variables. Defaults to <tt>undefined</tt>.
57871      */
57872     flashVars: undefined,
57873
57874     /**
57875      * @cfg {Object} flashParams
57876      * A set of key value pairs to be passed to the flash object as parameters. Possible parameters can be found here:
57877      * http://kb2.adobe.com/cps/127/tn_12701.html Defaults to <tt>undefined</tt>.
57878      */
57879     flashParams: undefined,
57880
57881     /**
57882      * @cfg {String} url
57883      * The URL of the chart to include. Defaults to <tt>undefined</tt>.
57884      */
57885     url: undefined,
57886     swfId : undefined,
57887     swfWidth: '100%',
57888     swfHeight: '100%',
57889
57890     /**
57891      * @cfg {Boolean} expressInstall
57892      * True to prompt the user to install flash if not installed. Note that this uses
57893      * Ext.FlashComponent.EXPRESS_INSTALL_URL, which should be set to the local resource. Defaults to <tt>false</tt>.
57894      */
57895     expressInstall: false,
57896
57897     initComponent : function(){
57898         Ext.FlashComponent.superclass.initComponent.call(this);
57899
57900         this.addEvents(
57901             /**
57902              * @event initialize
57903              *
57904              * @param {Chart} this
57905              */
57906             'initialize'
57907         );
57908     },
57909
57910     onRender : function(){
57911         Ext.FlashComponent.superclass.onRender.apply(this, arguments);
57912
57913         var params = Ext.apply({
57914             allowScriptAccess: 'always',
57915             bgcolor: this.backgroundColor,
57916             wmode: this.wmode
57917         }, this.flashParams), vars = Ext.apply({
57918             allowedDomain: document.location.hostname,
57919             YUISwfId: this.getId(),
57920             YUIBridgeCallback: 'Ext.FlashEventProxy.onEvent'
57921         }, this.flashVars);
57922
57923         new swfobject.embedSWF(this.url, this.id, this.swfWidth, this.swfHeight, this.flashVersion,
57924             this.expressInstall ? Ext.FlashComponent.EXPRESS_INSTALL_URL : undefined, vars, params);
57925
57926         this.swf = Ext.getDom(this.id);
57927         this.el = Ext.get(this.swf);
57928     },
57929
57930     getSwfId : function(){
57931         return this.swfId || (this.swfId = "extswf" + (++Ext.Component.AUTO_ID));
57932     },
57933
57934     getId : function(){
57935         return this.id || (this.id = "extflashcmp" + (++Ext.Component.AUTO_ID));
57936     },
57937
57938     onFlashEvent : function(e){
57939         switch(e.type){
57940             case "swfReady":
57941                 this.initSwf();
57942                 return;
57943             case "log":
57944                 return;
57945         }
57946         e.component = this;
57947         this.fireEvent(e.type.toLowerCase().replace(/event$/, ''), e);
57948     },
57949
57950     initSwf : function(){
57951         this.onSwfReady(!!this.isInitialized);
57952         this.isInitialized = true;
57953         this.fireEvent('initialize', this);
57954     },
57955
57956     beforeDestroy: function(){
57957         if(this.rendered){
57958             swfobject.removeSWF(this.swf.id);
57959         }
57960         Ext.FlashComponent.superclass.beforeDestroy.call(this);
57961     },
57962
57963     onSwfReady : Ext.emptyFn
57964 });
57965
57966 /**
57967  * Sets the url for installing flash if it doesn't exist. This should be set to a local resource.
57968  * @static
57969  * @type String
57970  */
57971 Ext.FlashComponent.EXPRESS_INSTALL_URL = 'http:/' + '/swfobject.googlecode.com/svn/trunk/swfobject/expressInstall.swf';
57972
57973 Ext.reg('flash', Ext.FlashComponent);/**
57974  * @class Ext.FlashProxy
57975  * @singleton
57976  */
57977 Ext.FlashEventProxy = {
57978     onEvent : function(id, e){
57979         var fp = Ext.getCmp(id);
57980         if(fp){
57981             fp.onFlashEvent(e);
57982         }else{
57983             arguments.callee.defer(10, this, [id, e]);
57984         }
57985     }
57986 };/**
57987  * @class Ext.chart.Chart
57988  * @extends Ext.FlashComponent
57989  * The Ext.chart package provides the capability to visualize data with flash based charting.
57990  * Each chart binds directly to an Ext.data.Store enabling automatic updates of the chart.
57991  * To change the look and feel of a chart, see the {@link #chartStyle} and {@link #extraStyle} config options.
57992  * @constructor
57993  * @xtype chart
57994  */
57995
57996  Ext.chart.Chart = Ext.extend(Ext.FlashComponent, {
57997     refreshBuffer: 100,
57998
57999     /**
58000      * @cfg {String} backgroundColor
58001      * @hide
58002      */
58003
58004     /**
58005      * @cfg {Object} chartStyle
58006      * Sets styles for this chart. This contains default styling, so modifying this property will <b>override</b>
58007      * the built in styles of the chart. Use {@link #extraStyle} to add customizations to the default styling.
58008      */
58009     chartStyle: {
58010         padding: 10,
58011         animationEnabled: true,
58012         font: {
58013             name: 'Tahoma',
58014             color: 0x444444,
58015             size: 11
58016         },
58017         dataTip: {
58018             padding: 5,
58019             border: {
58020                 color: 0x99bbe8,
58021                 size:1
58022             },
58023             background: {
58024                 color: 0xDAE7F6,
58025                 alpha: .9
58026             },
58027             font: {
58028                 name: 'Tahoma',
58029                 color: 0x15428B,
58030                 size: 10,
58031                 bold: true
58032             }
58033         }
58034     },
58035
58036     /**
58037      * @cfg {String} url
58038      * The url to load the chart from. This defaults to Ext.chart.Chart.CHART_URL, which should
58039      * be modified to point to the local charts resource.
58040      */
58041
58042     /**
58043      * @cfg {Object} extraStyle
58044      * Contains extra styles that will be added or overwritten to the default chartStyle. Defaults to <tt>null</tt>.
58045      * For a detailed list of the options available, visit the YUI Charts site
58046      * at <a href="http://developer.yahoo.com/yui/charts/#basicstyles">http://developer.yahoo.com/yui/charts/#basicstyles</a><br/>
58047      * Some of the options availabe:<br />
58048      * <ul style="padding:5px;padding-left:16px;list-style-type:inherit;">
58049      * <li><b>padding</b> - The space around the edge of the chart's contents. Padding does not increase the size of the chart.</li>
58050      * <li><b>animationEnabled</b> - A Boolean value that specifies whether marker animations are enabled or not. Enabled by default.</li>
58051      * <li><b>font</b> - An Object defining the font style to be used in the chart. Defaults to <tt>{ name: 'Tahoma', color: 0x444444, size: 11 }</tt><br/>
58052      *  <ul style="padding:5px;padding-left:26px;list-style-type:circle;">
58053      *      <li><b>name</b> - font name</li>
58054      *      <li><b>color</b> - font color (hex code, ie: "#ff0000", "ff0000" or 0xff0000)</li>
58055      *      <li><b>size</b> - font size in points (numeric portion only, ie: 11)</li>
58056      *      <li><b>bold</b> - boolean</li>
58057      *      <li><b>italic</b> - boolean</li>
58058      *      <li><b>underline</b> - boolean</li>
58059      *  </ul>
58060      * </li>
58061      * <li><b>border</b> - An object defining the border style around the chart. The chart itself will decrease in dimensions to accomodate the border.<br/>
58062      *  <ul style="padding:5px;padding-left:26px;list-style-type:circle;">
58063      *      <li><b>color</b> - border color (hex code, ie: "#ff0000", "ff0000" or 0xff0000)</li>
58064      *      <li><b>size</b> - border size in pixels (numeric portion only, ie: 1)</li>
58065      *  </ul>
58066      * </li>
58067      * <li><b>background</b> - An object defining the background style of the chart.<br/>
58068      *  <ul style="padding:5px;padding-left:26px;list-style-type:circle;">
58069      *      <li><b>color</b> - border color (hex code, ie: "#ff0000", "ff0000" or 0xff0000)</li>
58070      *      <li><b>image</b> - an image URL. May be relative to the current document or absolute.</li>
58071      *  </ul>
58072      * </li>
58073      * <li><b>legend</b> - An object defining the legend style<br/>
58074      *  <ul style="padding:5px;padding-left:26px;list-style-type:circle;">
58075      *      <li><b>display</b> - location of the legend. Possible values are "none", "left", "right", "top", and "bottom".</li>
58076      *      <li><b>spacing</b> - an image URL. May be relative to the current document or absolute.</li>
58077      *      <li><b>padding, border, background, font</b> - same options as described above.</li>
58078      *  </ul></li>
58079      * <li><b>dataTip</b> - An object defining the style of the data tip (tooltip).<br/>
58080      *  <ul style="padding:5px;padding-left:26px;list-style-type:circle;">
58081      *      <li><b>padding, border, background, font</b> - same options as described above.</li>
58082      *  </ul></li>
58083      * <li><b>xAxis and yAxis</b> - An object defining the style of the style of either axis.<br/>
58084      *  <ul style="padding:5px;padding-left:26px;list-style-type:circle;">
58085      *      <li><b>color</b> - same option as described above.</li>
58086      *      <li><b>size</b> - same option as described above.</li>
58087      *      <li><b>showLabels</b> - boolean</li>
58088      *      <li><b>labelRotation</b> - a value in degrees from -90 through 90. Default is zero.</li>
58089      *  </ul></li>
58090      * <li><b>majorGridLines and minorGridLines</b> - An object defining the style of the style of the grid lines.<br/>
58091      *  <ul style="padding:5px;padding-left:26px;list-style-type:circle;">
58092      *      <li><b>color, size</b> - same options as described above.</li>
58093      *  </ul></li></li>
58094      * <li><b>zeroGridLine</b> - An object defining the style of the style of the zero grid line.<br/>
58095      *  <ul style="padding:5px;padding-left:26px;list-style-type:circle;">
58096      *      <li><b>color, size</b> - same options as described above.</li>
58097      *  </ul></li></li>
58098      * <li><b>majorTicks and minorTicks</b> - An object defining the style of the style of ticks in the chart.<br/>
58099      *  <ul style="padding:5px;padding-left:26px;list-style-type:circle;">
58100      *      <li><b>color, size</b> - same options as described above.</li>
58101      *      <li><b>length</b> - the length of each tick in pixels extending from the axis.</li>
58102      *      <li><b>display</b> - how the ticks are drawn. Possible values are "none", "inside", "outside", and "cross".</li>
58103      *  </ul></li></li>
58104      * </ul>
58105      */
58106     extraStyle: null,
58107
58108     /**
58109      * @cfg {Object} seriesStyles
58110      * Contains styles to apply to the series after a refresh. Defaults to <tt>null</tt>.
58111      */
58112     seriesStyles: null,
58113
58114     /**
58115      * @cfg {Boolean} disableCaching
58116      * True to add a "cache buster" to the end of the chart url. Defaults to true for Opera and IE.
58117      */
58118     disableCaching: Ext.isIE || Ext.isOpera,
58119     disableCacheParam: '_dc',
58120
58121     initComponent : function(){
58122         Ext.chart.Chart.superclass.initComponent.call(this);
58123         if(!this.url){
58124             this.url = Ext.chart.Chart.CHART_URL;
58125         }
58126         if(this.disableCaching){
58127             this.url = Ext.urlAppend(this.url, String.format('{0}={1}', this.disableCacheParam, new Date().getTime()));
58128         }
58129         this.addEvents(
58130             'itemmouseover',
58131             'itemmouseout',
58132             'itemclick',
58133             'itemdoubleclick',
58134             'itemdragstart',
58135             'itemdrag',
58136             'itemdragend',
58137             /**
58138              * @event beforerefresh
58139              * Fires before a refresh to the chart data is called.  If the beforerefresh handler returns
58140              * <tt>false</tt> the {@link #refresh} action will be cancelled.
58141              * @param {Chart} this
58142              */
58143             'beforerefresh',
58144             /**
58145              * @event refresh
58146              * Fires after the chart data has been refreshed.
58147              * @param {Chart} this
58148              */
58149             'refresh'
58150         );
58151         this.store = Ext.StoreMgr.lookup(this.store);
58152     },
58153
58154     /**
58155      * Sets a single style value on the Chart instance.
58156      *
58157      * @param name {String} Name of the Chart style value to change.
58158      * @param value {Object} New value to pass to the Chart style.
58159      */
58160      setStyle: function(name, value){
58161          this.swf.setStyle(name, Ext.encode(value));
58162      },
58163
58164     /**
58165      * Resets all styles on the Chart instance.
58166      *
58167      * @param styles {Object} Initializer for all Chart styles.
58168      */
58169     setStyles: function(styles){
58170         this.swf.setStyles(Ext.encode(styles));
58171     },
58172
58173     /**
58174      * Sets the styles on all series in the Chart.
58175      *
58176      * @param styles {Array} Initializer for all Chart series styles.
58177      */
58178     setSeriesStyles: function(styles){
58179         this.seriesStyles = styles;
58180         var s = [];
58181         Ext.each(styles, function(style){
58182             s.push(Ext.encode(style));
58183         });
58184         this.swf.setSeriesStyles(s);
58185     },
58186
58187     setCategoryNames : function(names){
58188         this.swf.setCategoryNames(names);
58189     },
58190
58191     setLegendRenderer : function(fn, scope){
58192         var chart = this;
58193         scope = scope || chart;
58194         chart.removeFnProxy(chart.legendFnName);
58195         chart.legendFnName = chart.createFnProxy(function(name){
58196             return fn.call(scope, name);
58197         });
58198         chart.swf.setLegendLabelFunction(chart.legendFnName);
58199     },
58200
58201     setTipRenderer : function(fn, scope){
58202         var chart = this;
58203         scope = scope || chart;
58204         chart.removeFnProxy(chart.tipFnName);
58205         chart.tipFnName = chart.createFnProxy(function(item, index, series){
58206             var record = chart.store.getAt(index);
58207             return fn.call(scope, chart, record, index, series);
58208         });
58209         chart.swf.setDataTipFunction(chart.tipFnName);
58210     },
58211
58212     setSeries : function(series){
58213         this.series = series;
58214         this.refresh();
58215     },
58216
58217     /**
58218      * Changes the data store bound to this chart and refreshes it.
58219      * @param {Store} store The store to bind to this chart
58220      */
58221     bindStore : function(store, initial){
58222         if(!initial && this.store){
58223             if(store !== this.store && this.store.autoDestroy){
58224                 this.store.destroy();
58225             }else{
58226                 this.store.un("datachanged", this.refresh, this);
58227                 this.store.un("add", this.delayRefresh, this);
58228                 this.store.un("remove", this.delayRefresh, this);
58229                 this.store.un("update", this.delayRefresh, this);
58230                 this.store.un("clear", this.refresh, this);
58231             }
58232         }
58233         if(store){
58234             store = Ext.StoreMgr.lookup(store);
58235             store.on({
58236                 scope: this,
58237                 datachanged: this.refresh,
58238                 add: this.delayRefresh,
58239                 remove: this.delayRefresh,
58240                 update: this.delayRefresh,
58241                 clear: this.refresh
58242             });
58243         }
58244         this.store = store;
58245         if(store && !initial){
58246             this.refresh();
58247         }
58248     },
58249
58250     onSwfReady : function(isReset){
58251         Ext.chart.Chart.superclass.onSwfReady.call(this, isReset);
58252         var ref;
58253         this.swf.setType(this.type);
58254
58255         if(this.chartStyle){
58256             this.setStyles(Ext.apply({}, this.extraStyle, this.chartStyle));
58257         }
58258
58259         if(this.categoryNames){
58260             this.setCategoryNames(this.categoryNames);
58261         }
58262
58263         if(this.tipRenderer){
58264             ref = this.getFunctionRef(this.tipRenderer);
58265             this.setTipRenderer(ref.fn, ref.scope);
58266         }
58267         if(this.legendRenderer){
58268             ref = this.getFunctionRef(this.legendRenderer);
58269             this.setLegendRenderer(ref.fn, ref.scope);
58270         }
58271         if(!isReset){
58272             this.bindStore(this.store, true);
58273         }
58274         this.refresh.defer(10, this);
58275     },
58276
58277     delayRefresh : function(){
58278         if(!this.refreshTask){
58279             this.refreshTask = new Ext.util.DelayedTask(this.refresh, this);
58280         }
58281         this.refreshTask.delay(this.refreshBuffer);
58282     },
58283
58284     refresh : function(){
58285         if(this.fireEvent('beforerefresh', this) !== false){
58286             var styleChanged = false;
58287             // convert the store data into something YUI charts can understand
58288             var data = [], rs = this.store.data.items;
58289             for(var j = 0, len = rs.length; j < len; j++){
58290                 data[j] = rs[j].data;
58291             }
58292             //make a copy of the series definitions so that we aren't
58293             //editing them directly.
58294             var dataProvider = [];
58295             var seriesCount = 0;
58296             var currentSeries = null;
58297             var i = 0;
58298             if(this.series){
58299                 seriesCount = this.series.length;
58300                 for(i = 0; i < seriesCount; i++){
58301                     currentSeries = this.series[i];
58302                     var clonedSeries = {};
58303                     for(var prop in currentSeries){
58304                         if(prop == "style" && currentSeries.style !== null){
58305                             clonedSeries.style = Ext.encode(currentSeries.style);
58306                             styleChanged = true;
58307                             //we don't want to modify the styles again next time
58308                             //so null out the style property.
58309                             // this causes issues
58310                             // currentSeries.style = null;
58311                         } else{
58312                             clonedSeries[prop] = currentSeries[prop];
58313                         }
58314                     }
58315                     dataProvider.push(clonedSeries);
58316                 }
58317             }
58318
58319             if(seriesCount > 0){
58320                 for(i = 0; i < seriesCount; i++){
58321                     currentSeries = dataProvider[i];
58322                     if(!currentSeries.type){
58323                         currentSeries.type = this.type;
58324                     }
58325                     currentSeries.dataProvider = data;
58326                 }
58327             } else{
58328                 dataProvider.push({type: this.type, dataProvider: data});
58329             }
58330             this.swf.setDataProvider(dataProvider);
58331             if(this.seriesStyles){
58332                 this.setSeriesStyles(this.seriesStyles);
58333             }
58334             this.fireEvent('refresh', this);
58335         }
58336     },
58337
58338     // private
58339     createFnProxy : function(fn){
58340         var fnName = 'extFnProxy' + (++Ext.chart.Chart.PROXY_FN_ID);
58341         Ext.chart.Chart.proxyFunction[fnName] = fn;
58342         return 'Ext.chart.Chart.proxyFunction.' + fnName;
58343     },
58344
58345     // private
58346     removeFnProxy : function(fn){
58347         if(!Ext.isEmpty(fn)){
58348             fn = fn.replace('Ext.chart.Chart.proxyFunction.', '');
58349             delete Ext.chart.Chart.proxyFunction[fn];
58350         }
58351     },
58352
58353     // private
58354     getFunctionRef : function(val){
58355         if(Ext.isFunction(val)){
58356             return {
58357                 fn: val,
58358                 scope: this
58359             };
58360         }else{
58361             return {
58362                 fn: val.fn,
58363                 scope: val.scope || this
58364             };
58365         }
58366     },
58367
58368     // private
58369     onDestroy: function(){
58370         if (this.refreshTask && this.refreshTask.cancel){
58371             this.refreshTask.cancel();
58372         }
58373         Ext.chart.Chart.superclass.onDestroy.call(this);
58374         this.bindStore(null);
58375         this.removeFnProxy(this.tipFnName);
58376         this.removeFnProxy(this.legendFnName);
58377     }
58378 });
58379 Ext.reg('chart', Ext.chart.Chart);
58380 Ext.chart.Chart.PROXY_FN_ID = 0;
58381 Ext.chart.Chart.proxyFunction = {};
58382
58383 /**
58384  * Sets the url to load the chart from. This should be set to a local resource.
58385  * @static
58386  * @type String
58387  */
58388 Ext.chart.Chart.CHART_URL = 'http:/' + '/yui.yahooapis.com/2.8.2/build/charts/assets/charts.swf';
58389
58390 /**
58391  * @class Ext.chart.PieChart
58392  * @extends Ext.chart.Chart
58393  * @constructor
58394  * @xtype piechart
58395  */
58396 Ext.chart.PieChart = Ext.extend(Ext.chart.Chart, {
58397     type: 'pie',
58398
58399     onSwfReady : function(isReset){
58400         Ext.chart.PieChart.superclass.onSwfReady.call(this, isReset);
58401
58402         this.setDataField(this.dataField);
58403         this.setCategoryField(this.categoryField);
58404     },
58405
58406     setDataField : function(field){
58407         this.dataField = field;
58408         this.swf.setDataField(field);
58409     },
58410
58411     setCategoryField : function(field){
58412         this.categoryField = field;
58413         this.swf.setCategoryField(field);
58414     }
58415 });
58416 Ext.reg('piechart', Ext.chart.PieChart);
58417
58418 /**
58419  * @class Ext.chart.CartesianChart
58420  * @extends Ext.chart.Chart
58421  * @constructor
58422  * @xtype cartesianchart
58423  */
58424 Ext.chart.CartesianChart = Ext.extend(Ext.chart.Chart, {
58425     onSwfReady : function(isReset){
58426         Ext.chart.CartesianChart.superclass.onSwfReady.call(this, isReset);
58427         this.labelFn = [];
58428         if(this.xField){
58429             this.setXField(this.xField);
58430         }
58431         if(this.yField){
58432             this.setYField(this.yField);
58433         }
58434         if(this.xAxis){
58435             this.setXAxis(this.xAxis);
58436         }
58437         if(this.xAxes){
58438             this.setXAxes(this.xAxes);
58439         }
58440         if(this.yAxis){
58441             this.setYAxis(this.yAxis);
58442         }
58443         if(this.yAxes){
58444             this.setYAxes(this.yAxes);
58445         }
58446         if(Ext.isDefined(this.constrainViewport)){
58447             this.swf.setConstrainViewport(this.constrainViewport);
58448         }
58449     },
58450
58451     setXField : function(value){
58452         this.xField = value;
58453         this.swf.setHorizontalField(value);
58454     },
58455
58456     setYField : function(value){
58457         this.yField = value;
58458         this.swf.setVerticalField(value);
58459     },
58460
58461     setXAxis : function(value){
58462         this.xAxis = this.createAxis('xAxis', value);
58463         this.swf.setHorizontalAxis(this.xAxis);
58464     },
58465
58466     setXAxes : function(value){
58467         var axis;
58468         for(var i = 0; i < value.length; i++) {
58469             axis = this.createAxis('xAxis' + i, value[i]);
58470             this.swf.setHorizontalAxis(axis);
58471         }
58472     },
58473
58474     setYAxis : function(value){
58475         this.yAxis = this.createAxis('yAxis', value);
58476         this.swf.setVerticalAxis(this.yAxis);
58477     },
58478
58479     setYAxes : function(value){
58480         var axis;
58481         for(var i = 0; i < value.length; i++) {
58482             axis = this.createAxis('yAxis' + i, value[i]);
58483             this.swf.setVerticalAxis(axis);
58484         }
58485     },
58486
58487     createAxis : function(axis, value){
58488         var o = Ext.apply({}, value),
58489             ref,
58490             old;
58491
58492         if(this[axis]){
58493             old = this[axis].labelFunction;
58494             this.removeFnProxy(old);
58495             this.labelFn.remove(old);
58496         }
58497         if(o.labelRenderer){
58498             ref = this.getFunctionRef(o.labelRenderer);
58499             o.labelFunction = this.createFnProxy(function(v){
58500                 return ref.fn.call(ref.scope, v);
58501             });
58502             delete o.labelRenderer;
58503             this.labelFn.push(o.labelFunction);
58504         }
58505         if(axis.indexOf('xAxis') > -1 && o.position == 'left'){
58506             o.position = 'bottom';
58507         }
58508         return o;
58509     },
58510
58511     onDestroy : function(){
58512         Ext.chart.CartesianChart.superclass.onDestroy.call(this);
58513         Ext.each(this.labelFn, function(fn){
58514             this.removeFnProxy(fn);
58515         }, this);
58516     }
58517 });
58518 Ext.reg('cartesianchart', Ext.chart.CartesianChart);
58519
58520 /**
58521  * @class Ext.chart.LineChart
58522  * @extends Ext.chart.CartesianChart
58523  * @constructor
58524  * @xtype linechart
58525  */
58526 Ext.chart.LineChart = Ext.extend(Ext.chart.CartesianChart, {
58527     type: 'line'
58528 });
58529 Ext.reg('linechart', Ext.chart.LineChart);
58530
58531 /**
58532  * @class Ext.chart.ColumnChart
58533  * @extends Ext.chart.CartesianChart
58534  * @constructor
58535  * @xtype columnchart
58536  */
58537 Ext.chart.ColumnChart = Ext.extend(Ext.chart.CartesianChart, {
58538     type: 'column'
58539 });
58540 Ext.reg('columnchart', Ext.chart.ColumnChart);
58541
58542 /**
58543  * @class Ext.chart.StackedColumnChart
58544  * @extends Ext.chart.CartesianChart
58545  * @constructor
58546  * @xtype stackedcolumnchart
58547  */
58548 Ext.chart.StackedColumnChart = Ext.extend(Ext.chart.CartesianChart, {
58549     type: 'stackcolumn'
58550 });
58551 Ext.reg('stackedcolumnchart', Ext.chart.StackedColumnChart);
58552
58553 /**
58554  * @class Ext.chart.BarChart
58555  * @extends Ext.chart.CartesianChart
58556  * @constructor
58557  * @xtype barchart
58558  */
58559 Ext.chart.BarChart = Ext.extend(Ext.chart.CartesianChart, {
58560     type: 'bar'
58561 });
58562 Ext.reg('barchart', Ext.chart.BarChart);
58563
58564 /**
58565  * @class Ext.chart.StackedBarChart
58566  * @extends Ext.chart.CartesianChart
58567  * @constructor
58568  * @xtype stackedbarchart
58569  */
58570 Ext.chart.StackedBarChart = Ext.extend(Ext.chart.CartesianChart, {
58571     type: 'stackbar'
58572 });
58573 Ext.reg('stackedbarchart', Ext.chart.StackedBarChart);
58574
58575
58576
58577 /**
58578  * @class Ext.chart.Axis
58579  * Defines a CartesianChart's vertical or horizontal axis.
58580  * @constructor
58581  */
58582 Ext.chart.Axis = function(config){
58583     Ext.apply(this, config);
58584 };
58585
58586 Ext.chart.Axis.prototype =
58587 {
58588     /**
58589      * The type of axis.
58590      *
58591      * @property type
58592      * @type String
58593      */
58594     type: null,
58595
58596     /**
58597      * The direction in which the axis is drawn. May be "horizontal" or "vertical".
58598      *
58599      * @property orientation
58600      * @type String
58601      */
58602     orientation: "horizontal",
58603
58604     /**
58605      * If true, the items on the axis will be drawn in opposite direction.
58606      *
58607      * @property reverse
58608      * @type Boolean
58609      */
58610     reverse: false,
58611
58612     /**
58613      * A string reference to the globally-accessible function that may be called to
58614      * determine each of the label values for this axis.
58615      *
58616      * @property labelFunction
58617      * @type String
58618      */
58619     labelFunction: null,
58620
58621     /**
58622      * If true, labels that overlap previously drawn labels on the axis will be hidden.
58623      *
58624      * @property hideOverlappingLabels
58625      * @type Boolean
58626      */
58627     hideOverlappingLabels: true,
58628
58629     /**
58630      * The space, in pixels, between labels on an axis.
58631      *
58632      * @property labelSpacing
58633      * @type Number
58634      */
58635     labelSpacing: 2
58636 };
58637
58638 /**
58639  * @class Ext.chart.NumericAxis
58640  * @extends Ext.chart.Axis
58641  * A type of axis whose units are measured in numeric values.
58642  * @constructor
58643  */
58644 Ext.chart.NumericAxis = Ext.extend(Ext.chart.Axis, {
58645     type: "numeric",
58646
58647     /**
58648      * The minimum value drawn by the axis. If not set explicitly, the axis
58649      * minimum will be calculated automatically.
58650      *
58651      * @property minimum
58652      * @type Number
58653      */
58654     minimum: NaN,
58655
58656     /**
58657      * The maximum value drawn by the axis. If not set explicitly, the axis
58658      * maximum will be calculated automatically.
58659      *
58660      * @property maximum
58661      * @type Number
58662      */
58663     maximum: NaN,
58664
58665     /**
58666      * The spacing between major intervals on this axis.
58667      *
58668      * @property majorUnit
58669      * @type Number
58670      */
58671     majorUnit: NaN,
58672
58673     /**
58674      * The spacing between minor intervals on this axis.
58675      *
58676      * @property minorUnit
58677      * @type Number
58678      */
58679     minorUnit: NaN,
58680
58681     /**
58682      * If true, the labels, ticks, gridlines, and other objects will snap to the
58683      * nearest major or minor unit. If false, their position will be based on
58684      * the minimum value.
58685      *
58686      * @property snapToUnits
58687      * @type Boolean
58688      */
58689     snapToUnits: true,
58690
58691     /**
58692      * If true, and the bounds are calculated automatically, either the minimum
58693      * or maximum will be set to zero.
58694      *
58695      * @property alwaysShowZero
58696      * @type Boolean
58697      */
58698     alwaysShowZero: true,
58699
58700     /**
58701      * The scaling algorithm to use on this axis. May be "linear" or
58702      * "logarithmic".
58703      *
58704      * @property scale
58705      * @type String
58706      */
58707     scale: "linear",
58708
58709     /**
58710      * Indicates whether to round the major unit.
58711      *
58712      * @property roundMajorUnit
58713      * @type Boolean
58714      */
58715     roundMajorUnit: true,
58716
58717     /**
58718      * Indicates whether to factor in the size of the labels when calculating a
58719      * major unit.
58720      *
58721      * @property calculateByLabelSize
58722      * @type Boolean
58723      */
58724     calculateByLabelSize: true,
58725
58726     /**
58727      * Indicates the position of the axis relative to the chart
58728      *
58729      * @property position
58730      * @type String
58731      */
58732     position: 'left',
58733
58734     /**
58735      * Indicates whether to extend maximum beyond data's maximum to the nearest
58736      * majorUnit.
58737      *
58738      * @property adjustMaximumByMajorUnit
58739      * @type Boolean
58740      */
58741     adjustMaximumByMajorUnit: true,
58742
58743     /**
58744      * Indicates whether to extend the minimum beyond data's minimum to the
58745      * nearest majorUnit.
58746      *
58747      * @property adjustMinimumByMajorUnit
58748      * @type Boolean
58749      */
58750     adjustMinimumByMajorUnit: true
58751
58752 });
58753
58754 /**
58755  * @class Ext.chart.TimeAxis
58756  * @extends Ext.chart.Axis
58757  * A type of axis whose units are measured in time-based values.
58758  * @constructor
58759  */
58760 Ext.chart.TimeAxis = Ext.extend(Ext.chart.Axis, {
58761     type: "time",
58762
58763     /**
58764      * The minimum value drawn by the axis. If not set explicitly, the axis
58765      * minimum will be calculated automatically.
58766      *
58767      * @property minimum
58768      * @type Date
58769      */
58770     minimum: null,
58771
58772     /**
58773      * The maximum value drawn by the axis. If not set explicitly, the axis
58774      * maximum will be calculated automatically.
58775      *
58776      * @property maximum
58777      * @type Number
58778      */
58779     maximum: null,
58780
58781     /**
58782      * The spacing between major intervals on this axis.
58783      *
58784      * @property majorUnit
58785      * @type Number
58786      */
58787     majorUnit: NaN,
58788
58789     /**
58790      * The time unit used by the majorUnit.
58791      *
58792      * @property majorTimeUnit
58793      * @type String
58794      */
58795     majorTimeUnit: null,
58796
58797     /**
58798      * The spacing between minor intervals on this axis.
58799      *
58800      * @property majorUnit
58801      * @type Number
58802      */
58803     minorUnit: NaN,
58804
58805     /**
58806      * The time unit used by the minorUnit.
58807      *
58808      * @property majorTimeUnit
58809      * @type String
58810      */
58811     minorTimeUnit: null,
58812
58813     /**
58814      * If true, the labels, ticks, gridlines, and other objects will snap to the
58815      * nearest major or minor unit. If false, their position will be based on
58816      * the minimum value.
58817      *
58818      * @property snapToUnits
58819      * @type Boolean
58820      */
58821     snapToUnits: true,
58822
58823     /**
58824      * Series that are stackable will only stack when this value is set to true.
58825      *
58826      * @property stackingEnabled
58827      * @type Boolean
58828      */
58829     stackingEnabled: false,
58830
58831     /**
58832      * Indicates whether to factor in the size of the labels when calculating a
58833      * major unit.
58834      *
58835      * @property calculateByLabelSize
58836      * @type Boolean
58837      */
58838     calculateByLabelSize: true
58839
58840 });
58841
58842 /**
58843  * @class Ext.chart.CategoryAxis
58844  * @extends Ext.chart.Axis
58845  * A type of axis that displays items in categories.
58846  * @constructor
58847  */
58848 Ext.chart.CategoryAxis = Ext.extend(Ext.chart.Axis, {
58849     type: "category",
58850
58851     /**
58852      * A list of category names to display along this axis.
58853      *
58854      * @property categoryNames
58855      * @type Array
58856      */
58857     categoryNames: null,
58858
58859     /**
58860      * Indicates whether or not to calculate the number of categories (ticks and
58861      * labels) when there is not enough room to display all labels on the axis.
58862      * If set to true, the axis will determine the number of categories to plot.
58863      * If not, all categories will be plotted.
58864      *
58865      * @property calculateCategoryCount
58866      * @type Boolean
58867      */
58868     calculateCategoryCount: false
58869
58870 });
58871
58872 /**
58873  * @class Ext.chart.Series
58874  * Series class for the charts widget.
58875  * @constructor
58876  */
58877 Ext.chart.Series = function(config) { Ext.apply(this, config); };
58878
58879 Ext.chart.Series.prototype =
58880 {
58881     /**
58882      * The type of series.
58883      *
58884      * @property type
58885      * @type String
58886      */
58887     type: null,
58888
58889     /**
58890      * The human-readable name of the series.
58891      *
58892      * @property displayName
58893      * @type String
58894      */
58895     displayName: null
58896 };
58897
58898 /**
58899  * @class Ext.chart.CartesianSeries
58900  * @extends Ext.chart.Series
58901  * CartesianSeries class for the charts widget.
58902  * @constructor
58903  */
58904 Ext.chart.CartesianSeries = Ext.extend(Ext.chart.Series, {
58905     /**
58906      * The field used to access the x-axis value from the items from the data
58907      * source.
58908      *
58909      * @property xField
58910      * @type String
58911      */
58912     xField: null,
58913
58914     /**
58915      * The field used to access the y-axis value from the items from the data
58916      * source.
58917      *
58918      * @property yField
58919      * @type String
58920      */
58921     yField: null,
58922
58923     /**
58924      * False to not show this series in the legend. Defaults to <tt>true</tt>.
58925      *
58926      * @property showInLegend
58927      * @type Boolean
58928      */
58929     showInLegend: true,
58930
58931     /**
58932      * Indicates which axis the series will bind to
58933      *
58934      * @property axis
58935      * @type String
58936      */
58937     axis: 'primary'
58938 });
58939
58940 /**
58941  * @class Ext.chart.ColumnSeries
58942  * @extends Ext.chart.CartesianSeries
58943  * ColumnSeries class for the charts widget.
58944  * @constructor
58945  */
58946 Ext.chart.ColumnSeries = Ext.extend(Ext.chart.CartesianSeries, {
58947     type: "column"
58948 });
58949
58950 /**
58951  * @class Ext.chart.LineSeries
58952  * @extends Ext.chart.CartesianSeries
58953  * LineSeries class for the charts widget.
58954  * @constructor
58955  */
58956 Ext.chart.LineSeries = Ext.extend(Ext.chart.CartesianSeries, {
58957     type: "line"
58958 });
58959
58960 /**
58961  * @class Ext.chart.BarSeries
58962  * @extends Ext.chart.CartesianSeries
58963  * BarSeries class for the charts widget.
58964  * @constructor
58965  */
58966 Ext.chart.BarSeries = Ext.extend(Ext.chart.CartesianSeries, {
58967     type: "bar"
58968 });
58969
58970
58971 /**
58972  * @class Ext.chart.PieSeries
58973  * @extends Ext.chart.Series
58974  * PieSeries class for the charts widget.
58975  * @constructor
58976  */
58977 Ext.chart.PieSeries = Ext.extend(Ext.chart.Series, {
58978     type: "pie",
58979     dataField: null,
58980     categoryField: null
58981 });/**
58982  * @class Ext.menu.Menu
58983  * @extends Ext.Container
58984  * <p>A menu object.  This is the container to which you may add menu items.  Menu can also serve as a base class
58985  * when you want a specialized menu based off of another component (like {@link Ext.menu.DateMenu} for example).</p>
58986  * <p>Menus may contain either {@link Ext.menu.Item menu items}, or general {@link Ext.Component Component}s.</p>
58987  * <p>To make a contained general {@link Ext.Component Component} line up with other {@link Ext.menu.Item menu items}
58988  * specify <tt>iconCls: 'no-icon'</tt>.  This reserves a space for an icon, and indents the Component in line
58989  * with the other menu items.  See {@link Ext.form.ComboBox}.{@link Ext.form.ComboBox#getListParent getListParent}
58990  * for an example.</p>
58991  * <p>By default, Menus are absolutely positioned, floating Components. By configuring a Menu with
58992  * <b><tt>{@link #floating}:false</tt></b>, a Menu may be used as child of a Container.</p>
58993  *
58994  * @xtype menu
58995  */
58996 Ext.menu.Menu = Ext.extend(Ext.Container, {
58997     /**
58998      * @cfg {Object} defaults
58999      * A config object that will be applied to all items added to this container either via the {@link #items}
59000      * config or via the {@link #add} method.  The defaults config can contain any number of
59001      * name/value property pairs to be added to each item, and should be valid for the types of items
59002      * being added to the menu.
59003      */
59004     /**
59005      * @cfg {Mixed} items
59006      * An array of items to be added to this menu. Menus may contain either {@link Ext.menu.Item menu items},
59007      * or general {@link Ext.Component Component}s.
59008      */
59009     /**
59010      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
59011      */
59012     minWidth : 120,
59013     /**
59014      * @cfg {Boolean/String} shadow True or 'sides' for the default effect, 'frame' for 4-way shadow, and 'drop'
59015      * for bottom-right shadow (defaults to 'sides')
59016      */
59017     shadow : 'sides',
59018     /**
59019      * @cfg {String} subMenuAlign The {@link Ext.Element#alignTo} anchor position value to use for submenus of
59020      * this menu (defaults to 'tl-tr?')
59021      */
59022     subMenuAlign : 'tl-tr?',
59023     /**
59024      * @cfg {String} defaultAlign The default {@link Ext.Element#alignTo} anchor position value for this menu
59025      * relative to its element of origin (defaults to 'tl-bl?')
59026      */
59027     defaultAlign : 'tl-bl?',
59028     /**
59029      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
59030      */
59031     allowOtherMenus : false,
59032     /**
59033      * @cfg {Boolean} ignoreParentClicks True to ignore clicks on any item in this menu that is a parent item (displays
59034      * a submenu) so that the submenu is not dismissed when clicking the parent item (defaults to false).
59035      */
59036     ignoreParentClicks : false,
59037     /**
59038      * @cfg {Boolean} enableScrolling True to allow the menu container to have scroller controls if the menu is too long (defaults to true).
59039      */
59040     enableScrolling : true,
59041     /**
59042      * @cfg {Number} maxHeight The maximum height of the menu. Only applies when enableScrolling is set to True (defaults to null).
59043      */
59044     maxHeight : null,
59045     /**
59046      * @cfg {Number} scrollIncrement The amount to scroll the menu. Only applies when enableScrolling is set to True (defaults to 24).
59047      */
59048     scrollIncrement : 24,
59049     /**
59050      * @cfg {Boolean} showSeparator True to show the icon separator. (defaults to true).
59051      */
59052     showSeparator : true,
59053     /**
59054      * @cfg {Array} defaultOffsets An array specifying the [x, y] offset in pixels by which to
59055      * change the default Menu popup position after aligning according to the {@link #defaultAlign}
59056      * configuration. Defaults to <tt>[0, 0]</tt>.
59057      */
59058     defaultOffsets : [0, 0],
59059
59060     /**
59061      * @cfg {Boolean} plain
59062      * True to remove the incised line down the left side of the menu. Defaults to <tt>false</tt>.
59063      */
59064     plain : false,
59065
59066     /**
59067      * @cfg {Boolean} floating
59068      * <p>By default, a Menu configured as <b><code>floating:true</code></b>
59069      * will be rendered as an {@link Ext.Layer} (an absolutely positioned,
59070      * floating Component with zindex=15000).
59071      * If configured as <b><code>floating:false</code></b>, the Menu may be
59072      * used as child item of another Container instead of a free-floating
59073      * {@link Ext.Layer Layer}.
59074      */
59075     floating : true,
59076
59077
59078     /**
59079      * @cfg {Number} zIndex
59080      * zIndex to use when the menu is floating.
59081      */
59082     zIndex: 15000,
59083
59084     // private
59085     hidden : true,
59086
59087     /**
59088      * @cfg {String/Object} layout
59089      * This class assigns a default layout (<code>layout:'<b>menu</b>'</code>).
59090      * Developers <i>may</i> override this configuration option if another layout is required.
59091      * See {@link Ext.Container#layout} for additional information.
59092      */
59093     layout : 'menu',
59094     hideMode : 'offsets',    // Important for laying out Components
59095     scrollerHeight : 8,
59096     autoLayout : true,       // Provided for backwards compat
59097     defaultType : 'menuitem',
59098     bufferResize : false,
59099
59100     initComponent : function(){
59101         if(Ext.isArray(this.initialConfig)){
59102             Ext.apply(this, {items:this.initialConfig});
59103         }
59104         this.addEvents(
59105             /**
59106              * @event click
59107              * Fires when this menu is clicked (or when the enter key is pressed while it is active)
59108              * @param {Ext.menu.Menu} this
59109             * @param {Ext.menu.Item} menuItem The menu item that was clicked
59110              * @param {Ext.EventObject} e
59111              */
59112             'click',
59113             /**
59114              * @event mouseover
59115              * Fires when the mouse is hovering over this menu
59116              * @param {Ext.menu.Menu} this
59117              * @param {Ext.EventObject} e
59118              * @param {Ext.menu.Item} menuItem The menu item that was clicked
59119              */
59120             'mouseover',
59121             /**
59122              * @event mouseout
59123              * Fires when the mouse exits this menu
59124              * @param {Ext.menu.Menu} this
59125              * @param {Ext.EventObject} e
59126              * @param {Ext.menu.Item} menuItem The menu item that was clicked
59127              */
59128             'mouseout',
59129             /**
59130              * @event itemclick
59131              * Fires when a menu item contained in this menu is clicked
59132              * @param {Ext.menu.BaseItem} baseItem The BaseItem that was clicked
59133              * @param {Ext.EventObject} e
59134              */
59135             'itemclick'
59136         );
59137         Ext.menu.MenuMgr.register(this);
59138         if(this.floating){
59139             Ext.EventManager.onWindowResize(this.hide, this);
59140         }else{
59141             if(this.initialConfig.hidden !== false){
59142                 this.hidden = false;
59143             }
59144             this.internalDefaults = {hideOnClick: false};
59145         }
59146         Ext.menu.Menu.superclass.initComponent.call(this);
59147         if(this.autoLayout){
59148             var fn = this.doLayout.createDelegate(this, []);
59149             this.on({
59150                 add: fn,
59151                 remove: fn
59152             });
59153         }
59154     },
59155
59156     //private
59157     getLayoutTarget : function() {
59158         return this.ul;
59159     },
59160
59161     // private
59162     onRender : function(ct, position){
59163         if(!ct){
59164             ct = Ext.getBody();
59165         }
59166
59167         var dh = {
59168             id: this.getId(),
59169             cls: 'x-menu ' + ((this.floating) ? 'x-menu-floating x-layer ' : '') + (this.cls || '') + (this.plain ? ' x-menu-plain' : '') + (this.showSeparator ? '' : ' x-menu-nosep'),
59170             style: this.style,
59171             cn: [
59172                 {tag: 'a', cls: 'x-menu-focus', href: '#', onclick: 'return false;', tabIndex: '-1'},
59173                 {tag: 'ul', cls: 'x-menu-list'}
59174             ]
59175         };
59176         if(this.floating){
59177             this.el = new Ext.Layer({
59178                 shadow: this.shadow,
59179                 dh: dh,
59180                 constrain: false,
59181                 parentEl: ct,
59182                 zindex: this.zIndex
59183             });
59184         }else{
59185             this.el = ct.createChild(dh);
59186         }
59187         Ext.menu.Menu.superclass.onRender.call(this, ct, position);
59188
59189         if(!this.keyNav){
59190             this.keyNav = new Ext.menu.MenuNav(this);
59191         }
59192         // generic focus element
59193         this.focusEl = this.el.child('a.x-menu-focus');
59194         this.ul = this.el.child('ul.x-menu-list');
59195         this.mon(this.ul, {
59196             scope: this,
59197             click: this.onClick,
59198             mouseover: this.onMouseOver,
59199             mouseout: this.onMouseOut
59200         });
59201         if(this.enableScrolling){
59202             this.mon(this.el, {
59203                 scope: this,
59204                 delegate: '.x-menu-scroller',
59205                 click: this.onScroll,
59206                 mouseover: this.deactivateActive
59207             });
59208         }
59209     },
59210
59211     // private
59212     findTargetItem : function(e){
59213         var t = e.getTarget('.x-menu-list-item', this.ul, true);
59214         if(t && t.menuItemId){
59215             return this.items.get(t.menuItemId);
59216         }
59217     },
59218
59219     // private
59220     onClick : function(e){
59221         var t = this.findTargetItem(e);
59222         if(t){
59223             if(t.isFormField){
59224                 this.setActiveItem(t);
59225             }else if(t instanceof Ext.menu.BaseItem){
59226                 if(t.menu && this.ignoreParentClicks){
59227                     t.expandMenu();
59228                     e.preventDefault();
59229                 }else if(t.onClick){
59230                     t.onClick(e);
59231                     this.fireEvent('click', this, t, e);
59232                 }
59233             }
59234         }
59235     },
59236
59237     // private
59238     setActiveItem : function(item, autoExpand){
59239         if(item != this.activeItem){
59240             this.deactivateActive();
59241             if((this.activeItem = item).isFormField){
59242                 item.focus();
59243             }else{
59244                 item.activate(autoExpand);
59245             }
59246         }else if(autoExpand){
59247             item.expandMenu();
59248         }
59249     },
59250
59251     deactivateActive : function(){
59252         var a = this.activeItem;
59253         if(a){
59254             if(a.isFormField){
59255                 //Fields cannot deactivate, but Combos must collapse
59256                 if(a.collapse){
59257                     a.collapse();
59258                 }
59259             }else{
59260                 a.deactivate();
59261             }
59262             delete this.activeItem;
59263         }
59264     },
59265
59266     // private
59267     tryActivate : function(start, step){
59268         var items = this.items;
59269         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
59270             var item = items.get(i);
59271             if(item.isVisible() && !item.disabled && (item.canActivate || item.isFormField)){
59272                 this.setActiveItem(item, false);
59273                 return item;
59274             }
59275         }
59276         return false;
59277     },
59278
59279     // private
59280     onMouseOver : function(e){
59281         var t = this.findTargetItem(e);
59282         if(t){
59283             if(t.canActivate && !t.disabled){
59284                 this.setActiveItem(t, true);
59285             }
59286         }
59287         this.over = true;
59288         this.fireEvent('mouseover', this, e, t);
59289     },
59290
59291     // private
59292     onMouseOut : function(e){
59293         var t = this.findTargetItem(e);
59294         if(t){
59295             if(t == this.activeItem && t.shouldDeactivate && t.shouldDeactivate(e)){
59296                 this.activeItem.deactivate();
59297                 delete this.activeItem;
59298             }
59299         }
59300         this.over = false;
59301         this.fireEvent('mouseout', this, e, t);
59302     },
59303
59304     // private
59305     onScroll : function(e, t){
59306         if(e){
59307             e.stopEvent();
59308         }
59309         var ul = this.ul.dom, top = Ext.fly(t).is('.x-menu-scroller-top');
59310         ul.scrollTop += this.scrollIncrement * (top ? -1 : 1);
59311         if(top ? ul.scrollTop <= 0 : ul.scrollTop + this.activeMax >= ul.scrollHeight){
59312            this.onScrollerOut(null, t);
59313         }
59314     },
59315
59316     // private
59317     onScrollerIn : function(e, t){
59318         var ul = this.ul.dom, top = Ext.fly(t).is('.x-menu-scroller-top');
59319         if(top ? ul.scrollTop > 0 : ul.scrollTop + this.activeMax < ul.scrollHeight){
59320             Ext.fly(t).addClass(['x-menu-item-active', 'x-menu-scroller-active']);
59321         }
59322     },
59323
59324     // private
59325     onScrollerOut : function(e, t){
59326         Ext.fly(t).removeClass(['x-menu-item-active', 'x-menu-scroller-active']);
59327     },
59328
59329     /**
59330      * If <code>{@link #floating}=true</code>, shows this menu relative to
59331      * another element using {@link #showat}, otherwise uses {@link Ext.Component#show}.
59332      * @param {Mixed} element The element to align to
59333      * @param {String} position (optional) The {@link Ext.Element#alignTo} anchor position to use in aligning to
59334      * the element (defaults to this.defaultAlign)
59335      * @param {Ext.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
59336      */
59337     show : function(el, pos, parentMenu){
59338         if(this.floating){
59339             this.parentMenu = parentMenu;
59340             if(!this.el){
59341                 this.render();
59342                 this.doLayout(false, true);
59343             }
59344             this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign, this.defaultOffsets), parentMenu);
59345         }else{
59346             Ext.menu.Menu.superclass.show.call(this);
59347         }
59348     },
59349
59350     /**
59351      * Displays this menu at a specific xy position and fires the 'show' event if a
59352      * handler for the 'beforeshow' event does not return false cancelling the operation.
59353      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
59354      * @param {Ext.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
59355      */
59356     showAt : function(xy, parentMenu){
59357         if(this.fireEvent('beforeshow', this) !== false){
59358             this.parentMenu = parentMenu;
59359             if(!this.el){
59360                 this.render();
59361             }
59362             if(this.enableScrolling){
59363                 // set the position so we can figure out the constrain value.
59364                 this.el.setXY(xy);
59365                 //constrain the value, keep the y coordinate the same
59366                 xy[1] = this.constrainScroll(xy[1]);
59367                 xy = [this.el.adjustForConstraints(xy)[0], xy[1]];
59368             }else{
59369                 //constrain to the viewport.
59370                 xy = this.el.adjustForConstraints(xy);
59371             }
59372             this.el.setXY(xy);
59373             this.el.show();
59374             Ext.menu.Menu.superclass.onShow.call(this);
59375             if(Ext.isIE){
59376                 // internal event, used so we don't couple the layout to the menu
59377                 this.fireEvent('autosize', this);
59378                 if(!Ext.isIE8){
59379                     this.el.repaint();
59380                 }
59381             }
59382             this.hidden = false;
59383             this.focus();
59384             this.fireEvent('show', this);
59385         }
59386     },
59387
59388     constrainScroll : function(y){
59389         var max, full = this.ul.setHeight('auto').getHeight(),
59390             returnY = y, normalY, parentEl, scrollTop, viewHeight;
59391         if(this.floating){
59392             parentEl = Ext.fly(this.el.dom.parentNode);
59393             scrollTop = parentEl.getScroll().top;
59394             viewHeight = parentEl.getViewSize().height;
59395             //Normalize y by the scroll position for the parent element.  Need to move it into the coordinate space
59396             //of the view.
59397             normalY = y - scrollTop;
59398             max = this.maxHeight ? this.maxHeight : viewHeight - normalY;
59399             if(full > viewHeight) {
59400                 max = viewHeight;
59401                 //Set returnY equal to (0,0) in view space by reducing y by the value of normalY
59402                 returnY = y - normalY;
59403             } else if(max < full) {
59404                 returnY = y - (full - max);
59405                 max = full;
59406             }
59407         }else{
59408             max = this.getHeight();
59409         }
59410         // Always respect maxHeight 
59411         if (this.maxHeight){
59412             max = Math.min(this.maxHeight, max);
59413         }
59414         if(full > max && max > 0){
59415             this.activeMax = max - this.scrollerHeight * 2 - this.el.getFrameWidth('tb') - Ext.num(this.el.shadowOffset, 0);
59416             this.ul.setHeight(this.activeMax);
59417             this.createScrollers();
59418             this.el.select('.x-menu-scroller').setDisplayed('');
59419         }else{
59420             this.ul.setHeight(full);
59421             this.el.select('.x-menu-scroller').setDisplayed('none');
59422         }
59423         this.ul.dom.scrollTop = 0;
59424         return returnY;
59425     },
59426
59427     createScrollers : function(){
59428         if(!this.scroller){
59429             this.scroller = {
59430                 pos: 0,
59431                 top: this.el.insertFirst({
59432                     tag: 'div',
59433                     cls: 'x-menu-scroller x-menu-scroller-top',
59434                     html: '&#160;'
59435                 }),
59436                 bottom: this.el.createChild({
59437                     tag: 'div',
59438                     cls: 'x-menu-scroller x-menu-scroller-bottom',
59439                     html: '&#160;'
59440                 })
59441             };
59442             this.scroller.top.hover(this.onScrollerIn, this.onScrollerOut, this);
59443             this.scroller.topRepeater = new Ext.util.ClickRepeater(this.scroller.top, {
59444                 listeners: {
59445                     click: this.onScroll.createDelegate(this, [null, this.scroller.top], false)
59446                 }
59447             });
59448             this.scroller.bottom.hover(this.onScrollerIn, this.onScrollerOut, this);
59449             this.scroller.bottomRepeater = new Ext.util.ClickRepeater(this.scroller.bottom, {
59450                 listeners: {
59451                     click: this.onScroll.createDelegate(this, [null, this.scroller.bottom], false)
59452                 }
59453             });
59454         }
59455     },
59456
59457     onLayout : function(){
59458         if(this.isVisible()){
59459             if(this.enableScrolling){
59460                 this.constrainScroll(this.el.getTop());
59461             }
59462             if(this.floating){
59463                 this.el.sync();
59464             }
59465         }
59466     },
59467
59468     focus : function(){
59469         if(!this.hidden){
59470             this.doFocus.defer(50, this);
59471         }
59472     },
59473
59474     doFocus : function(){
59475         if(!this.hidden){
59476             this.focusEl.focus();
59477         }
59478     },
59479
59480     /**
59481      * Hides this menu and optionally all parent menus
59482      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
59483      */
59484     hide : function(deep){
59485         if (!this.isDestroyed) {
59486             this.deepHide = deep;
59487             Ext.menu.Menu.superclass.hide.call(this);
59488             delete this.deepHide;
59489         }
59490     },
59491
59492     // private
59493     onHide : function(){
59494         Ext.menu.Menu.superclass.onHide.call(this);
59495         this.deactivateActive();
59496         if(this.el && this.floating){
59497             this.el.hide();
59498         }
59499         var pm = this.parentMenu;
59500         if(this.deepHide === true && pm){
59501             if(pm.floating){
59502                 pm.hide(true);
59503             }else{
59504                 pm.deactivateActive();
59505             }
59506         }
59507     },
59508
59509     // private
59510     lookupComponent : function(c){
59511          if(Ext.isString(c)){
59512             c = (c == 'separator' || c == '-') ? new Ext.menu.Separator() : new Ext.menu.TextItem(c);
59513              this.applyDefaults(c);
59514          }else{
59515             if(Ext.isObject(c)){
59516                 c = this.getMenuItem(c);
59517             }else if(c.tagName || c.el){ // element. Wrap it.
59518                 c = new Ext.BoxComponent({
59519                     el: c
59520                 });
59521             }
59522          }
59523          return c;
59524     },
59525
59526     applyDefaults : function(c) {
59527         if (!Ext.isString(c)) {
59528             c = Ext.menu.Menu.superclass.applyDefaults.call(this, c);
59529             var d = this.internalDefaults;
59530             if(d){
59531                 if(c.events){
59532                     Ext.applyIf(c.initialConfig, d);
59533                     Ext.apply(c, d);
59534                 }else{
59535                     Ext.applyIf(c, d);
59536                 }
59537             }
59538         }
59539         return c;
59540     },
59541
59542     // private
59543     getMenuItem : function(config) {
59544         if (!config.isXType) {
59545             if (!config.xtype && Ext.isBoolean(config.checked)) {
59546                 return new Ext.menu.CheckItem(config);
59547             }
59548             return Ext.create(config, this.defaultType);
59549         }
59550         return config;
59551     },
59552
59553     /**
59554      * Adds a separator bar to the menu
59555      * @return {Ext.menu.Item} The menu item that was added
59556      */
59557     addSeparator : function() {
59558         return this.add(new Ext.menu.Separator());
59559     },
59560
59561     /**
59562      * Adds an {@link Ext.Element} object to the menu
59563      * @param {Mixed} el The element or DOM node to add, or its id
59564      * @return {Ext.menu.Item} The menu item that was added
59565      */
59566     addElement : function(el) {
59567         return this.add(new Ext.menu.BaseItem({
59568             el: el
59569         }));
59570     },
59571
59572     /**
59573      * Adds an existing object based on {@link Ext.menu.BaseItem} to the menu
59574      * @param {Ext.menu.Item} item The menu item to add
59575      * @return {Ext.menu.Item} The menu item that was added
59576      */
59577     addItem : function(item) {
59578         return this.add(item);
59579     },
59580
59581     /**
59582      * Creates a new {@link Ext.menu.Item} based an the supplied config object and adds it to the menu
59583      * @param {Object} config A MenuItem config object
59584      * @return {Ext.menu.Item} The menu item that was added
59585      */
59586     addMenuItem : function(config) {
59587         return this.add(this.getMenuItem(config));
59588     },
59589
59590     /**
59591      * Creates a new {@link Ext.menu.TextItem} with the supplied text and adds it to the menu
59592      * @param {String} text The text to display in the menu item
59593      * @return {Ext.menu.Item} The menu item that was added
59594      */
59595     addText : function(text){
59596         return this.add(new Ext.menu.TextItem(text));
59597     },
59598
59599     //private
59600     onDestroy : function(){
59601         Ext.EventManager.removeResizeListener(this.hide, this);
59602         var pm = this.parentMenu;
59603         if(pm && pm.activeChild == this){
59604             delete pm.activeChild;
59605         }
59606         delete this.parentMenu;
59607         Ext.menu.Menu.superclass.onDestroy.call(this);
59608         Ext.menu.MenuMgr.unregister(this);
59609         if(this.keyNav) {
59610             this.keyNav.disable();
59611         }
59612         var s = this.scroller;
59613         if(s){
59614             Ext.destroy(s.topRepeater, s.bottomRepeater, s.top, s.bottom);
59615         }
59616         Ext.destroy(
59617             this.el,
59618             this.focusEl,
59619             this.ul
59620         );
59621     }
59622 });
59623
59624 Ext.reg('menu', Ext.menu.Menu);
59625
59626 // MenuNav is a private utility class used internally by the Menu
59627 Ext.menu.MenuNav = Ext.extend(Ext.KeyNav, function(){
59628     function up(e, m){
59629         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
59630             m.tryActivate(m.items.length-1, -1);
59631         }
59632     }
59633     function down(e, m){
59634         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
59635             m.tryActivate(0, 1);
59636         }
59637     }
59638     return {
59639         constructor : function(menu){
59640             Ext.menu.MenuNav.superclass.constructor.call(this, menu.el);
59641             this.scope = this.menu = menu;
59642         },
59643
59644         doRelay : function(e, h){
59645             var k = e.getKey();
59646 //          Keystrokes within a form Field (e.g.: down in a Combo) do not navigate. Allow only TAB
59647             if (this.menu.activeItem && this.menu.activeItem.isFormField && k != e.TAB) {
59648                 return false;
59649             }
59650             if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
59651                 this.menu.tryActivate(0, 1);
59652                 return false;
59653             }
59654             return h.call(this.scope || this, e, this.menu);
59655         },
59656
59657         tab: function(e, m) {
59658             e.stopEvent();
59659             if (e.shiftKey) {
59660                 up(e, m);
59661             } else {
59662                 down(e, m);
59663             }
59664         },
59665
59666         up : up,
59667
59668         down : down,
59669
59670         right : function(e, m){
59671             if(m.activeItem){
59672                 m.activeItem.expandMenu(true);
59673             }
59674         },
59675
59676         left : function(e, m){
59677             m.hide();
59678             if(m.parentMenu && m.parentMenu.activeItem){
59679                 m.parentMenu.activeItem.activate();
59680             }
59681         },
59682
59683         enter : function(e, m){
59684             if(m.activeItem){
59685                 e.stopPropagation();
59686                 m.activeItem.onClick(e);
59687                 m.fireEvent('click', this, m.activeItem);
59688                 return true;
59689             }
59690         }
59691     };
59692 }());
59693 /**
59694  * @class Ext.menu.MenuMgr
59695  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
59696  * @singleton
59697  */
59698 Ext.menu.MenuMgr = function(){
59699    var menus, active, groups = {}, attached = false, lastShow = new Date();
59700
59701    // private - called when first menu is created
59702    function init(){
59703        menus = {};
59704        active = new Ext.util.MixedCollection();
59705        Ext.getDoc().addKeyListener(27, function(){
59706            if(active.length > 0){
59707                hideAll();
59708            }
59709        });
59710    }
59711
59712    // private
59713    function hideAll(){
59714        if(active && active.length > 0){
59715            var c = active.clone();
59716            c.each(function(m){
59717                m.hide();
59718            });
59719            return true;
59720        }
59721        return false;
59722    }
59723
59724    // private
59725    function onHide(m){
59726        active.remove(m);
59727        if(active.length < 1){
59728            Ext.getDoc().un("mousedown", onMouseDown);
59729            attached = false;
59730        }
59731    }
59732
59733    // private
59734    function onShow(m){
59735        var last = active.last();
59736        lastShow = new Date();
59737        active.add(m);
59738        if(!attached){
59739            Ext.getDoc().on("mousedown", onMouseDown);
59740            attached = true;
59741        }
59742        if(m.parentMenu){
59743           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
59744           m.parentMenu.activeChild = m;
59745        }else if(last && !last.isDestroyed && last.isVisible()){
59746           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
59747        }
59748    }
59749
59750    // private
59751    function onBeforeHide(m){
59752        if(m.activeChild){
59753            m.activeChild.hide();
59754        }
59755        if(m.autoHideTimer){
59756            clearTimeout(m.autoHideTimer);
59757            delete m.autoHideTimer;
59758        }
59759    }
59760
59761    // private
59762    function onBeforeShow(m){
59763        var pm = m.parentMenu;
59764        if(!pm && !m.allowOtherMenus){
59765            hideAll();
59766        }else if(pm && pm.activeChild){
59767            pm.activeChild.hide();
59768        }
59769    }
59770
59771    // private
59772    function onMouseDown(e){
59773        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
59774            hideAll();
59775        }
59776    }
59777
59778    return {
59779
59780        /**
59781         * Hides all menus that are currently visible
59782         * @return {Boolean} success True if any active menus were hidden.
59783         */
59784        hideAll : function(){
59785             return hideAll();
59786        },
59787
59788        // private
59789        register : function(menu){
59790            if(!menus){
59791                init();
59792            }
59793            menus[menu.id] = menu;
59794            menu.on({
59795                beforehide: onBeforeHide,
59796                hide: onHide,
59797                beforeshow: onBeforeShow,
59798                show: onShow
59799            });
59800        },
59801
59802         /**
59803          * Returns a {@link Ext.menu.Menu} object
59804          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
59805          * be used to generate and return a new Menu instance.
59806          * @return {Ext.menu.Menu} The specified menu, or null if none are found
59807          */
59808        get : function(menu){
59809            if(typeof menu == "string"){ // menu id
59810                if(!menus){  // not initialized, no menus to return
59811                    return null;
59812                }
59813                return menus[menu];
59814            }else if(menu.events){  // menu instance
59815                return menu;
59816            }else if(typeof menu.length == 'number'){ // array of menu items?
59817                return new Ext.menu.Menu({items:menu});
59818            }else{ // otherwise, must be a config
59819                return Ext.create(menu, 'menu');
59820            }
59821        },
59822
59823        // private
59824        unregister : function(menu){
59825            delete menus[menu.id];
59826            menu.un("beforehide", onBeforeHide);
59827            menu.un("hide", onHide);
59828            menu.un("beforeshow", onBeforeShow);
59829            menu.un("show", onShow);
59830        },
59831
59832        // private
59833        registerCheckable : function(menuItem){
59834            var g = menuItem.group;
59835            if(g){
59836                if(!groups[g]){
59837                    groups[g] = [];
59838                }
59839                groups[g].push(menuItem);
59840            }
59841        },
59842
59843        // private
59844        unregisterCheckable : function(menuItem){
59845            var g = menuItem.group;
59846            if(g){
59847                groups[g].remove(menuItem);
59848            }
59849        },
59850        
59851        // private
59852        onCheckChange: function(item, state){
59853            if(item.group && state){
59854                var group = groups[item.group],
59855                    i = 0,
59856                    len = group.length,
59857                    current;
59858                    
59859                for(; i < len; i++){
59860                    current = group[i];
59861                    if(current != item){
59862                        current.setChecked(false);
59863                    }
59864                }
59865            }
59866        },
59867
59868        getCheckedItem : function(groupId){
59869            var g = groups[groupId];
59870            if(g){
59871                for(var i = 0, l = g.length; i < l; i++){
59872                    if(g[i].checked){
59873                        return g[i];
59874                    }
59875                }
59876            }
59877            return null;
59878        },
59879
59880        setCheckedItem : function(groupId, itemId){
59881            var g = groups[groupId];
59882            if(g){
59883                for(var i = 0, l = g.length; i < l; i++){
59884                    if(g[i].id == itemId){
59885                        g[i].setChecked(true);
59886                    }
59887                }
59888            }
59889            return null;
59890        }
59891    };
59892 }();
59893 /**
59894  * @class Ext.menu.BaseItem
59895  * @extends Ext.Component
59896  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
59897  * management and base configuration options shared by all menu components.
59898  * @constructor
59899  * Creates a new BaseItem
59900  * @param {Object} config Configuration options
59901  * @xtype menubaseitem
59902  */
59903 Ext.menu.BaseItem = Ext.extend(Ext.Component, {
59904     /**
59905      * @property parentMenu
59906      * @type Ext.menu.Menu
59907      * The parent Menu of this Item.
59908      */
59909     /**
59910      * @cfg {Function} handler
59911      * A function that will handle the click event of this menu item (optional).
59912      * The handler is passed the following parameters:<div class="mdetail-params"><ul>
59913      * <li><code>b</code> : Item<div class="sub-desc">This menu Item.</div></li>
59914      * <li><code>e</code> : EventObject<div class="sub-desc">The click event.</div></li>
59915      * </ul></div>
59916      */
59917     /**
59918      * @cfg {Object} scope
59919      * The scope (<tt><b>this</b></tt> reference) in which the handler function will be called.
59920      */
59921     /**
59922      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
59923      */
59924     canActivate : false,
59925     /**
59926      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
59927      */
59928     activeClass : "x-menu-item-active",
59929     /**
59930      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
59931      */
59932     hideOnClick : true,
59933     /**
59934      * @cfg {Number} clickHideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 1)
59935      */
59936     clickHideDelay : 1,
59937
59938     // private
59939     ctype : "Ext.menu.BaseItem",
59940
59941     // private
59942     actionMode : "container",
59943
59944     initComponent : function(){
59945         Ext.menu.BaseItem.superclass.initComponent.call(this);
59946         this.addEvents(
59947             /**
59948              * @event click
59949              * Fires when this item is clicked
59950              * @param {Ext.menu.BaseItem} this
59951              * @param {Ext.EventObject} e
59952              */
59953             'click',
59954             /**
59955              * @event activate
59956              * Fires when this item is activated
59957              * @param {Ext.menu.BaseItem} this
59958              */
59959             'activate',
59960             /**
59961              * @event deactivate
59962              * Fires when this item is deactivated
59963              * @param {Ext.menu.BaseItem} this
59964              */
59965             'deactivate'
59966         );
59967         if(this.handler){
59968             this.on("click", this.handler, this.scope);
59969         }
59970     },
59971
59972     // private
59973     onRender : function(container, position){
59974         Ext.menu.BaseItem.superclass.onRender.apply(this, arguments);
59975         if(this.ownerCt && this.ownerCt instanceof Ext.menu.Menu){
59976             this.parentMenu = this.ownerCt;
59977         }else{
59978             this.container.addClass('x-menu-list-item');
59979             this.mon(this.el, {
59980                 scope: this,
59981                 click: this.onClick,
59982                 mouseenter: this.activate,
59983                 mouseleave: this.deactivate
59984             });
59985         }
59986     },
59987
59988     /**
59989      * Sets the function that will handle click events for this item (equivalent to passing in the {@link #handler}
59990      * config property).  If an existing handler is already registered, it will be unregistered for you.
59991      * @param {Function} handler The function that should be called on click
59992      * @param {Object} scope The scope (<code>this</code> reference) in which the handler function is executed. Defaults to this menu item.
59993      */
59994     setHandler : function(handler, scope){
59995         if(this.handler){
59996             this.un("click", this.handler, this.scope);
59997         }
59998         this.on("click", this.handler = handler, this.scope = scope);
59999     },
60000
60001     // private
60002     onClick : function(e){
60003         if(!this.disabled && this.fireEvent("click", this, e) !== false
60004                 && (this.parentMenu && this.parentMenu.fireEvent("itemclick", this, e) !== false)){
60005             this.handleClick(e);
60006         }else{
60007             e.stopEvent();
60008         }
60009     },
60010
60011     // private
60012     activate : function(){
60013         if(this.disabled){
60014             return false;
60015         }
60016         var li = this.container;
60017         li.addClass(this.activeClass);
60018         this.region = li.getRegion().adjust(2, 2, -2, -2);
60019         this.fireEvent("activate", this);
60020         return true;
60021     },
60022
60023     // private
60024     deactivate : function(){
60025         this.container.removeClass(this.activeClass);
60026         this.fireEvent("deactivate", this);
60027     },
60028
60029     // private
60030     shouldDeactivate : function(e){
60031         return !this.region || !this.region.contains(e.getPoint());
60032     },
60033
60034     // private
60035     handleClick : function(e){
60036         var pm = this.parentMenu;
60037         if(this.hideOnClick){
60038             if(pm.floating){
60039                 pm.hide.defer(this.clickHideDelay, pm, [true]);
60040             }else{
60041                 pm.deactivateActive();
60042             }
60043         }
60044     },
60045
60046     // private. Do nothing
60047     expandMenu : Ext.emptyFn,
60048
60049     // private. Do nothing
60050     hideMenu : Ext.emptyFn
60051 });
60052 Ext.reg('menubaseitem', Ext.menu.BaseItem);/**
60053  * @class Ext.menu.TextItem
60054  * @extends Ext.menu.BaseItem
60055  * Adds a static text string to a menu, usually used as either a heading or group separator.
60056  * @constructor
60057  * Creates a new TextItem
60058  * @param {Object/String} config If config is a string, it is used as the text to display, otherwise it
60059  * is applied as a config object (and should contain a <tt>text</tt> property).
60060  * @xtype menutextitem
60061  */
60062 Ext.menu.TextItem = Ext.extend(Ext.menu.BaseItem, {
60063     /**
60064      * @cfg {String} text The text to display for this item (defaults to '')
60065      */
60066     /**
60067      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
60068      */
60069     hideOnClick : false,
60070     /**
60071      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
60072      */
60073     itemCls : "x-menu-text",
60074     
60075     constructor : function(config) {
60076         if (typeof config == 'string') {
60077             config = {
60078                 text: config
60079             };
60080         }
60081         Ext.menu.TextItem.superclass.constructor.call(this, config);
60082     },
60083
60084     // private
60085     onRender : function() {
60086         var s = document.createElement("span");
60087         s.className = this.itemCls;
60088         s.innerHTML = this.text;
60089         this.el = s;
60090         Ext.menu.TextItem.superclass.onRender.apply(this, arguments);
60091     }
60092 });
60093 Ext.reg('menutextitem', Ext.menu.TextItem);/**
60094  * @class Ext.menu.Separator
60095  * @extends Ext.menu.BaseItem
60096  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
60097  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
60098  * @constructor
60099  * @param {Object} config Configuration options
60100  * @xtype menuseparator
60101  */
60102 Ext.menu.Separator = Ext.extend(Ext.menu.BaseItem, {
60103     /**
60104      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
60105      */
60106     itemCls : "x-menu-sep",
60107     /**
60108      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
60109      */
60110     hideOnClick : false,
60111     
60112     /** 
60113      * @cfg {String} activeClass
60114      * @hide 
60115      */
60116     activeClass: '',
60117
60118     // private
60119     onRender : function(li){
60120         var s = document.createElement("span");
60121         s.className = this.itemCls;
60122         s.innerHTML = "&#160;";
60123         this.el = s;
60124         li.addClass("x-menu-sep-li");
60125         Ext.menu.Separator.superclass.onRender.apply(this, arguments);
60126     }
60127 });
60128 Ext.reg('menuseparator', Ext.menu.Separator);/**
60129  * @class Ext.menu.Item
60130  * @extends Ext.menu.BaseItem
60131  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
60132  * display items.  Item extends the base functionality of {@link Ext.menu.BaseItem} by adding menu-specific
60133  * activation and click handling.
60134  * @constructor
60135  * Creates a new Item
60136  * @param {Object} config Configuration options
60137  * @xtype menuitem
60138  */
60139 Ext.menu.Item = Ext.extend(Ext.menu.BaseItem, {
60140     /**
60141      * @property menu
60142      * @type Ext.menu.Menu
60143      * The submenu associated with this Item if one was configured.
60144      */
60145     /**
60146      * @cfg {Mixed} menu (optional) Either an instance of {@link Ext.menu.Menu} or the config object for an
60147      * {@link Ext.menu.Menu} which acts as the submenu when this item is activated.
60148      */
60149     /**
60150      * @cfg {String} icon The path to an icon to display in this item (defaults to Ext.BLANK_IMAGE_URL).  If
60151      * icon is specified {@link #iconCls} should not be.
60152      */
60153     /**
60154      * @cfg {String} iconCls A CSS class that specifies a background image that will be used as the icon for
60155      * this item (defaults to '').  If iconCls is specified {@link #icon} should not be.
60156      */
60157     /**
60158      * @cfg {String} text The text to display in this item (defaults to '').
60159      */
60160     /**
60161      * @cfg {String} href The href attribute to use for the underlying anchor link (defaults to '#').
60162      */
60163     /**
60164      * @cfg {String} hrefTarget The target attribute to use for the underlying anchor link (defaults to '').
60165      */
60166     /**
60167      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to 'x-menu-item')
60168      */
60169     itemCls : 'x-menu-item',
60170     /**
60171      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
60172      */
60173     canActivate : true,
60174     /**
60175      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
60176      */
60177     showDelay: 200,
60178     
60179     /**
60180      * @cfg {String} altText The altText to use for the icon, if it exists. Defaults to <tt>''</tt>.
60181      */
60182     altText: '',
60183     
60184     // doc'd in BaseItem
60185     hideDelay: 200,
60186
60187     // private
60188     ctype: 'Ext.menu.Item',
60189
60190     initComponent : function(){
60191         Ext.menu.Item.superclass.initComponent.call(this);
60192         if(this.menu){
60193             this.menu = Ext.menu.MenuMgr.get(this.menu);
60194             this.menu.ownerCt = this;
60195         }
60196     },
60197
60198     // private
60199     onRender : function(container, position){
60200         if (!this.itemTpl) {
60201             this.itemTpl = Ext.menu.Item.prototype.itemTpl = new Ext.XTemplate(
60202                 '<a id="{id}" class="{cls}" hidefocus="true" unselectable="on" href="{href}"',
60203                     '<tpl if="hrefTarget">',
60204                         ' target="{hrefTarget}"',
60205                     '</tpl>',
60206                  '>',
60207                      '<img alt="{altText}" src="{icon}" class="x-menu-item-icon {iconCls}"/>',
60208                      '<span class="x-menu-item-text">{text}</span>',
60209                  '</a>'
60210              );
60211         }
60212         var a = this.getTemplateArgs();
60213         this.el = position ? this.itemTpl.insertBefore(position, a, true) : this.itemTpl.append(container, a, true);
60214         this.iconEl = this.el.child('img.x-menu-item-icon');
60215         this.textEl = this.el.child('.x-menu-item-text');
60216         if(!this.href) { // if no link defined, prevent the default anchor event
60217             this.mon(this.el, 'click', Ext.emptyFn, null, { preventDefault: true });
60218         }
60219         Ext.menu.Item.superclass.onRender.call(this, container, position);
60220     },
60221
60222     getTemplateArgs: function() {
60223         return {
60224             id: this.id,
60225             cls: this.itemCls + (this.menu ?  ' x-menu-item-arrow' : '') + (this.cls ?  ' ' + this.cls : ''),
60226             href: this.href || '#',
60227             hrefTarget: this.hrefTarget,
60228             icon: this.icon || Ext.BLANK_IMAGE_URL,
60229             iconCls: this.iconCls || '',
60230             text: this.itemText||this.text||'&#160;',
60231             altText: this.altText || ''
60232         };
60233     },
60234
60235     /**
60236      * Sets the text to display in this menu item
60237      * @param {String} text The text to display
60238      */
60239     setText : function(text){
60240         this.text = text||'&#160;';
60241         if(this.rendered){
60242             this.textEl.update(this.text);
60243             this.parentMenu.layout.doAutoSize();
60244         }
60245     },
60246
60247     /**
60248      * Sets the CSS class to apply to the item's icon element
60249      * @param {String} cls The CSS class to apply
60250      */
60251     setIconClass : function(cls){
60252         var oldCls = this.iconCls;
60253         this.iconCls = cls;
60254         if(this.rendered){
60255             this.iconEl.replaceClass(oldCls, this.iconCls);
60256         }
60257     },
60258
60259     //private
60260     beforeDestroy: function(){
60261         if (this.menu){
60262             delete this.menu.ownerCt;
60263             this.menu.destroy();
60264         }
60265         Ext.menu.Item.superclass.beforeDestroy.call(this);
60266     },
60267
60268     // private
60269     handleClick : function(e){
60270         if(!this.href){ // if no link defined, stop the event automatically
60271             e.stopEvent();
60272         }
60273         Ext.menu.Item.superclass.handleClick.apply(this, arguments);
60274     },
60275
60276     // private
60277     activate : function(autoExpand){
60278         if(Ext.menu.Item.superclass.activate.apply(this, arguments)){
60279             this.focus();
60280             if(autoExpand){
60281                 this.expandMenu();
60282             }
60283         }
60284         return true;
60285     },
60286
60287     // private
60288     shouldDeactivate : function(e){
60289         if(Ext.menu.Item.superclass.shouldDeactivate.call(this, e)){
60290             if(this.menu && this.menu.isVisible()){
60291                 return !this.menu.getEl().getRegion().contains(e.getPoint());
60292             }
60293             return true;
60294         }
60295         return false;
60296     },
60297
60298     // private
60299     deactivate : function(){
60300         Ext.menu.Item.superclass.deactivate.apply(this, arguments);
60301         this.hideMenu();
60302     },
60303
60304     // private
60305     expandMenu : function(autoActivate){
60306         if(!this.disabled && this.menu){
60307             clearTimeout(this.hideTimer);
60308             delete this.hideTimer;
60309             if(!this.menu.isVisible() && !this.showTimer){
60310                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
60311             }else if (this.menu.isVisible() && autoActivate){
60312                 this.menu.tryActivate(0, 1);
60313             }
60314         }
60315     },
60316
60317     // private
60318     deferExpand : function(autoActivate){
60319         delete this.showTimer;
60320         this.menu.show(this.container, this.parentMenu.subMenuAlign || 'tl-tr?', this.parentMenu);
60321         if(autoActivate){
60322             this.menu.tryActivate(0, 1);
60323         }
60324     },
60325
60326     // private
60327     hideMenu : function(){
60328         clearTimeout(this.showTimer);
60329         delete this.showTimer;
60330         if(!this.hideTimer && this.menu && this.menu.isVisible()){
60331             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
60332         }
60333     },
60334
60335     // private
60336     deferHide : function(){
60337         delete this.hideTimer;
60338         if(this.menu.over){
60339             this.parentMenu.setActiveItem(this, false);
60340         }else{
60341             this.menu.hide();
60342         }
60343     }
60344 });
60345 Ext.reg('menuitem', Ext.menu.Item);/**
60346  * @class Ext.menu.CheckItem
60347  * @extends Ext.menu.Item
60348  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
60349  * @constructor
60350  * Creates a new CheckItem
60351  * @param {Object} config Configuration options
60352  * @xtype menucheckitem
60353  */
60354 Ext.menu.CheckItem = Ext.extend(Ext.menu.Item, {
60355     /**
60356      * @cfg {String} group
60357      * All check items with the same group name will automatically be grouped into a single-select
60358      * radio button group (defaults to '')
60359      */
60360     /**
60361      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
60362      */
60363     itemCls : "x-menu-item x-menu-check-item",
60364     /**
60365      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
60366      */
60367     groupClass : "x-menu-group-item",
60368
60369     /**
60370      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
60371      * if this checkbox is part of a radio group (group = true) only the first item in the group that is
60372      * initialized with checked = true will be rendered as checked.
60373      */
60374     checked: false,
60375
60376     // private
60377     ctype: "Ext.menu.CheckItem",
60378     
60379     initComponent : function(){
60380         Ext.menu.CheckItem.superclass.initComponent.call(this);
60381             this.addEvents(
60382                 /**
60383                  * @event beforecheckchange
60384                  * Fires before the checked value is set, providing an opportunity to cancel if needed
60385                  * @param {Ext.menu.CheckItem} this
60386                  * @param {Boolean} checked The new checked value that will be set
60387                  */
60388                 "beforecheckchange" ,
60389                 /**
60390                  * @event checkchange
60391                  * Fires after the checked value has been set
60392                  * @param {Ext.menu.CheckItem} this
60393                  * @param {Boolean} checked The checked value that was set
60394                  */
60395                 "checkchange"
60396             );
60397             /**
60398              * A function that handles the checkchange event.  The function is undefined by default, but if an implementation
60399              * is provided, it will be called automatically when the checkchange event fires.
60400              * @param {Ext.menu.CheckItem} this
60401              * @param {Boolean} checked The checked value that was set
60402              * @method checkHandler
60403              */
60404             if(this.checkHandler){
60405                 this.on('checkchange', this.checkHandler, this.scope);
60406             }
60407             Ext.menu.MenuMgr.registerCheckable(this);
60408     },
60409
60410     // private
60411     onRender : function(c){
60412         Ext.menu.CheckItem.superclass.onRender.apply(this, arguments);
60413         if(this.group){
60414             this.el.addClass(this.groupClass);
60415         }
60416         if(this.checked){
60417             this.checked = false;
60418             this.setChecked(true, true);
60419         }
60420     },
60421
60422     // private
60423     destroy : function(){
60424         Ext.menu.MenuMgr.unregisterCheckable(this);
60425         Ext.menu.CheckItem.superclass.destroy.apply(this, arguments);
60426     },
60427
60428     /**
60429      * Set the checked state of this item
60430      * @param {Boolean} checked The new checked value
60431      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
60432      */
60433     setChecked : function(state, suppressEvent){
60434         var suppress = suppressEvent === true;
60435         if(this.checked != state && (suppress || this.fireEvent("beforecheckchange", this, state) !== false)){
60436             Ext.menu.MenuMgr.onCheckChange(this, state);
60437             if(this.container){
60438                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
60439             }
60440             this.checked = state;
60441             if(!suppress){
60442                 this.fireEvent("checkchange", this, state);
60443             }
60444         }
60445     },
60446
60447     // private
60448     handleClick : function(e){
60449        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
60450            this.setChecked(!this.checked);
60451        }
60452        Ext.menu.CheckItem.superclass.handleClick.apply(this, arguments);
60453     }
60454 });
60455 Ext.reg('menucheckitem', Ext.menu.CheckItem);/**
60456  * @class Ext.menu.DateMenu
60457  * @extends Ext.menu.Menu
60458  * <p>A menu containing an {@link Ext.DatePicker} Component.</p>
60459  * <p>Notes:</p><div class="mdetail-params"><ul>
60460  * <li>Although not listed here, the <b>constructor</b> for this class
60461  * accepts all of the configuration options of <b>{@link Ext.DatePicker}</b>.</li>
60462  * <li>If subclassing DateMenu, any configuration options for the DatePicker must be
60463  * applied to the <tt><b>initialConfig</b></tt> property of the DateMenu.
60464  * Applying {@link Ext.DatePicker DatePicker} configuration settings to
60465  * <b><tt>this</tt></b> will <b>not</b> affect the DatePicker's configuration.</li>
60466  * </ul></div>
60467  * @xtype datemenu
60468  */
60469  Ext.menu.DateMenu = Ext.extend(Ext.menu.Menu, {
60470     /** 
60471      * @cfg {Boolean} enableScrolling
60472      * @hide 
60473      */
60474     enableScrolling : false,
60475     /**
60476      * @cfg {Function} handler
60477      * Optional. A function that will handle the select event of this menu.
60478      * The handler is passed the following parameters:<div class="mdetail-params"><ul>
60479      * <li><code>picker</code> : DatePicker<div class="sub-desc">The Ext.DatePicker.</div></li>
60480      * <li><code>date</code> : Date<div class="sub-desc">The selected date.</div></li>
60481      * </ul></div>
60482      */
60483     /**
60484      * @cfg {Object} scope
60485      * The scope (<tt><b>this</b></tt> reference) in which the <code>{@link #handler}</code>
60486      * function will be called.  Defaults to this DateMenu instance.
60487      */    
60488     /** 
60489      * @cfg {Boolean} hideOnClick
60490      * False to continue showing the menu after a date is selected, defaults to true.
60491      */
60492     hideOnClick : true,
60493     
60494     /** 
60495      * @cfg {String} pickerId
60496      * An id to assign to the underlying date picker. Defaults to <tt>null</tt>.
60497      */
60498     pickerId : null,
60499     
60500     /** 
60501      * @cfg {Number} maxHeight
60502      * @hide 
60503      */
60504     /** 
60505      * @cfg {Number} scrollIncrement
60506      * @hide 
60507      */
60508     /**
60509      * The {@link Ext.DatePicker} instance for this DateMenu
60510      * @property picker
60511      * @type DatePicker
60512      */
60513     cls : 'x-date-menu',
60514     
60515     /**
60516      * @event click
60517      * @hide
60518      */
60519     
60520     /**
60521      * @event itemclick
60522      * @hide
60523      */
60524
60525     initComponent : function(){
60526         this.on('beforeshow', this.onBeforeShow, this);
60527         if(this.strict = (Ext.isIE7 && Ext.isStrict)){
60528             this.on('show', this.onShow, this, {single: true, delay: 20});
60529         }
60530         Ext.apply(this, {
60531             plain: true,
60532             showSeparator: false,
60533             items: this.picker = new Ext.DatePicker(Ext.applyIf({
60534                 internalRender: this.strict || !Ext.isIE,
60535                 ctCls: 'x-menu-date-item',
60536                 id: this.pickerId
60537             }, this.initialConfig))
60538         });
60539         this.picker.purgeListeners();
60540         Ext.menu.DateMenu.superclass.initComponent.call(this);
60541         /**
60542          * @event select
60543          * Fires when a date is selected from the {@link #picker Ext.DatePicker}
60544          * @param {DatePicker} picker The {@link #picker Ext.DatePicker}
60545          * @param {Date} date The selected date
60546          */
60547         this.relayEvents(this.picker, ['select']);
60548         this.on('show', this.picker.focus, this.picker);
60549         this.on('select', this.menuHide, this);
60550         if(this.handler){
60551             this.on('select', this.handler, this.scope || this);
60552         }
60553     },
60554
60555     menuHide : function() {
60556         if(this.hideOnClick){
60557             this.hide(true);
60558         }
60559     },
60560
60561     onBeforeShow : function(){
60562         if(this.picker){
60563             this.picker.hideMonthPicker(true);
60564         }
60565     },
60566
60567     onShow : function(){
60568         var el = this.picker.getEl();
60569         el.setWidth(el.getWidth()); //nasty hack for IE7 strict mode
60570     }
60571  });
60572  Ext.reg('datemenu', Ext.menu.DateMenu);
60573  /**
60574  * @class Ext.menu.ColorMenu
60575  * @extends Ext.menu.Menu
60576  * <p>A menu containing a {@link Ext.ColorPalette} Component.</p>
60577  * <p>Notes:</p><div class="mdetail-params"><ul>
60578  * <li>Although not listed here, the <b>constructor</b> for this class
60579  * accepts all of the configuration options of <b>{@link Ext.ColorPalette}</b>.</li>
60580  * <li>If subclassing ColorMenu, any configuration options for the ColorPalette must be
60581  * applied to the <tt><b>initialConfig</b></tt> property of the ColorMenu.
60582  * Applying {@link Ext.ColorPalette ColorPalette} configuration settings to
60583  * <b><tt>this</tt></b> will <b>not</b> affect the ColorPalette's configuration.</li>
60584  * </ul></div> * 
60585  * @xtype colormenu
60586  */
60587  Ext.menu.ColorMenu = Ext.extend(Ext.menu.Menu, {
60588     /** 
60589      * @cfg {Boolean} enableScrolling
60590      * @hide 
60591      */
60592     enableScrolling : false,
60593     /**
60594      * @cfg {Function} handler
60595      * Optional. A function that will handle the select event of this menu.
60596      * The handler is passed the following parameters:<div class="mdetail-params"><ul>
60597      * <li><code>palette</code> : ColorPalette<div class="sub-desc">The {@link #palette Ext.ColorPalette}.</div></li>
60598      * <li><code>color</code> : String<div class="sub-desc">The 6-digit color hex code (without the # symbol).</div></li>
60599      * </ul></div>
60600      */
60601     /**
60602      * @cfg {Object} scope
60603      * The scope (<tt><b>this</b></tt> reference) in which the <code>{@link #handler}</code>
60604      * function will be called.  Defaults to this ColorMenu instance.
60605      */    
60606     
60607     /** 
60608      * @cfg {Boolean} hideOnClick
60609      * False to continue showing the menu after a color is selected, defaults to true.
60610      */
60611     hideOnClick : true,
60612     
60613     cls : 'x-color-menu',
60614     
60615     /** 
60616      * @cfg {String} paletteId
60617      * An id to assign to the underlying color palette. Defaults to <tt>null</tt>.
60618      */
60619     paletteId : null,
60620     
60621     /** 
60622      * @cfg {Number} maxHeight
60623      * @hide 
60624      */
60625     /** 
60626      * @cfg {Number} scrollIncrement
60627      * @hide 
60628      */
60629     /**
60630      * @property palette
60631      * @type ColorPalette
60632      * The {@link Ext.ColorPalette} instance for this ColorMenu
60633      */
60634     
60635     
60636     /**
60637      * @event click
60638      * @hide
60639      */
60640     
60641     /**
60642      * @event itemclick
60643      * @hide
60644      */
60645     
60646     initComponent : function(){
60647         Ext.apply(this, {
60648             plain: true,
60649             showSeparator: false,
60650             items: this.palette = new Ext.ColorPalette(Ext.applyIf({
60651                 id: this.paletteId
60652             }, this.initialConfig))
60653         });
60654         this.palette.purgeListeners();
60655         Ext.menu.ColorMenu.superclass.initComponent.call(this);
60656         /**
60657          * @event select
60658          * Fires when a color is selected from the {@link #palette Ext.ColorPalette}
60659          * @param {Ext.ColorPalette} palette The {@link #palette Ext.ColorPalette}
60660              * @param {String} color The 6-digit color hex code (without the # symbol)
60661          */
60662         this.relayEvents(this.palette, ['select']);
60663         this.on('select', this.menuHide, this);
60664         if(this.handler){
60665             this.on('select', this.handler, this.scope || this);
60666         }
60667     },
60668
60669     menuHide : function(){
60670         if(this.hideOnClick){
60671             this.hide(true);
60672         }
60673     }
60674 });
60675 Ext.reg('colormenu', Ext.menu.ColorMenu);
60676 /**
60677  * @class Ext.form.Field
60678  * @extends Ext.BoxComponent
60679  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
60680  * @constructor
60681  * Creates a new Field
60682  * @param {Object} config Configuration options
60683  * @xtype field
60684  */
60685 Ext.form.Field = Ext.extend(Ext.BoxComponent,  {
60686     /**
60687      * <p>The label Element associated with this Field. <b>Only available after this Field has been rendered by a
60688      * {@link form Ext.layout.FormLayout} layout manager.</b></p>
60689      * @type Ext.Element
60690      * @property label
60691      */
60692     /**
60693      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password, file (defaults
60694      * to 'text'). The types 'file' and 'password' must be used to render those field types currently -- there are
60695      * no separate Ext components for those. Note that if you use <tt>inputType:'file'</tt>, {@link #emptyText}
60696      * is not supported and should be avoided.
60697      */
60698     /**
60699      * @cfg {Number} tabIndex The tabIndex for this field. Note this only applies to fields that are rendered,
60700      * not those which are built via applyTo (defaults to undefined).
60701      */
60702     /**
60703      * @cfg {Mixed} value A value to initialize this field with (defaults to undefined).
60704      */
60705     /**
60706      * @cfg {String} name The field's HTML name attribute (defaults to '').
60707      * <b>Note</b>: this property must be set if this field is to be automatically included with
60708      * {@link Ext.form.BasicForm#submit form submit()}.
60709      */
60710     /**
60711      * @cfg {String} cls A custom CSS class to apply to the field's underlying element (defaults to '').
60712      */
60713
60714     /**
60715      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to 'x-form-invalid')
60716      */
60717     invalidClass : 'x-form-invalid',
60718     /**
60719      * @cfg {String} invalidText The error text to use when marking a field invalid and no message is provided
60720      * (defaults to 'The value in this field is invalid')
60721      */
60722     invalidText : 'The value in this field is invalid',
60723     /**
60724      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to 'x-form-focus')
60725      */
60726     focusClass : 'x-form-focus',
60727     /**
60728      * @cfg {Boolean} preventMark
60729      * <tt>true</tt> to disable {@link #markInvalid marking the field invalid}.
60730      * Defaults to <tt>false</tt>.
60731      */
60732     /**
60733      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
60734       automatic validation (defaults to 'keyup').
60735      */
60736     validationEvent : 'keyup',
60737     /**
60738      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
60739      */
60740     validateOnBlur : true,
60741     /**
60742      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation
60743      * is initiated (defaults to 250)
60744      */
60745     validationDelay : 250,
60746     /**
60747      * @cfg {String/Object} autoCreate <p>A {@link Ext.DomHelper DomHelper} element spec, or true for a default
60748      * element spec. Used to create the {@link Ext.Component#getEl Element} which will encapsulate this Component.
60749      * See <tt>{@link Ext.Component#autoEl autoEl}</tt> for details.  Defaults to:</p>
60750      * <pre><code>{tag: 'input', type: 'text', size: '20', autocomplete: 'off'}</code></pre>
60751      */
60752     defaultAutoCreate : {tag: 'input', type: 'text', size: '20', autocomplete: 'off'},
60753     /**
60754      * @cfg {String} fieldClass The default CSS class for the field (defaults to 'x-form-field')
60755      */
60756     fieldClass : 'x-form-field',
60757     /**
60758      * @cfg {String} msgTarget <p>The location where the message text set through {@link #markInvalid} should display.
60759      * Must be one of the following values:</p>
60760      * <div class="mdetail-params"><ul>
60761      * <li><code>qtip</code> Display a quick tip containing the message when the user hovers over the field. This is the default.
60762      * <div class="subdesc"><b>{@link Ext.QuickTips#init Ext.QuickTips.init} must have been called for this setting to work.</b></div</li>
60763      * <li><code>title</code> Display the message in a default browser title attribute popup.</li>
60764      * <li><code>under</code> Add a block div beneath the field containing the error message.</li>
60765      * <li><code>side</code> Add an error icon to the right of the field, displaying the message in a popup on hover.</li>
60766      * <li><code>[element id]</code> Add the error message directly to the innerHTML of the specified element.</li>
60767      * </ul></div>
60768      */
60769     msgTarget : 'qtip',
60770     /**
60771      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field
60772      * (defaults to 'normal').
60773      */
60774     msgFx : 'normal',
60775     /**
60776      * @cfg {Boolean} readOnly <tt>true</tt> to mark the field as readOnly in HTML
60777      * (defaults to <tt>false</tt>).
60778      * <br><p><b>Note</b>: this only sets the element's readOnly DOM attribute.
60779      * Setting <code>readOnly=true</code>, for example, will not disable triggering a
60780      * ComboBox or DateField; it gives you the option of forcing the user to choose
60781      * via the trigger without typing in the text box. To hide the trigger use
60782      * <code>{@link Ext.form.TriggerField#hideTrigger hideTrigger}</code>.</p>
60783      */
60784     readOnly : false,
60785     /**
60786      * @cfg {Boolean} disabled True to disable the field (defaults to false).
60787      * <p>Be aware that conformant with the <a href="http://www.w3.org/TR/html401/interact/forms.html#h-17.12.1">HTML specification</a>,
60788      * disabled Fields will not be {@link Ext.form.BasicForm#submit submitted}.</p>
60789      */
60790     disabled : false,
60791     /**
60792      * @cfg {Boolean} submitValue False to clear the name attribute on the field so that it is not submitted during a form post.
60793      * Defaults to <tt>true</tt>.
60794      */
60795     submitValue: true,
60796
60797     // private
60798     isFormField : true,
60799
60800     // private
60801     msgDisplay: '',
60802
60803     // private
60804     hasFocus : false,
60805
60806     // private
60807     initComponent : function(){
60808         Ext.form.Field.superclass.initComponent.call(this);
60809         this.addEvents(
60810             /**
60811              * @event focus
60812              * Fires when this field receives input focus.
60813              * @param {Ext.form.Field} this
60814              */
60815             'focus',
60816             /**
60817              * @event blur
60818              * Fires when this field loses input focus.
60819              * @param {Ext.form.Field} this
60820              */
60821             'blur',
60822             /**
60823              * @event specialkey
60824              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.
60825              * To handle other keys see {@link Ext.Panel#keys} or {@link Ext.KeyMap}.
60826              * You can check {@link Ext.EventObject#getKey} to determine which key was pressed.
60827              * For example: <pre><code>
60828 var form = new Ext.form.FormPanel({
60829     ...
60830     items: [{
60831             fieldLabel: 'Field 1',
60832             name: 'field1',
60833             allowBlank: false
60834         },{
60835             fieldLabel: 'Field 2',
60836             name: 'field2',
60837             listeners: {
60838                 specialkey: function(field, e){
60839                     // e.HOME, e.END, e.PAGE_UP, e.PAGE_DOWN,
60840                     // e.TAB, e.ESC, arrow keys: e.LEFT, e.RIGHT, e.UP, e.DOWN
60841                     if (e.{@link Ext.EventObject#getKey getKey()} == e.ENTER) {
60842                         var form = field.ownerCt.getForm();
60843                         form.submit();
60844                     }
60845                 }
60846             }
60847         }
60848     ],
60849     ...
60850 });
60851              * </code></pre>
60852              * @param {Ext.form.Field} this
60853              * @param {Ext.EventObject} e The event object
60854              */
60855             'specialkey',
60856             /**
60857              * @event change
60858              * Fires just before the field blurs if the field value has changed.
60859              * @param {Ext.form.Field} this
60860              * @param {Mixed} newValue The new value
60861              * @param {Mixed} oldValue The original value
60862              */
60863             'change',
60864             /**
60865              * @event invalid
60866              * Fires after the field has been marked as invalid.
60867              * @param {Ext.form.Field} this
60868              * @param {String} msg The validation message
60869              */
60870             'invalid',
60871             /**
60872              * @event valid
60873              * Fires after the field has been validated with no errors.
60874              * @param {Ext.form.Field} this
60875              */
60876             'valid'
60877         );
60878     },
60879
60880     /**
60881      * Returns the {@link Ext.form.Field#name name} or {@link Ext.form.ComboBox#hiddenName hiddenName}
60882      * attribute of the field if available.
60883      * @return {String} name The field {@link Ext.form.Field#name name} or {@link Ext.form.ComboBox#hiddenName hiddenName}
60884      */
60885     getName : function(){
60886         return this.rendered && this.el.dom.name ? this.el.dom.name : this.name || this.id || '';
60887     },
60888
60889     // private
60890     onRender : function(ct, position){
60891         if(!this.el){
60892             var cfg = this.getAutoCreate();
60893
60894             if(!cfg.name){
60895                 cfg.name = this.name || this.id;
60896             }
60897             if(this.inputType){
60898                 cfg.type = this.inputType;
60899             }
60900             this.autoEl = cfg;
60901         }
60902         Ext.form.Field.superclass.onRender.call(this, ct, position);
60903         if(this.submitValue === false){
60904             this.el.dom.removeAttribute('name');
60905         }
60906         var type = this.el.dom.type;
60907         if(type){
60908             if(type == 'password'){
60909                 type = 'text';
60910             }
60911             this.el.addClass('x-form-'+type);
60912         }
60913         if(this.readOnly){
60914             this.setReadOnly(true);
60915         }
60916         if(this.tabIndex !== undefined){
60917             this.el.dom.setAttribute('tabIndex', this.tabIndex);
60918         }
60919
60920         this.el.addClass([this.fieldClass, this.cls]);
60921     },
60922
60923     // private
60924     getItemCt : function(){
60925         return this.itemCt;
60926     },
60927
60928     // private
60929     initValue : function(){
60930         if(this.value !== undefined){
60931             this.setValue(this.value);
60932         }else if(!Ext.isEmpty(this.el.dom.value) && this.el.dom.value != this.emptyText){
60933             this.setValue(this.el.dom.value);
60934         }
60935         /**
60936          * The original value of the field as configured in the {@link #value} configuration, or
60937          * as loaded by the last form load operation if the form's {@link Ext.form.BasicForm#trackResetOnLoad trackResetOnLoad}
60938          * setting is <code>true</code>.
60939          * @type mixed
60940          * @property originalValue
60941          */
60942         this.originalValue = this.getValue();
60943     },
60944
60945     /**
60946      * <p>Returns true if the value of this Field has been changed from its original value.
60947      * Will return false if the field is disabled or has not been rendered yet.</p>
60948      * <p>Note that if the owning {@link Ext.form.BasicForm form} was configured with
60949      * {@link Ext.form.BasicForm}.{@link Ext.form.BasicForm#trackResetOnLoad trackResetOnLoad}
60950      * then the <i>original value</i> is updated when the values are loaded by
60951      * {@link Ext.form.BasicForm}.{@link Ext.form.BasicForm#setValues setValues}.</p>
60952      * @return {Boolean} True if this field has been changed from its original value (and
60953      * is not disabled), false otherwise.
60954      */
60955     isDirty : function() {
60956         if(this.disabled || !this.rendered) {
60957             return false;
60958         }
60959         return String(this.getValue()) !== String(this.originalValue);
60960     },
60961
60962     /**
60963      * Sets the read only state of this field.
60964      * @param {Boolean} readOnly Whether the field should be read only.
60965      */
60966     setReadOnly : function(readOnly){
60967         if(this.rendered){
60968             this.el.dom.readOnly = readOnly;
60969         }
60970         this.readOnly = readOnly;
60971     },
60972
60973     // private
60974     afterRender : function(){
60975         Ext.form.Field.superclass.afterRender.call(this);
60976         this.initEvents();
60977         this.initValue();
60978     },
60979
60980     // private
60981     fireKey : function(e){
60982         if(e.isSpecialKey()){
60983             this.fireEvent('specialkey', this, e);
60984         }
60985     },
60986
60987     /**
60988      * Resets the current field value to the originally loaded value and clears any validation messages.
60989      * See {@link Ext.form.BasicForm}.{@link Ext.form.BasicForm#trackResetOnLoad trackResetOnLoad}
60990      */
60991     reset : function(){
60992         this.setValue(this.originalValue);
60993         this.clearInvalid();
60994     },
60995
60996     // private
60997     initEvents : function(){
60998         this.mon(this.el, Ext.EventManager.getKeyEvent(), this.fireKey,  this);
60999         this.mon(this.el, 'focus', this.onFocus, this);
61000
61001         // standardise buffer across all browsers + OS-es for consistent event order.
61002         // (the 10ms buffer for Editors fixes a weird FF/Win editor issue when changing OS window focus)
61003         this.mon(this.el, 'blur', this.onBlur, this, this.inEditor ? {buffer:10} : null);
61004     },
61005
61006     // private
61007     preFocus: Ext.emptyFn,
61008
61009     // private
61010     onFocus : function(){
61011         this.preFocus();
61012         if(this.focusClass){
61013             this.el.addClass(this.focusClass);
61014         }
61015         if(!this.hasFocus){
61016             this.hasFocus = true;
61017             /**
61018              * <p>The value that the Field had at the time it was last focused. This is the value that is passed
61019              * to the {@link #change} event which is fired if the value has been changed when the Field is blurred.</p>
61020              * <p><b>This will be undefined until the Field has been visited.</b> Compare {@link #originalValue}.</p>
61021              * @type mixed
61022              * @property startValue
61023              */
61024             this.startValue = this.getValue();
61025             this.fireEvent('focus', this);
61026         }
61027     },
61028
61029     // private
61030     beforeBlur : Ext.emptyFn,
61031
61032     // private
61033     onBlur : function(){
61034         this.beforeBlur();
61035         if(this.focusClass){
61036             this.el.removeClass(this.focusClass);
61037         }
61038         this.hasFocus = false;
61039         if(this.validationEvent !== false && (this.validateOnBlur || this.validationEvent == 'blur')){
61040             this.validate();
61041         }
61042         var v = this.getValue();
61043         if(String(v) !== String(this.startValue)){
61044             this.fireEvent('change', this, v, this.startValue);
61045         }
61046         this.fireEvent('blur', this);
61047         this.postBlur();
61048     },
61049
61050     // private
61051     postBlur : Ext.emptyFn,
61052
61053     /**
61054      * Returns whether or not the field value is currently valid by
61055      * {@link #validateValue validating} the {@link #processValue processed value}
61056      * of the field. <b>Note</b>: {@link #disabled} fields are ignored.
61057      * @param {Boolean} preventMark True to disable marking the field invalid
61058      * @return {Boolean} True if the value is valid, else false
61059      */
61060     isValid : function(preventMark){
61061         if(this.disabled){
61062             return true;
61063         }
61064         var restore = this.preventMark;
61065         this.preventMark = preventMark === true;
61066         var v = this.validateValue(this.processValue(this.getRawValue()));
61067         this.preventMark = restore;
61068         return v;
61069     },
61070
61071     /**
61072      * Validates the field value
61073      * @return {Boolean} True if the value is valid, else false
61074      */
61075     validate : function(){
61076         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
61077             this.clearInvalid();
61078             return true;
61079         }
61080         return false;
61081     },
61082
61083     /**
61084      * This method should only be overridden if necessary to prepare raw values
61085      * for validation (see {@link #validate} and {@link #isValid}).  This method
61086      * is expected to return the processed value for the field which will
61087      * be used for validation (see validateValue method).
61088      * @param {Mixed} value
61089      */
61090     processValue : function(value){
61091         return value;
61092     },
61093
61094     /**
61095      * Uses getErrors to build an array of validation errors. If any errors are found, markInvalid is called
61096      * with the first and false is returned, otherwise true is returned. Previously, subclasses were invited
61097      * to provide an implementation of this to process validations - from 3.2 onwards getErrors should be
61098      * overridden instead.
61099      * @param {Mixed} The current value of the field
61100      * @return {Boolean} True if all validations passed, false if one or more failed
61101      */
61102      validateValue : function(value) {
61103          //currently, we only show 1 error at a time for a field, so just use the first one
61104          var error = this.getErrors(value)[0];
61105
61106          if (error == undefined) {
61107              return true;
61108          } else {
61109              this.markInvalid(error);
61110              return false;
61111          }
61112      },
61113     
61114     /**
61115      * Runs this field's validators and returns an array of error messages for any validation failures.
61116      * This is called internally during validation and would not usually need to be used manually.
61117      * Each subclass should override or augment the return value to provide their own errors
61118      * @return {Array} All error messages for this field
61119      */
61120     getErrors: function() {
61121         return [];
61122     },
61123
61124     /**
61125      * Gets the active error message for this field.
61126      * @return {String} Returns the active error message on the field, if there is no error, an empty string is returned.
61127      */
61128     getActiveError : function(){
61129         return this.activeError || '';
61130     },
61131
61132     /**
61133      * <p>Display an error message associated with this field, using {@link #msgTarget} to determine how to
61134      * display the message and applying {@link #invalidClass} to the field's UI element.</p>
61135      * <p><b>Note</b>: this method does not cause the Field's {@link #validate} method to return <code>false</code>
61136      * if the value does <i>pass</i> validation. So simply marking a Field as invalid will not prevent
61137      * submission of forms submitted with the {@link Ext.form.Action.Submit#clientValidation} option set.</p>
61138      * {@link #isValid invalid}.
61139      * @param {String} msg (optional) The validation message (defaults to {@link #invalidText})
61140      */
61141     markInvalid : function(msg){
61142         //don't set the error icon if we're not rendered or marking is prevented
61143         if (this.rendered && !this.preventMark) {
61144             msg = msg || this.invalidText;
61145
61146             var mt = this.getMessageHandler();
61147             if(mt){
61148                 mt.mark(this, msg);
61149             }else if(this.msgTarget){
61150                 this.el.addClass(this.invalidClass);
61151                 var t = Ext.getDom(this.msgTarget);
61152                 if(t){
61153                     t.innerHTML = msg;
61154                     t.style.display = this.msgDisplay;
61155                 }
61156             }
61157         }
61158         
61159         this.setActiveError(msg);
61160     },
61161     
61162     /**
61163      * Clear any invalid styles/messages for this field
61164      */
61165     clearInvalid : function(){
61166         //don't remove the error icon if we're not rendered or marking is prevented
61167         if (this.rendered && !this.preventMark) {
61168             this.el.removeClass(this.invalidClass);
61169             var mt = this.getMessageHandler();
61170             if(mt){
61171                 mt.clear(this);
61172             }else if(this.msgTarget){
61173                 this.el.removeClass(this.invalidClass);
61174                 var t = Ext.getDom(this.msgTarget);
61175                 if(t){
61176                     t.innerHTML = '';
61177                     t.style.display = 'none';
61178                 }
61179             }
61180         }
61181         
61182         this.unsetActiveError();
61183     },
61184
61185     /**
61186      * Sets the current activeError to the given string. Fires the 'invalid' event.
61187      * This does not set up the error icon, only sets the message and fires the event. To show the error icon,
61188      * use markInvalid instead, which calls this method internally
61189      * @param {String} msg The error message
61190      * @param {Boolean} suppressEvent True to suppress the 'invalid' event from being fired
61191      */
61192     setActiveError: function(msg, suppressEvent) {
61193         this.activeError = msg;
61194         if (suppressEvent !== true) this.fireEvent('invalid', this, msg);
61195     },
61196     
61197     /**
61198      * Clears the activeError and fires the 'valid' event. This is called internally by clearInvalid and would not
61199      * usually need to be called manually
61200      * @param {Boolean} suppressEvent True to suppress the 'invalid' event from being fired
61201      */
61202     unsetActiveError: function(suppressEvent) {
61203         delete this.activeError;
61204         if (suppressEvent !== true) this.fireEvent('valid', this);
61205     },
61206
61207     // private
61208     getMessageHandler : function(){
61209         return Ext.form.MessageTargets[this.msgTarget];
61210     },
61211
61212     // private
61213     getErrorCt : function(){
61214         return this.el.findParent('.x-form-element', 5, true) || // use form element wrap if available
61215             this.el.findParent('.x-form-field-wrap', 5, true);   // else direct field wrap
61216     },
61217
61218     // Alignment for 'under' target
61219     alignErrorEl : function(){
61220         this.errorEl.setWidth(this.getErrorCt().getWidth(true) - 20);
61221     },
61222
61223     // Alignment for 'side' target
61224     alignErrorIcon : function(){
61225         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
61226     },
61227
61228     /**
61229      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
61230      * @return {Mixed} value The field value
61231      */
61232     getRawValue : function(){
61233         var v = this.rendered ? this.el.getValue() : Ext.value(this.value, '');
61234         if(v === this.emptyText){
61235             v = '';
61236         }
61237         return v;
61238     },
61239
61240     /**
61241      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
61242      * @return {Mixed} value The field value
61243      */
61244     getValue : function(){
61245         if(!this.rendered) {
61246             return this.value;
61247         }
61248         var v = this.el.getValue();
61249         if(v === this.emptyText || v === undefined){
61250             v = '';
61251         }
61252         return v;
61253     },
61254
61255     /**
61256      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
61257      * @param {Mixed} value The value to set
61258      * @return {Mixed} value The field value that is set
61259      */
61260     setRawValue : function(v){
61261         return this.rendered ? (this.el.dom.value = (Ext.isEmpty(v) ? '' : v)) : '';
61262     },
61263
61264     /**
61265      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
61266      * @param {Mixed} value The value to set
61267      * @return {Ext.form.Field} this
61268      */
61269     setValue : function(v){
61270         this.value = v;
61271         if(this.rendered){
61272             this.el.dom.value = (Ext.isEmpty(v) ? '' : v);
61273             this.validate();
61274         }
61275         return this;
61276     },
61277
61278     // private, does not work for all fields
61279     append : function(v){
61280          this.setValue([this.getValue(), v].join(''));
61281     }
61282
61283     /**
61284      * @cfg {Boolean} autoWidth @hide
61285      */
61286     /**
61287      * @cfg {Boolean} autoHeight @hide
61288      */
61289
61290     /**
61291      * @cfg {String} autoEl @hide
61292      */
61293 });
61294
61295
61296 Ext.form.MessageTargets = {
61297     'qtip' : {
61298         mark: function(field, msg){
61299             field.el.addClass(field.invalidClass);
61300             field.el.dom.qtip = msg;
61301             field.el.dom.qclass = 'x-form-invalid-tip';
61302             if(Ext.QuickTips){ // fix for floating editors interacting with DND
61303                 Ext.QuickTips.enable();
61304             }
61305         },
61306         clear: function(field){
61307             field.el.removeClass(field.invalidClass);
61308             field.el.dom.qtip = '';
61309         }
61310     },
61311     'title' : {
61312         mark: function(field, msg){
61313             field.el.addClass(field.invalidClass);
61314             field.el.dom.title = msg;
61315         },
61316         clear: function(field){
61317             field.el.dom.title = '';
61318         }
61319     },
61320     'under' : {
61321         mark: function(field, msg){
61322             field.el.addClass(field.invalidClass);
61323             if(!field.errorEl){
61324                 var elp = field.getErrorCt();
61325                 if(!elp){ // field has no container el
61326                     field.el.dom.title = msg;
61327                     return;
61328                 }
61329                 field.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
61330                 field.on('resize', field.alignErrorEl, field);
61331                 field.on('destroy', function(){
61332                     Ext.destroy(this.errorEl);
61333                 }, field);
61334             }
61335             field.alignErrorEl();
61336             field.errorEl.update(msg);
61337             Ext.form.Field.msgFx[field.msgFx].show(field.errorEl, field);
61338         },
61339         clear: function(field){
61340             field.el.removeClass(field.invalidClass);
61341             if(field.errorEl){
61342                 Ext.form.Field.msgFx[field.msgFx].hide(field.errorEl, field);
61343             }else{
61344                 field.el.dom.title = '';
61345             }
61346         }
61347     },
61348     'side' : {
61349         mark: function(field, msg){
61350             field.el.addClass(field.invalidClass);
61351             if(!field.errorIcon){
61352                 var elp = field.getErrorCt();
61353                 // field has no container el
61354                 if(!elp){
61355                     field.el.dom.title = msg;
61356                     return;
61357                 }
61358                 field.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
61359                 if (field.ownerCt) {
61360                     field.ownerCt.on('afterlayout', field.alignErrorIcon, field);
61361                     field.ownerCt.on('expand', field.alignErrorIcon, field);
61362                 }
61363                 field.on('resize', field.alignErrorIcon, field);
61364                 field.on('destroy', function(){
61365                     Ext.destroy(this.errorIcon);
61366                 }, field);
61367             }
61368             field.alignErrorIcon();
61369             field.errorIcon.dom.qtip = msg;
61370             field.errorIcon.dom.qclass = 'x-form-invalid-tip';
61371             field.errorIcon.show();
61372         },
61373         clear: function(field){
61374             field.el.removeClass(field.invalidClass);
61375             if(field.errorIcon){
61376                 field.errorIcon.dom.qtip = '';
61377                 field.errorIcon.hide();
61378             }else{
61379                 field.el.dom.title = '';
61380             }
61381         }
61382     }
61383 };
61384
61385 // anything other than normal should be considered experimental
61386 Ext.form.Field.msgFx = {
61387     normal : {
61388         show: function(msgEl, f){
61389             msgEl.setDisplayed('block');
61390         },
61391
61392         hide : function(msgEl, f){
61393             msgEl.setDisplayed(false).update('');
61394         }
61395     },
61396
61397     slide : {
61398         show: function(msgEl, f){
61399             msgEl.slideIn('t', {stopFx:true});
61400         },
61401
61402         hide : function(msgEl, f){
61403             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
61404         }
61405     },
61406
61407     slideRight : {
61408         show: function(msgEl, f){
61409             msgEl.fixDisplay();
61410             msgEl.alignTo(f.el, 'tl-tr');
61411             msgEl.slideIn('l', {stopFx:true});
61412         },
61413
61414         hide : function(msgEl, f){
61415             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
61416         }
61417     }
61418 };
61419 Ext.reg('field', Ext.form.Field);
61420 /**
61421  * @class Ext.form.TextField
61422  * @extends Ext.form.Field
61423  * <p>Basic text field.  Can be used as a direct replacement for traditional text inputs,
61424  * or as the base class for more sophisticated input controls (like {@link Ext.form.TextArea}
61425  * and {@link Ext.form.ComboBox}).</p>
61426  * <p><b><u>Validation</u></b></p>
61427  * <p>The validation procedure is described in the documentation for {@link #validateValue}.</p>
61428  * <p><b><u>Alter Validation Behavior</u></b></p>
61429  * <p>Validation behavior for each field can be configured:</p>
61430  * <div class="mdetail-params"><ul>
61431  * <li><code>{@link Ext.form.TextField#invalidText invalidText}</code> : the default validation message to
61432  * show if any validation step above does not provide a message when invalid</li>
61433  * <li><code>{@link Ext.form.TextField#maskRe maskRe}</code> : filter out keystrokes before any validation occurs</li>
61434  * <li><code>{@link Ext.form.TextField#stripCharsRe stripCharsRe}</code> : filter characters after being typed in,
61435  * but before being validated</li>
61436  * <li><code>{@link Ext.form.Field#invalidClass invalidClass}</code> : alternate style when invalid</li>
61437  * <li><code>{@link Ext.form.Field#validateOnBlur validateOnBlur}</code>,
61438  * <code>{@link Ext.form.Field#validationDelay validationDelay}</code>, and
61439  * <code>{@link Ext.form.Field#validationEvent validationEvent}</code> : modify how/when validation is triggered</li>
61440  * </ul></div>
61441  * 
61442  * @constructor Creates a new TextField
61443  * @param {Object} config Configuration options
61444  * 
61445  * @xtype textfield
61446  */
61447 Ext.form.TextField = Ext.extend(Ext.form.Field,  {
61448     /**
61449      * @cfg {String} vtypeText A custom error message to display in place of the default message provided
61450      * for the <b><code>{@link #vtype}</code></b> currently set for this field (defaults to <tt>''</tt>).  <b>Note</b>:
61451      * only applies if <b><code>{@link #vtype}</code></b> is set, else ignored.
61452      */
61453     /**
61454      * @cfg {RegExp} stripCharsRe A JavaScript RegExp object used to strip unwanted content from the value
61455      * before validation (defaults to <tt>null</tt>).
61456      */
61457     /**
61458      * @cfg {Boolean} grow <tt>true</tt> if this field should automatically grow and shrink to its content
61459      * (defaults to <tt>false</tt>)
61460      */
61461     grow : false,
61462     /**
61463      * @cfg {Number} growMin The minimum width to allow when <code><b>{@link #grow}</b> = true</code> (defaults
61464      * to <tt>30</tt>)
61465      */
61466     growMin : 30,
61467     /**
61468      * @cfg {Number} growMax The maximum width to allow when <code><b>{@link #grow}</b> = true</code> (defaults
61469      * to <tt>800</tt>)
61470      */
61471     growMax : 800,
61472     /**
61473      * @cfg {String} vtype A validation type name as defined in {@link Ext.form.VTypes} (defaults to <tt>null</tt>)
61474      */
61475     vtype : null,
61476     /**
61477      * @cfg {RegExp} maskRe An input mask regular expression that will be used to filter keystrokes that do
61478      * not match (defaults to <tt>null</tt>)
61479      */
61480     maskRe : null,
61481     /**
61482      * @cfg {Boolean} disableKeyFilter Specify <tt>true</tt> to disable input keystroke filtering (defaults
61483      * to <tt>false</tt>)
61484      */
61485     disableKeyFilter : false,
61486     /**
61487      * @cfg {Boolean} allowBlank Specify <tt>false</tt> to validate that the value's length is > 0 (defaults to
61488      * <tt>true</tt>)
61489      */
61490     allowBlank : true,
61491     /**
61492      * @cfg {Number} minLength Minimum input field length required (defaults to <tt>0</tt>)
61493      */
61494     minLength : 0,
61495     /**
61496      * @cfg {Number} maxLength Maximum input field length allowed by validation (defaults to Number.MAX_VALUE).
61497      * This behavior is intended to provide instant feedback to the user by improving usability to allow pasting
61498      * and editing or overtyping and back tracking. To restrict the maximum number of characters that can be
61499      * entered into the field use <tt><b>{@link Ext.form.Field#autoCreate autoCreate}</b></tt> to add
61500      * any attributes you want to a field, for example:<pre><code>
61501 var myField = new Ext.form.NumberField({
61502     id: 'mobile',
61503     anchor:'90%',
61504     fieldLabel: 'Mobile',
61505     maxLength: 16, // for validation
61506     autoCreate: {tag: 'input', type: 'text', size: '20', autocomplete: 'off', maxlength: '10'}
61507 });
61508 </code></pre>
61509      */
61510     maxLength : Number.MAX_VALUE,
61511     /**
61512      * @cfg {String} minLengthText Error text to display if the <b><tt>{@link #minLength minimum length}</tt></b>
61513      * validation fails (defaults to <tt>'The minimum length for this field is {minLength}'</tt>)
61514      */
61515     minLengthText : 'The minimum length for this field is {0}',
61516     /**
61517      * @cfg {String} maxLengthText Error text to display if the <b><tt>{@link #maxLength maximum length}</tt></b>
61518      * validation fails (defaults to <tt>'The maximum length for this field is {maxLength}'</tt>)
61519      */
61520     maxLengthText : 'The maximum length for this field is {0}',
61521     /**
61522      * @cfg {Boolean} selectOnFocus <tt>true</tt> to automatically select any existing field text when the field
61523      * receives input focus (defaults to <tt>false</tt>)
61524      */
61525     selectOnFocus : false,
61526     /**
61527      * @cfg {String} blankText The error text to display if the <b><tt>{@link #allowBlank}</tt></b> validation
61528      * fails (defaults to <tt>'This field is required'</tt>)
61529      */
61530     blankText : 'This field is required',
61531     /**
61532      * @cfg {Function} validator
61533      * <p>A custom validation function to be called during field validation ({@link #validateValue})
61534      * (defaults to <tt>null</tt>). If specified, this function will be called first, allowing the
61535      * developer to override the default validation process.</p>
61536      * <br><p>This function will be passed the following Parameters:</p>
61537      * <div class="mdetail-params"><ul>
61538      * <li><code>value</code>: <i>Mixed</i>
61539      * <div class="sub-desc">The current field value</div></li>
61540      * </ul></div>
61541      * <br><p>This function is to Return:</p>
61542      * <div class="mdetail-params"><ul>
61543      * <li><code>true</code>: <i>Boolean</i>
61544      * <div class="sub-desc"><code>true</code> if the value is valid</div></li>
61545      * <li><code>msg</code>: <i>String</i>
61546      * <div class="sub-desc">An error message if the value is invalid</div></li>
61547      * </ul></div>
61548      */
61549     validator : null,
61550     /**
61551      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation
61552      * (defaults to <tt>null</tt>). If the test fails, the field will be marked invalid using
61553      * <b><tt>{@link #regexText}</tt></b>.
61554      */
61555     regex : null,
61556     /**
61557      * @cfg {String} regexText The error text to display if <b><tt>{@link #regex}</tt></b> is used and the
61558      * test fails during validation (defaults to <tt>''</tt>)
61559      */
61560     regexText : '',
61561     /**
61562      * @cfg {String} emptyText The default text to place into an empty field (defaults to <tt>null</tt>).
61563      * <b>Note</b>: that this value will be submitted to the server if this field is enabled and configured
61564      * with a {@link #name}.
61565      */
61566     emptyText : null,
61567     /**
61568      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the <b><tt>{@link #emptyText}</tt></b>
61569      * (defaults to <tt>'x-form-empty-field'</tt>).  This class is automatically added and removed as needed
61570      * depending on the current field value.
61571      */
61572     emptyClass : 'x-form-empty-field',
61573
61574     /**
61575      * @cfg {Boolean} enableKeyEvents <tt>true</tt> to enable the proxying of key events for the HTML input
61576      * field (defaults to <tt>false</tt>)
61577      */
61578
61579     initComponent : function(){
61580         Ext.form.TextField.superclass.initComponent.call(this);
61581         this.addEvents(
61582             /**
61583              * @event autosize
61584              * Fires when the <tt><b>{@link #autoSize}</b></tt> function is triggered. The field may or
61585              * may not have actually changed size according to the default logic, but this event provides
61586              * a hook for the developer to apply additional logic at runtime to resize the field if needed.
61587              * @param {Ext.form.Field} this This text field
61588              * @param {Number} width The new field width
61589              */
61590             'autosize',
61591
61592             /**
61593              * @event keydown
61594              * Keydown input field event. This event only fires if <tt><b>{@link #enableKeyEvents}</b></tt>
61595              * is set to true.
61596              * @param {Ext.form.TextField} this This text field
61597              * @param {Ext.EventObject} e
61598              */
61599             'keydown',
61600             /**
61601              * @event keyup
61602              * Keyup input field event. This event only fires if <tt><b>{@link #enableKeyEvents}</b></tt>
61603              * is set to true.
61604              * @param {Ext.form.TextField} this This text field
61605              * @param {Ext.EventObject} e
61606              */
61607             'keyup',
61608             /**
61609              * @event keypress
61610              * Keypress input field event. This event only fires if <tt><b>{@link #enableKeyEvents}</b></tt>
61611              * is set to true.
61612              * @param {Ext.form.TextField} this This text field
61613              * @param {Ext.EventObject} e
61614              */
61615             'keypress'
61616         );
61617     },
61618
61619     // private
61620     initEvents : function(){
61621         Ext.form.TextField.superclass.initEvents.call(this);
61622         if(this.validationEvent == 'keyup'){
61623             this.validationTask = new Ext.util.DelayedTask(this.validate, this);
61624             this.mon(this.el, 'keyup', this.filterValidation, this);
61625         }
61626         else if(this.validationEvent !== false && this.validationEvent != 'blur'){
61627                 this.mon(this.el, this.validationEvent, this.validate, this, {buffer: this.validationDelay});
61628         }
61629         if(this.selectOnFocus || this.emptyText){            
61630             this.mon(this.el, 'mousedown', this.onMouseDown, this);
61631             
61632             if(this.emptyText){
61633                 this.applyEmptyText();
61634             }
61635         }
61636         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Ext.form.VTypes[this.vtype+'Mask']))){
61637                 this.mon(this.el, 'keypress', this.filterKeys, this);
61638         }
61639         if(this.grow){
61640                 this.mon(this.el, 'keyup', this.onKeyUpBuffered, this, {buffer: 50});
61641                         this.mon(this.el, 'click', this.autoSize, this);
61642         }
61643         if(this.enableKeyEvents){
61644             this.mon(this.el, {
61645                 scope: this,
61646                 keyup: this.onKeyUp,
61647                 keydown: this.onKeyDown,
61648                 keypress: this.onKeyPress
61649             });
61650         }
61651     },
61652     
61653     onMouseDown: function(e){
61654         if(!this.hasFocus){
61655             this.mon(this.el, 'mouseup', Ext.emptyFn, this, { single: true, preventDefault: true });
61656         }
61657     },
61658
61659     processValue : function(value){
61660         if(this.stripCharsRe){
61661             var newValue = value.replace(this.stripCharsRe, '');
61662             if(newValue !== value){
61663                 this.setRawValue(newValue);
61664                 return newValue;
61665             }
61666         }
61667         return value;
61668     },
61669
61670     filterValidation : function(e){
61671         if(!e.isNavKeyPress()){
61672             this.validationTask.delay(this.validationDelay);
61673         }
61674     },
61675     
61676     //private
61677     onDisable: function(){
61678         Ext.form.TextField.superclass.onDisable.call(this);
61679         if(Ext.isIE){
61680             this.el.dom.unselectable = 'on';
61681         }
61682     },
61683     
61684     //private
61685     onEnable: function(){
61686         Ext.form.TextField.superclass.onEnable.call(this);
61687         if(Ext.isIE){
61688             this.el.dom.unselectable = '';
61689         }
61690     },
61691
61692     // private
61693     onKeyUpBuffered : function(e){
61694         if(this.doAutoSize(e)){
61695             this.autoSize();
61696         }
61697     },
61698     
61699     // private
61700     doAutoSize : function(e){
61701         return !e.isNavKeyPress();
61702     },
61703
61704     // private
61705     onKeyUp : function(e){
61706         this.fireEvent('keyup', this, e);
61707     },
61708
61709     // private
61710     onKeyDown : function(e){
61711         this.fireEvent('keydown', this, e);
61712     },
61713
61714     // private
61715     onKeyPress : function(e){
61716         this.fireEvent('keypress', this, e);
61717     },
61718
61719     /**
61720      * Resets the current field value to the originally-loaded value and clears any validation messages.
61721      * Also adds <tt><b>{@link #emptyText}</b></tt> and <tt><b>{@link #emptyClass}</b></tt> if the
61722      * original value was blank.
61723      */
61724     reset : function(){
61725         Ext.form.TextField.superclass.reset.call(this);
61726         this.applyEmptyText();
61727     },
61728
61729     applyEmptyText : function(){
61730         if(this.rendered && this.emptyText && this.getRawValue().length < 1 && !this.hasFocus){
61731             this.setRawValue(this.emptyText);
61732             this.el.addClass(this.emptyClass);
61733         }
61734     },
61735
61736     // private
61737     preFocus : function(){
61738         var el = this.el,
61739             isEmpty;
61740         if(this.emptyText){
61741             if(el.dom.value == this.emptyText){
61742                 this.setRawValue('');
61743                 isEmpty = true;
61744             }
61745             el.removeClass(this.emptyClass);
61746         }
61747         if(this.selectOnFocus || isEmpty){
61748             el.dom.select();
61749         }
61750     },
61751
61752     // private
61753     postBlur : function(){
61754         this.applyEmptyText();
61755     },
61756
61757     // private
61758     filterKeys : function(e){
61759         if(e.ctrlKey){
61760             return;
61761         }
61762         var k = e.getKey();
61763         if(Ext.isGecko && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
61764             return;
61765         }
61766         var cc = String.fromCharCode(e.getCharCode());
61767         if(!Ext.isGecko && e.isSpecialKey() && !cc){
61768             return;
61769         }
61770         if(!this.maskRe.test(cc)){
61771             e.stopEvent();
61772         }
61773     },
61774
61775     setValue : function(v){
61776         if(this.emptyText && this.el && !Ext.isEmpty(v)){
61777             this.el.removeClass(this.emptyClass);
61778         }
61779         Ext.form.TextField.superclass.setValue.apply(this, arguments);
61780         this.applyEmptyText();
61781         this.autoSize();
61782         return this;
61783     },
61784
61785     /**
61786      * <p>Validates a value according to the field's validation rules and returns an array of errors
61787      * for any failing validations. Validation rules are processed in the following order:</p>
61788      * <div class="mdetail-params"><ul>
61789      * 
61790      * <li><b>1. Field specific validator</b>
61791      * <div class="sub-desc">
61792      * <p>A validator offers a way to customize and reuse a validation specification.
61793      * If a field is configured with a <code>{@link #validator}</code>
61794      * function, it will be passed the current field value.  The <code>{@link #validator}</code>
61795      * function is expected to return either:
61796      * <div class="mdetail-params"><ul>
61797      * <li>Boolean <tt>true</tt> if the value is valid (validation continues).</li>
61798      * <li>a String to represent the invalid message if invalid (validation halts).</li>
61799      * </ul></div>
61800      * </div></li>
61801      * 
61802      * <li><b>2. Basic Validation</b>
61803      * <div class="sub-desc">
61804      * <p>If the <code>{@link #validator}</code> has not halted validation,
61805      * basic validation proceeds as follows:</p>
61806      * 
61807      * <div class="mdetail-params"><ul>
61808      * 
61809      * <li><code>{@link #allowBlank}</code> : (Invalid message =
61810      * <code>{@link #emptyText}</code>)<div class="sub-desc">
61811      * Depending on the configuration of <code>{@link #allowBlank}</code>, a
61812      * blank field will cause validation to halt at this step and return
61813      * Boolean true or false accordingly.  
61814      * </div></li>
61815      * 
61816      * <li><code>{@link #minLength}</code> : (Invalid message =
61817      * <code>{@link #minLengthText}</code>)<div class="sub-desc">
61818      * If the passed value does not satisfy the <code>{@link #minLength}</code>
61819      * specified, validation halts.
61820      * </div></li>
61821      * 
61822      * <li><code>{@link #maxLength}</code> : (Invalid message =
61823      * <code>{@link #maxLengthText}</code>)<div class="sub-desc">
61824      * If the passed value does not satisfy the <code>{@link #maxLength}</code>
61825      * specified, validation halts.
61826      * </div></li>
61827      * 
61828      * </ul></div>
61829      * </div></li>
61830      * 
61831      * <li><b>3. Preconfigured Validation Types (VTypes)</b>
61832      * <div class="sub-desc">
61833      * <p>If none of the prior validation steps halts validation, a field
61834      * configured with a <code>{@link #vtype}</code> will utilize the
61835      * corresponding {@link Ext.form.VTypes VTypes} validation function.
61836      * If invalid, either the field's <code>{@link #vtypeText}</code> or
61837      * the VTypes vtype Text property will be used for the invalid message.
61838      * Keystrokes on the field will be filtered according to the VTypes
61839      * vtype Mask property.</p>
61840      * </div></li>
61841      * 
61842      * <li><b>4. Field specific regex test</b>
61843      * <div class="sub-desc">
61844      * <p>If none of the prior validation steps halts validation, a field's
61845      * configured <code>{@link #regex}</code> test will be processed.
61846      * The invalid message for this test is configured with
61847      * <code>{@link #regexText}</code>.</p>
61848      * </div></li>
61849      * 
61850      * @param {Mixed} value The value to validate. The processed raw value will be used if nothing is passed
61851      * @return {Array} Array of any validation errors
61852      */
61853     getErrors: function(value) {
61854         var errors = Ext.form.TextField.superclass.getErrors.apply(this, arguments);
61855         
61856         value = Ext.isDefined(value) ? value : this.processValue(this.getRawValue());        
61857         
61858         if (Ext.isFunction(this.validator)) {
61859             var msg = this.validator(value);
61860             if (msg !== true) {
61861                 errors.push(msg);
61862             }
61863         }
61864         
61865         if (value.length < 1 || value === this.emptyText) {
61866             if (this.allowBlank) {
61867                 //if value is blank and allowBlank is true, there cannot be any additional errors
61868                 return errors;
61869             } else {
61870                 errors.push(this.blankText);
61871             }
61872         }
61873         
61874         if (!this.allowBlank && (value.length < 1 || value === this.emptyText)) { // if it's blank
61875             errors.push(this.blankText);
61876         }
61877         
61878         if (value.length < this.minLength) {
61879             errors.push(String.format(this.minLengthText, this.minLength));
61880         }
61881         
61882         if (value.length > this.maxLength) {
61883             errors.push(String.format(this.maxLengthText, this.maxLength));
61884         }
61885         
61886         if (this.vtype) {
61887             var vt = Ext.form.VTypes;
61888             if(!vt[this.vtype](value, this)){
61889                 errors.push(this.vtypeText || vt[this.vtype +'Text']);
61890             }
61891         }
61892         
61893         if (this.regex && !this.regex.test(value)) {
61894             errors.push(this.regexText);
61895         }
61896         
61897         return errors;
61898     },
61899
61900     /**
61901      * Selects text in this field
61902      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
61903      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
61904      */
61905     selectText : function(start, end){
61906         var v = this.getRawValue();
61907         var doFocus = false;
61908         if(v.length > 0){
61909             start = start === undefined ? 0 : start;
61910             end = end === undefined ? v.length : end;
61911             var d = this.el.dom;
61912             if(d.setSelectionRange){
61913                 d.setSelectionRange(start, end);
61914             }else if(d.createTextRange){
61915                 var range = d.createTextRange();
61916                 range.moveStart('character', start);
61917                 range.moveEnd('character', end-v.length);
61918                 range.select();
61919             }
61920             doFocus = Ext.isGecko || Ext.isOpera;
61921         }else{
61922             doFocus = true;
61923         }
61924         if(doFocus){
61925             this.focus();
61926         }
61927     },
61928
61929     /**
61930      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
61931      * This only takes effect if <tt><b>{@link #grow}</b> = true</tt>, and fires the {@link #autosize} event.
61932      */
61933     autoSize : function(){
61934         if(!this.grow || !this.rendered){
61935             return;
61936         }
61937         if(!this.metrics){
61938             this.metrics = Ext.util.TextMetrics.createInstance(this.el);
61939         }
61940         var el = this.el;
61941         var v = el.dom.value;
61942         var d = document.createElement('div');
61943         d.appendChild(document.createTextNode(v));
61944         v = d.innerHTML;
61945         Ext.removeNode(d);
61946         d = null;
61947         v += '&#160;';
61948         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
61949         this.el.setWidth(w);
61950         this.fireEvent('autosize', this, w);
61951     },
61952         
61953         onDestroy: function(){
61954                 if(this.validationTask){
61955                         this.validationTask.cancel();
61956                         this.validationTask = null;
61957                 }
61958                 Ext.form.TextField.superclass.onDestroy.call(this);
61959         }
61960 });
61961 Ext.reg('textfield', Ext.form.TextField);
61962 /**
61963  * @class Ext.form.TriggerField
61964  * @extends Ext.form.TextField
61965  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
61966  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
61967  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
61968  * for which you can provide a custom implementation.  For example:
61969  * <pre><code>
61970 var trigger = new Ext.form.TriggerField();
61971 trigger.onTriggerClick = myTriggerFn;
61972 trigger.applyToMarkup('my-field');
61973 </code></pre>
61974  *
61975  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
61976  * {@link Ext.form.DateField} and {@link Ext.form.ComboBox} are perfect examples of this.
61977  *
61978  * @constructor
61979  * Create a new TriggerField.
61980  * @param {Object} config Configuration options (valid {@Ext.form.TextField} config options will also be applied
61981  * to the base TextField)
61982  * @xtype trigger
61983  */
61984 Ext.form.TriggerField = Ext.extend(Ext.form.TextField,  {
61985     /**
61986      * @cfg {String} triggerClass
61987      * An additional CSS class used to style the trigger button.  The trigger will always get the
61988      * class <tt>'x-form-trigger'</tt> by default and <tt>triggerClass</tt> will be <b>appended</b> if specified.
61989      */
61990     /**
61991      * @cfg {Mixed} triggerConfig
61992      * <p>A {@link Ext.DomHelper DomHelper} config object specifying the structure of the
61993      * trigger element for this Field. (Optional).</p>
61994      * <p>Specify this when you need a customized element to act as the trigger button for a TriggerField.</p>
61995      * <p>Note that when using this option, it is the developer's responsibility to ensure correct sizing, positioning
61996      * and appearance of the trigger.  Defaults to:</p>
61997      * <pre><code>{tag: "img", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass}</code></pre>
61998      */
61999     /**
62000      * @cfg {String/Object} autoCreate <p>A {@link Ext.DomHelper DomHelper} element spec, or true for a default
62001      * element spec. Used to create the {@link Ext.Component#getEl Element} which will encapsulate this Component.
62002      * See <tt>{@link Ext.Component#autoEl autoEl}</tt> for details.  Defaults to:</p>
62003      * <pre><code>{tag: "input", type: "text", size: "16", autocomplete: "off"}</code></pre>
62004      */
62005     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
62006     /**
62007      * @cfg {Boolean} hideTrigger <tt>true</tt> to hide the trigger element and display only the base
62008      * text field (defaults to <tt>false</tt>)
62009      */
62010     hideTrigger:false,
62011     /**
62012      * @cfg {Boolean} editable <tt>false</tt> to prevent the user from typing text directly into the field,
62013      * the field will only respond to a click on the trigger to set the value. (defaults to <tt>true</tt>).
62014      */
62015     editable: true,
62016     /**
62017      * @cfg {Boolean} readOnly <tt>true</tt> to prevent the user from changing the field, and
62018      * hides the trigger.  Superceeds the editable and hideTrigger options if the value is true.
62019      * (defaults to <tt>false</tt>)
62020      */
62021     readOnly: false,
62022     /**
62023      * @cfg {String} wrapFocusClass The class added to the to the wrap of the trigger element. Defaults to
62024      * <tt>x-trigger-wrap-focus</tt>.
62025      */
62026     wrapFocusClass: 'x-trigger-wrap-focus',
62027     /**
62028      * @hide
62029      * @method autoSize
62030      */
62031     autoSize: Ext.emptyFn,
62032     // private
62033     monitorTab : true,
62034     // private
62035     deferHeight : true,
62036     // private
62037     mimicing : false,
62038
62039     actionMode: 'wrap',
62040
62041     defaultTriggerWidth: 17,
62042
62043     // private
62044     onResize : function(w, h){
62045         Ext.form.TriggerField.superclass.onResize.call(this, w, h);
62046         var tw = this.getTriggerWidth();
62047         if(Ext.isNumber(w)){
62048             this.el.setWidth(w - tw);
62049         }
62050         this.wrap.setWidth(this.el.getWidth() + tw);
62051     },
62052
62053     getTriggerWidth: function(){
62054         var tw = this.trigger.getWidth();
62055         if(!this.hideTrigger && !this.readOnly && tw === 0){
62056             tw = this.defaultTriggerWidth;
62057         }
62058         return tw;
62059     },
62060
62061     // private
62062     alignErrorIcon : function(){
62063         if(this.wrap){
62064             this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
62065         }
62066     },
62067
62068     // private
62069     onRender : function(ct, position){
62070         this.doc = Ext.isIE ? Ext.getBody() : Ext.getDoc();
62071         Ext.form.TriggerField.superclass.onRender.call(this, ct, position);
62072
62073         this.wrap = this.el.wrap({cls: 'x-form-field-wrap x-form-field-trigger-wrap'});
62074         this.trigger = this.wrap.createChild(this.triggerConfig ||
62075                 {tag: "img", src: Ext.BLANK_IMAGE_URL, alt: "", cls: "x-form-trigger " + this.triggerClass});
62076         this.initTrigger();
62077         if(!this.width){
62078             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
62079         }
62080         this.resizeEl = this.positionEl = this.wrap;
62081     },
62082
62083     getWidth: function() {
62084         return(this.el.getWidth() + this.trigger.getWidth());
62085     },
62086
62087     updateEditState: function(){
62088         if(this.rendered){
62089             if (this.readOnly) {
62090                 this.el.dom.readOnly = true;
62091                 this.el.addClass('x-trigger-noedit');
62092                 this.mun(this.el, 'click', this.onTriggerClick, this);
62093                 this.trigger.setDisplayed(false);
62094             } else {
62095                 if (!this.editable) {
62096                     this.el.dom.readOnly = true;
62097                     this.el.addClass('x-trigger-noedit');
62098                     this.mon(this.el, 'click', this.onTriggerClick, this);
62099                 } else {
62100                     this.el.dom.readOnly = false;
62101                     this.el.removeClass('x-trigger-noedit');
62102                     this.mun(this.el, 'click', this.onTriggerClick, this);
62103                 }
62104                 this.trigger.setDisplayed(!this.hideTrigger);
62105             }
62106             this.onResize(this.width || this.wrap.getWidth());
62107         }
62108     },
62109
62110     /**
62111      * Changes the hidden status of the trigger.
62112      * @param {Boolean} hideTrigger True to hide the trigger, false to show it.
62113      */
62114     setHideTrigger: function(hideTrigger){
62115         if(hideTrigger != this.hideTrigger){
62116             this.hideTrigger = hideTrigger;
62117             this.updateEditState();
62118         }
62119     },
62120
62121     /**
62122      * Allow or prevent the user from directly editing the field text.  If false is passed,
62123      * the user will only be able to modify the field using the trigger.  Will also add
62124      * a click event to the text field which will call the trigger. This method
62125      * is the runtime equivalent of setting the {@link #editable} config option at config time.
62126      * @param {Boolean} value True to allow the user to directly edit the field text.
62127      */
62128     setEditable: function(editable){
62129         if(editable != this.editable){
62130             this.editable = editable;
62131             this.updateEditState();
62132         }
62133     },
62134
62135     /**
62136      * Setting this to true will supersede settings {@link #editable} and {@link #hideTrigger}.
62137      * Setting this to false will defer back to {@link #editable} and {@link #hideTrigger}. This method
62138      * is the runtime equivalent of setting the {@link #readOnly} config option at config time.
62139      * @param {Boolean} value True to prevent the user changing the field and explicitly
62140      * hide the trigger.
62141      */
62142     setReadOnly: function(readOnly){
62143         if(readOnly != this.readOnly){
62144             this.readOnly = readOnly;
62145             this.updateEditState();
62146         }
62147     },
62148
62149     afterRender : function(){
62150         Ext.form.TriggerField.superclass.afterRender.call(this);
62151         this.updateEditState();
62152     },
62153
62154     // private
62155     initTrigger : function(){
62156         this.mon(this.trigger, 'click', this.onTriggerClick, this, {preventDefault:true});
62157         this.trigger.addClassOnOver('x-form-trigger-over');
62158         this.trigger.addClassOnClick('x-form-trigger-click');
62159     },
62160
62161     // private
62162     onDestroy : function(){
62163         Ext.destroy(this.trigger, this.wrap);
62164         if (this.mimicing){
62165             this.doc.un('mousedown', this.mimicBlur, this);
62166         }
62167         delete this.doc;
62168         Ext.form.TriggerField.superclass.onDestroy.call(this);
62169     },
62170
62171     // private
62172     onFocus : function(){
62173         Ext.form.TriggerField.superclass.onFocus.call(this);
62174         if(!this.mimicing){
62175             this.wrap.addClass(this.wrapFocusClass);
62176             this.mimicing = true;
62177             this.doc.on('mousedown', this.mimicBlur, this, {delay: 10});
62178             if(this.monitorTab){
62179                 this.on('specialkey', this.checkTab, this);
62180             }
62181         }
62182     },
62183
62184     // private
62185     checkTab : function(me, e){
62186         if(e.getKey() == e.TAB){
62187             this.triggerBlur();
62188         }
62189     },
62190
62191     // private
62192     onBlur : Ext.emptyFn,
62193
62194     // private
62195     mimicBlur : function(e){
62196         if(!this.isDestroyed && !this.wrap.contains(e.target) && this.validateBlur(e)){
62197             this.triggerBlur();
62198         }
62199     },
62200
62201     // private
62202     triggerBlur : function(){
62203         this.mimicing = false;
62204         this.doc.un('mousedown', this.mimicBlur, this);
62205         if(this.monitorTab && this.el){
62206             this.un('specialkey', this.checkTab, this);
62207         }
62208         Ext.form.TriggerField.superclass.onBlur.call(this);
62209         if(this.wrap){
62210             this.wrap.removeClass(this.wrapFocusClass);
62211         }
62212     },
62213
62214     beforeBlur : Ext.emptyFn,
62215
62216     // private
62217     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
62218     validateBlur : function(e){
62219         return true;
62220     },
62221
62222     /**
62223      * The function that should handle the trigger's click event.  This method does nothing by default
62224      * until overridden by an implementing function.  See Ext.form.ComboBox and Ext.form.DateField for
62225      * sample implementations.
62226      * @method
62227      * @param {EventObject} e
62228      */
62229     onTriggerClick : Ext.emptyFn
62230
62231     /**
62232      * @cfg {Boolean} grow @hide
62233      */
62234     /**
62235      * @cfg {Number} growMin @hide
62236      */
62237     /**
62238      * @cfg {Number} growMax @hide
62239      */
62240 });
62241
62242 /**
62243  * @class Ext.form.TwinTriggerField
62244  * @extends Ext.form.TriggerField
62245  * TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
62246  * to be extended by an implementing class.  For an example of implementing this class, see the custom
62247  * SearchField implementation here:
62248  * <a href="http://extjs.com/deploy/ext/examples/form/custom.html">http://extjs.com/deploy/ext/examples/form/custom.html</a>
62249  */
62250 Ext.form.TwinTriggerField = Ext.extend(Ext.form.TriggerField, {
62251     /**
62252      * @cfg {Mixed} triggerConfig
62253      * <p>A {@link Ext.DomHelper DomHelper} config object specifying the structure of the trigger elements
62254      * for this Field. (Optional).</p>
62255      * <p>Specify this when you need a customized element to contain the two trigger elements for this Field.
62256      * Each trigger element must be marked by the CSS class <tt>x-form-trigger</tt> (also see
62257      * <tt>{@link #trigger1Class}</tt> and <tt>{@link #trigger2Class}</tt>).</p>
62258      * <p>Note that when using this option, it is the developer's responsibility to ensure correct sizing,
62259      * positioning and appearance of the triggers.</p>
62260      */
62261     /**
62262      * @cfg {String} trigger1Class
62263      * An additional CSS class used to style the trigger button.  The trigger will always get the
62264      * class <tt>'x-form-trigger'</tt> by default and <tt>triggerClass</tt> will be <b>appended</b> if specified.
62265      */
62266     /**
62267      * @cfg {String} trigger2Class
62268      * An additional CSS class used to style the trigger button.  The trigger will always get the
62269      * class <tt>'x-form-trigger'</tt> by default and <tt>triggerClass</tt> will be <b>appended</b> if specified.
62270      */
62271
62272     initComponent : function(){
62273         Ext.form.TwinTriggerField.superclass.initComponent.call(this);
62274
62275         this.triggerConfig = {
62276             tag:'span', cls:'x-form-twin-triggers', cn:[
62277             {tag: "img", src: Ext.BLANK_IMAGE_URL, alt: "", cls: "x-form-trigger " + this.trigger1Class},
62278             {tag: "img", src: Ext.BLANK_IMAGE_URL, alt: "", cls: "x-form-trigger " + this.trigger2Class}
62279         ]};
62280     },
62281
62282     getTrigger : function(index){
62283         return this.triggers[index];
62284     },
62285     
62286     afterRender: function(){
62287         Ext.form.TwinTriggerField.superclass.afterRender.call(this);
62288         var triggers = this.triggers,
62289             i = 0,
62290             len = triggers.length;
62291             
62292         for(; i < len; ++i){
62293             if(this['hideTrigger' + (i + 1)]){
62294                     triggers[i].hide();
62295                 }
62296
62297         }    
62298     },
62299
62300     initTrigger : function(){
62301         var ts = this.trigger.select('.x-form-trigger', true),
62302             triggerField = this;
62303             
62304         ts.each(function(t, all, index){
62305             var triggerIndex = 'Trigger'+(index+1);
62306             t.hide = function(){
62307                 var w = triggerField.wrap.getWidth();
62308                 this.dom.style.display = 'none';
62309                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
62310                 triggerField['hidden' + triggerIndex] = true;
62311             };
62312             t.show = function(){
62313                 var w = triggerField.wrap.getWidth();
62314                 this.dom.style.display = '';
62315                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
62316                 triggerField['hidden' + triggerIndex] = false;
62317             };
62318             this.mon(t, 'click', this['on'+triggerIndex+'Click'], this, {preventDefault:true});
62319             t.addClassOnOver('x-form-trigger-over');
62320             t.addClassOnClick('x-form-trigger-click');
62321         }, this);
62322         this.triggers = ts.elements;
62323     },
62324
62325     getTriggerWidth: function(){
62326         var tw = 0;
62327         Ext.each(this.triggers, function(t, index){
62328             var triggerIndex = 'Trigger' + (index + 1),
62329                 w = t.getWidth();
62330             if(w === 0 && !this['hidden' + triggerIndex]){
62331                 tw += this.defaultTriggerWidth;
62332             }else{
62333                 tw += w;
62334             }
62335         }, this);
62336         return tw;
62337     },
62338
62339     // private
62340     onDestroy : function() {
62341         Ext.destroy(this.triggers);
62342         Ext.form.TwinTriggerField.superclass.onDestroy.call(this);
62343     },
62344
62345     /**
62346      * The function that should handle the trigger's click event.  This method does nothing by default
62347      * until overridden by an implementing function. See {@link Ext.form.TriggerField#onTriggerClick}
62348      * for additional information.
62349      * @method
62350      * @param {EventObject} e
62351      */
62352     onTrigger1Click : Ext.emptyFn,
62353     /**
62354      * The function that should handle the trigger's click event.  This method does nothing by default
62355      * until overridden by an implementing function. See {@link Ext.form.TriggerField#onTriggerClick}
62356      * for additional information.
62357      * @method
62358      * @param {EventObject} e
62359      */
62360     onTrigger2Click : Ext.emptyFn
62361 });
62362 Ext.reg('trigger', Ext.form.TriggerField);
62363 /**
62364  * @class Ext.form.TextArea
62365  * @extends Ext.form.TextField
62366  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
62367  * support for auto-sizing.
62368  * @constructor
62369  * Creates a new TextArea
62370  * @param {Object} config Configuration options
62371  * @xtype textarea
62372  */
62373 Ext.form.TextArea = Ext.extend(Ext.form.TextField,  {
62374     /**
62375      * @cfg {Number} growMin The minimum height to allow when <tt>{@link Ext.form.TextField#grow grow}=true</tt>
62376      * (defaults to <tt>60</tt>)
62377      */
62378     growMin : 60,
62379     /**
62380      * @cfg {Number} growMax The maximum height to allow when <tt>{@link Ext.form.TextField#grow grow}=true</tt>
62381      * (defaults to <tt>1000</tt>)
62382      */
62383     growMax: 1000,
62384     growAppend : '&#160;\n&#160;',
62385
62386     enterIsSpecial : false,
62387
62388     /**
62389      * @cfg {Boolean} preventScrollbars <tt>true</tt> to prevent scrollbars from appearing regardless of how much text is
62390      * in the field. This option is only relevant when {@link #grow} is <tt>true</tt>. Equivalent to setting overflow: hidden, defaults to 
62391      * <tt>false</tt>.
62392      */
62393     preventScrollbars: false,
62394     /**
62395      * @cfg {String/Object} autoCreate <p>A {@link Ext.DomHelper DomHelper} element spec, or true for a default
62396      * element spec. Used to create the {@link Ext.Component#getEl Element} which will encapsulate this Component.
62397      * See <tt>{@link Ext.Component#autoEl autoEl}</tt> for details.  Defaults to:</p>
62398      * <pre><code>{tag: "textarea", style: "width:100px;height:60px;", autocomplete: "off"}</code></pre>
62399      */
62400
62401     // private
62402     onRender : function(ct, position){
62403         if(!this.el){
62404             this.defaultAutoCreate = {
62405                 tag: "textarea",
62406                 style:"width:100px;height:60px;",
62407                 autocomplete: "off"
62408             };
62409         }
62410         Ext.form.TextArea.superclass.onRender.call(this, ct, position);
62411         if(this.grow){
62412             this.textSizeEl = Ext.DomHelper.append(document.body, {
62413                 tag: "pre", cls: "x-form-grow-sizer"
62414             });
62415             if(this.preventScrollbars){
62416                 this.el.setStyle("overflow", "hidden");
62417             }
62418             this.el.setHeight(this.growMin);
62419         }
62420     },
62421
62422     onDestroy : function(){
62423         Ext.removeNode(this.textSizeEl);
62424         Ext.form.TextArea.superclass.onDestroy.call(this);
62425     },
62426
62427     fireKey : function(e){
62428         if(e.isSpecialKey() && (this.enterIsSpecial || (e.getKey() != e.ENTER || e.hasModifier()))){
62429             this.fireEvent("specialkey", this, e);
62430         }
62431     },
62432     
62433     // private
62434     doAutoSize : function(e){
62435         return !e.isNavKeyPress() || e.getKey() == e.ENTER;
62436     },
62437     
62438     // inherit docs
62439     filterValidation: function(e) {            
62440         if(!e.isNavKeyPress() || (!this.enterIsSpecial && e.keyCode == e.ENTER)){
62441             this.validationTask.delay(this.validationDelay);
62442         }
62443     },
62444
62445     /**
62446      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
62447      * This only takes effect if grow = true, and fires the {@link #autosize} event if the height changes.
62448      */
62449     autoSize: function(){
62450         if(!this.grow || !this.textSizeEl){
62451             return;
62452         }
62453         var el = this.el,
62454             v = Ext.util.Format.htmlEncode(el.dom.value),
62455             ts = this.textSizeEl,
62456             h;
62457             
62458         Ext.fly(ts).setWidth(this.el.getWidth());
62459         if(v.length < 1){
62460             v = "&#160;&#160;";
62461         }else{
62462             v += this.growAppend;
62463             if(Ext.isIE){
62464                 v = v.replace(/\n/g, '&#160;<br />');
62465             }
62466         }
62467         ts.innerHTML = v;
62468         h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
62469         if(h != this.lastHeight){
62470             this.lastHeight = h;
62471             this.el.setHeight(h);
62472             this.fireEvent("autosize", this, h);
62473         }
62474     }
62475 });
62476 Ext.reg('textarea', Ext.form.TextArea);/**
62477  * @class Ext.form.NumberField
62478  * @extends Ext.form.TextField
62479  * Numeric text field that provides automatic keystroke filtering and numeric validation.
62480  * @constructor
62481  * Creates a new NumberField
62482  * @param {Object} config Configuration options
62483  * @xtype numberfield
62484  */
62485 Ext.form.NumberField = Ext.extend(Ext.form.TextField,  {
62486     /**
62487      * @cfg {RegExp} stripCharsRe @hide
62488      */
62489     /**
62490      * @cfg {RegExp} maskRe @hide
62491      */
62492     /**
62493      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
62494      */
62495     fieldClass: "x-form-field x-form-num-field",
62496     
62497     /**
62498      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
62499      */
62500     allowDecimals : true,
62501     
62502     /**
62503      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
62504      */
62505     decimalSeparator : ".",
62506     
62507     /**
62508      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
62509      */
62510     decimalPrecision : 2,
62511     
62512     /**
62513      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
62514      */
62515     allowNegative : true,
62516     
62517     /**
62518      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
62519      */
62520     minValue : Number.NEGATIVE_INFINITY,
62521     
62522     /**
62523      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
62524      */
62525     maxValue : Number.MAX_VALUE,
62526     
62527     /**
62528      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
62529      */
62530     minText : "The minimum value for this field is {0}",
62531     
62532     /**
62533      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
62534      */
62535     maxText : "The maximum value for this field is {0}",
62536     
62537     /**
62538      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
62539      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
62540      */
62541     nanText : "{0} is not a valid number",
62542     
62543     /**
62544      * @cfg {String} baseChars The base set of characters to evaluate as valid numbers (defaults to '0123456789').
62545      */
62546     baseChars : "0123456789",
62547     
62548     /**
62549      * @cfg {Boolean} autoStripChars True to automatically strip not allowed characters from the field. Defaults to <tt>false</tt>
62550      */
62551     autoStripChars: false,
62552
62553     // private
62554     initEvents : function() {
62555         var allowed = this.baseChars + '';
62556         if (this.allowDecimals) {
62557             allowed += this.decimalSeparator;
62558         }
62559         if (this.allowNegative) {
62560             allowed += '-';
62561         }
62562         allowed = Ext.escapeRe(allowed);
62563         this.maskRe = new RegExp('[' + allowed + ']');
62564         if (this.autoStripChars) {
62565             this.stripCharsRe = new RegExp('[^' + allowed + ']', 'gi');
62566         }
62567         
62568         Ext.form.NumberField.superclass.initEvents.call(this);
62569     },
62570     
62571     /**
62572      * Runs all of NumberFields validations and returns an array of any errors. Note that this first
62573      * runs TextField's validations, so the returned array is an amalgamation of all field errors.
62574      * The additional validations run test that the value is a number, and that it is within the
62575      * configured min and max values.
62576      * @param {Mixed} value The value to get errors for (defaults to the current field value)
62577      * @return {Array} All validation errors for this field
62578      */
62579     getErrors: function(value) {
62580         var errors = Ext.form.NumberField.superclass.getErrors.apply(this, arguments);
62581         
62582         value = Ext.isDefined(value) ? value : this.processValue(this.getRawValue());
62583         
62584         if (value.length < 1) { // if it's blank and textfield didn't flag it then it's valid
62585              return errors;
62586         }
62587         
62588         value = String(value).replace(this.decimalSeparator, ".");
62589         
62590         if(isNaN(value)){
62591             errors.push(String.format(this.nanText, value));
62592         }
62593         
62594         var num = this.parseValue(value);
62595         
62596         if (num < this.minValue) {
62597             errors.push(String.format(this.minText, this.minValue));
62598         }
62599         
62600         if (num > this.maxValue) {
62601             errors.push(String.format(this.maxText, this.maxValue));
62602         }
62603         
62604         return errors;
62605     },
62606
62607     getValue : function() {
62608         return this.fixPrecision(this.parseValue(Ext.form.NumberField.superclass.getValue.call(this)));
62609     },
62610
62611     setValue : function(v) {
62612         v = this.fixPrecision(v);
62613         v = Ext.isNumber(v) ? v : parseFloat(String(v).replace(this.decimalSeparator, "."));
62614         v = isNaN(v) ? '' : String(v).replace(".", this.decimalSeparator);
62615         return Ext.form.NumberField.superclass.setValue.call(this, v);
62616     },
62617     
62618     /**
62619      * Replaces any existing {@link #minValue} with the new value.
62620      * @param {Number} value The minimum value
62621      */
62622     setMinValue : function(value) {
62623         this.minValue = Ext.num(value, Number.NEGATIVE_INFINITY);
62624     },
62625     
62626     /**
62627      * Replaces any existing {@link #maxValue} with the new value.
62628      * @param {Number} value The maximum value
62629      */
62630     setMaxValue : function(value) {
62631         this.maxValue = Ext.num(value, Number.MAX_VALUE);    
62632     },
62633
62634     // private
62635     parseValue : function(value) {
62636         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
62637         return isNaN(value) ? '' : value;
62638     },
62639
62640     /**
62641      * @private
62642      * 
62643      */
62644     fixPrecision : function(value) {
62645         var nan = isNaN(value);
62646         
62647         if (!this.allowDecimals || this.decimalPrecision == -1 || nan || !value) {
62648             return nan ? '' : value;
62649         }
62650         
62651         return parseFloat(parseFloat(value).toFixed(this.decimalPrecision));
62652     },
62653
62654     beforeBlur : function() {
62655         var v = this.parseValue(this.getRawValue());
62656         
62657         if (!Ext.isEmpty(v)) {
62658             this.setValue(v);
62659         }
62660     }
62661 });
62662
62663 Ext.reg('numberfield', Ext.form.NumberField);
62664 /**
62665  * @class Ext.form.DateField
62666  * @extends Ext.form.TriggerField
62667  * Provides a date input field with a {@link Ext.DatePicker} dropdown and automatic date validation.
62668  * @constructor
62669  * Create a new DateField
62670  * @param {Object} config
62671  * @xtype datefield
62672  */
62673 Ext.form.DateField = Ext.extend(Ext.form.TriggerField,  {
62674     /**
62675      * @cfg {String} format
62676      * The default date format string which can be overriden for localization support.  The format must be
62677      * valid according to {@link Date#parseDate} (defaults to <tt>'m/d/Y'</tt>).
62678      */
62679     format : "m/d/Y",
62680     /**
62681      * @cfg {String} altFormats
62682      * Multiple date formats separated by "<tt>|</tt>" to try when parsing a user input value and it
62683      * does not match the defined format (defaults to
62684      * <tt>'m/d/Y|n/j/Y|n/j/y|m/j/y|n/d/y|m/j/Y|n/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d|Y-m-d|n-j|n/j'</tt>).
62685      */
62686     altFormats : "m/d/Y|n/j/Y|n/j/y|m/j/y|n/d/y|m/j/Y|n/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d|Y-m-d|n-j|n/j",
62687     /**
62688      * @cfg {String} disabledDaysText
62689      * The tooltip to display when the date falls on a disabled day (defaults to <tt>'Disabled'</tt>)
62690      */
62691     disabledDaysText : "Disabled",
62692     /**
62693      * @cfg {String} disabledDatesText
62694      * The tooltip text to display when the date falls on a disabled date (defaults to <tt>'Disabled'</tt>)
62695      */
62696     disabledDatesText : "Disabled",
62697     /**
62698      * @cfg {String} minText
62699      * The error text to display when the date in the cell is before <tt>{@link #minValue}</tt> (defaults to
62700      * <tt>'The date in this field must be after {minValue}'</tt>).
62701      */
62702     minText : "The date in this field must be equal to or after {0}",
62703     /**
62704      * @cfg {String} maxText
62705      * The error text to display when the date in the cell is after <tt>{@link #maxValue}</tt> (defaults to
62706      * <tt>'The date in this field must be before {maxValue}'</tt>).
62707      */
62708     maxText : "The date in this field must be equal to or before {0}",
62709     /**
62710      * @cfg {String} invalidText
62711      * The error text to display when the date in the field is invalid (defaults to
62712      * <tt>'{value} is not a valid date - it must be in the format {format}'</tt>).
62713      */
62714     invalidText : "{0} is not a valid date - it must be in the format {1}",
62715     /**
62716      * @cfg {String} triggerClass
62717      * An additional CSS class used to style the trigger button.  The trigger will always get the
62718      * class <tt>'x-form-trigger'</tt> and <tt>triggerClass</tt> will be <b>appended</b> if specified
62719      * (defaults to <tt>'x-form-date-trigger'</tt> which displays a calendar icon).
62720      */
62721     triggerClass : 'x-form-date-trigger',
62722     /**
62723      * @cfg {Boolean} showToday
62724      * <tt>false</tt> to hide the footer area of the DatePicker containing the Today button and disable
62725      * the keyboard handler for spacebar that selects the current date (defaults to <tt>true</tt>).
62726      */
62727     showToday : true,
62728     
62729     /**
62730      * @cfg {Number} startDay
62731      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
62732      */
62733     startDay : 0,
62734     
62735     /**
62736      * @cfg {Date/String} minValue
62737      * The minimum allowed date. Can be either a Javascript date object or a string date in a
62738      * valid format (defaults to null).
62739      */
62740     /**
62741      * @cfg {Date/String} maxValue
62742      * The maximum allowed date. Can be either a Javascript date object or a string date in a
62743      * valid format (defaults to null).
62744      */
62745     /**
62746      * @cfg {Array} disabledDays
62747      * An array of days to disable, 0 based (defaults to null). Some examples:<pre><code>
62748 // disable Sunday and Saturday:
62749 disabledDays:  [0, 6]
62750 // disable weekdays:
62751 disabledDays: [1,2,3,4,5]
62752      * </code></pre>
62753      */
62754     /**
62755      * @cfg {Array} disabledDates
62756      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
62757      * expression so they are very powerful. Some examples:<pre><code>
62758 // disable these exact dates:
62759 disabledDates: ["03/08/2003", "09/16/2003"]
62760 // disable these days for every year:
62761 disabledDates: ["03/08", "09/16"]
62762 // only match the beginning (useful if you are using short years):
62763 disabledDates: ["^03/08"]
62764 // disable every day in March 2006:
62765 disabledDates: ["03/../2006"]
62766 // disable every day in every March:
62767 disabledDates: ["^03"]
62768      * </code></pre>
62769      * Note that the format of the dates included in the array should exactly match the {@link #format} config.
62770      * In order to support regular expressions, if you are using a {@link #format date format} that has "." in
62771      * it, you will have to escape the dot when restricting dates. For example: <tt>["03\\.08\\.03"]</tt>.
62772      */
62773     /**
62774      * @cfg {String/Object} autoCreate
62775      * A {@link Ext.DomHelper DomHelper element specification object}, or <tt>true</tt> for the default element
62776      * specification object:<pre><code>
62777      * autoCreate: {tag: "input", type: "text", size: "10", autocomplete: "off"}
62778      * </code></pre>
62779      */
62780
62781     // private
62782     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
62783
62784     // in the absence of a time value, a default value of 12 noon will be used
62785     // (note: 12 noon was chosen because it steers well clear of all DST timezone changes)
62786     initTime: '12', // 24 hour format
62787
62788     initTimeFormat: 'H',
62789
62790     // PUBLIC -- to be documented
62791     safeParse : function(value, format) {
62792         if (/[gGhH]/.test(format.replace(/(\\.)/g, ''))) {
62793             // if parse format contains hour information, no DST adjustment is necessary
62794             return Date.parseDate(value, format);
62795         } else {
62796             // set time to 12 noon, then clear the time
62797             var parsedDate = Date.parseDate(value + ' ' + this.initTime, format + ' ' + this.initTimeFormat);
62798
62799             if (parsedDate) {
62800                 return parsedDate.clearTime();
62801             }
62802         }
62803     },
62804
62805     initComponent : function(){
62806         Ext.form.DateField.superclass.initComponent.call(this);
62807
62808         this.addEvents(
62809             /**
62810              * @event select
62811              * Fires when a date is selected via the date picker.
62812              * @param {Ext.form.DateField} this
62813              * @param {Date} date The date that was selected
62814              */
62815             'select'
62816         );
62817
62818         if(Ext.isString(this.minValue)){
62819             this.minValue = this.parseDate(this.minValue);
62820         }
62821         if(Ext.isString(this.maxValue)){
62822             this.maxValue = this.parseDate(this.maxValue);
62823         }
62824         this.disabledDatesRE = null;
62825         this.initDisabledDays();
62826     },
62827
62828     initEvents: function() {
62829         Ext.form.DateField.superclass.initEvents.call(this);
62830         this.keyNav = new Ext.KeyNav(this.el, {
62831             "down": function(e) {
62832                 this.onTriggerClick();
62833             },
62834             scope: this,
62835             forceKeyDown: true
62836         });
62837     },
62838
62839
62840     // private
62841     initDisabledDays : function(){
62842         if(this.disabledDates){
62843             var dd = this.disabledDates,
62844                 len = dd.length - 1,
62845                 re = "(?:";
62846
62847             Ext.each(dd, function(d, i){
62848                 re += Ext.isDate(d) ? '^' + Ext.escapeRe(d.dateFormat(this.format)) + '$' : dd[i];
62849                 if(i != len){
62850                     re += '|';
62851                 }
62852             }, this);
62853             this.disabledDatesRE = new RegExp(re + ')');
62854         }
62855     },
62856
62857     /**
62858      * Replaces any existing disabled dates with new values and refreshes the DatePicker.
62859      * @param {Array} disabledDates An array of date strings (see the <tt>{@link #disabledDates}</tt> config
62860      * for details on supported values) used to disable a pattern of dates.
62861      */
62862     setDisabledDates : function(dd){
62863         this.disabledDates = dd;
62864         this.initDisabledDays();
62865         if(this.menu){
62866             this.menu.picker.setDisabledDates(this.disabledDatesRE);
62867         }
62868     },
62869
62870     /**
62871      * Replaces any existing disabled days (by index, 0-6) with new values and refreshes the DatePicker.
62872      * @param {Array} disabledDays An array of disabled day indexes. See the <tt>{@link #disabledDays}</tt>
62873      * config for details on supported values.
62874      */
62875     setDisabledDays : function(dd){
62876         this.disabledDays = dd;
62877         if(this.menu){
62878             this.menu.picker.setDisabledDays(dd);
62879         }
62880     },
62881
62882     /**
62883      * Replaces any existing <tt>{@link #minValue}</tt> with the new value and refreshes the DatePicker.
62884      * @param {Date} value The minimum date that can be selected
62885      */
62886     setMinValue : function(dt){
62887         this.minValue = (Ext.isString(dt) ? this.parseDate(dt) : dt);
62888         if(this.menu){
62889             this.menu.picker.setMinDate(this.minValue);
62890         }
62891     },
62892
62893     /**
62894      * Replaces any existing <tt>{@link #maxValue}</tt> with the new value and refreshes the DatePicker.
62895      * @param {Date} value The maximum date that can be selected
62896      */
62897     setMaxValue : function(dt){
62898         this.maxValue = (Ext.isString(dt) ? this.parseDate(dt) : dt);
62899         if(this.menu){
62900             this.menu.picker.setMaxDate(this.maxValue);
62901         }
62902     },
62903
62904     /**
62905      * Runs all of NumberFields validations and returns an array of any errors. Note that this first
62906      * runs TextField's validations, so the returned array is an amalgamation of all field errors.
62907      * The additional validation checks are testing that the date format is valid, that the chosen
62908      * date is within the min and max date constraints set, that the date chosen is not in the disabledDates
62909      * regex and that the day chosed is not one of the disabledDays.
62910      * @param {Mixed} value The value to get errors for (defaults to the current field value)
62911      * @return {Array} All validation errors for this field
62912      */
62913     getErrors: function(value) {
62914         var errors = Ext.form.DateField.superclass.getErrors.apply(this, arguments);
62915
62916         value = this.formatDate(value || this.processValue(this.getRawValue()));
62917
62918         if (value.length < 1) { // if it's blank and textfield didn't flag it then it's valid
62919              return errors;
62920         }
62921
62922         var svalue = value;
62923         value = this.parseDate(value);
62924         if (!value) {
62925             errors.push(String.format(this.invalidText, svalue, this.format));
62926             return errors;
62927         }
62928
62929         var time = value.getTime();
62930         if (this.minValue && time < this.minValue.clearTime().getTime()) {
62931             errors.push(String.format(this.minText, this.formatDate(this.minValue)));
62932         }
62933
62934         if (this.maxValue && time > this.maxValue.clearTime().getTime()) {
62935             errors.push(String.format(this.maxText, this.formatDate(this.maxValue)));
62936         }
62937
62938         if (this.disabledDays) {
62939             var day = value.getDay();
62940
62941             for(var i = 0; i < this.disabledDays.length; i++) {
62942                 if (day === this.disabledDays[i]) {
62943                     errors.push(this.disabledDaysText);
62944                     break;
62945                 }
62946             }
62947         }
62948
62949         var fvalue = this.formatDate(value);
62950         if (this.disabledDatesRE && this.disabledDatesRE.test(fvalue)) {
62951             errors.push(String.format(this.disabledDatesText, fvalue));
62952         }
62953
62954         return errors;
62955     },
62956
62957     // private
62958     // Provides logic to override the default TriggerField.validateBlur which just returns true
62959     validateBlur : function(){
62960         return !this.menu || !this.menu.isVisible();
62961     },
62962
62963     /**
62964      * Returns the current date value of the date field.
62965      * @return {Date} The date value
62966      */
62967     getValue : function(){
62968         return this.parseDate(Ext.form.DateField.superclass.getValue.call(this)) || "";
62969     },
62970
62971     /**
62972      * Sets the value of the date field.  You can pass a date object or any string that can be
62973      * parsed into a valid date, using <tt>{@link #format}</tt> as the date format, according
62974      * to the same rules as {@link Date#parseDate} (the default format used is <tt>"m/d/Y"</tt>).
62975      * <br />Usage:
62976      * <pre><code>
62977 //All of these calls set the same date value (May 4, 2006)
62978
62979 //Pass a date object:
62980 var dt = new Date('5/4/2006');
62981 dateField.setValue(dt);
62982
62983 //Pass a date string (default format):
62984 dateField.setValue('05/04/2006');
62985
62986 //Pass a date string (custom format):
62987 dateField.format = 'Y-m-d';
62988 dateField.setValue('2006-05-04');
62989 </code></pre>
62990      * @param {String/Date} date The date or valid date string
62991      * @return {Ext.form.Field} this
62992      */
62993     setValue : function(date){
62994         return Ext.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
62995     },
62996
62997     // private
62998     parseDate : function(value) {
62999         if(!value || Ext.isDate(value)){
63000             return value;
63001         }
63002
63003         var v = this.safeParse(value, this.format),
63004             af = this.altFormats,
63005             afa = this.altFormatsArray;
63006
63007         if (!v && af) {
63008             afa = afa || af.split("|");
63009
63010             for (var i = 0, len = afa.length; i < len && !v; i++) {
63011                 v = this.safeParse(value, afa[i]);
63012             }
63013         }
63014         return v;
63015     },
63016
63017     // private
63018     onDestroy : function(){
63019         Ext.destroy(this.menu, this.keyNav);
63020         Ext.form.DateField.superclass.onDestroy.call(this);
63021     },
63022
63023     // private
63024     formatDate : function(date){
63025         return Ext.isDate(date) ? date.dateFormat(this.format) : date;
63026     },
63027
63028     /**
63029      * @method onTriggerClick
63030      * @hide
63031      */
63032     // private
63033     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
63034     onTriggerClick : function(){
63035         if(this.disabled){
63036             return;
63037         }
63038         if(this.menu == null){
63039             this.menu = new Ext.menu.DateMenu({
63040                 hideOnClick: false,
63041                 focusOnSelect: false
63042             });
63043         }
63044         this.onFocus();
63045         Ext.apply(this.menu.picker,  {
63046             minDate : this.minValue,
63047             maxDate : this.maxValue,
63048             disabledDatesRE : this.disabledDatesRE,
63049             disabledDatesText : this.disabledDatesText,
63050             disabledDays : this.disabledDays,
63051             disabledDaysText : this.disabledDaysText,
63052             format : this.format,
63053             showToday : this.showToday,
63054             startDay: this.startDay,
63055             minText : String.format(this.minText, this.formatDate(this.minValue)),
63056             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
63057         });
63058         this.menu.picker.setValue(this.getValue() || new Date());
63059         this.menu.show(this.el, "tl-bl?");
63060         this.menuEvents('on');
63061     },
63062
63063     //private
63064     menuEvents: function(method){
63065         this.menu[method]('select', this.onSelect, this);
63066         this.menu[method]('hide', this.onMenuHide, this);
63067         this.menu[method]('show', this.onFocus, this);
63068     },
63069
63070     onSelect: function(m, d){
63071         this.setValue(d);
63072         this.fireEvent('select', this, d);
63073         this.menu.hide();
63074     },
63075
63076     onMenuHide: function(){
63077         this.focus(false, 60);
63078         this.menuEvents('un');
63079     },
63080
63081     // private
63082     beforeBlur : function(){
63083         var v = this.parseDate(this.getRawValue());
63084         if(v){
63085             this.setValue(v);
63086         }
63087     }
63088
63089     /**
63090      * @cfg {Boolean} grow @hide
63091      */
63092     /**
63093      * @cfg {Number} growMin @hide
63094      */
63095     /**
63096      * @cfg {Number} growMax @hide
63097      */
63098     /**
63099      * @hide
63100      * @method autoSize
63101      */
63102 });
63103 Ext.reg('datefield', Ext.form.DateField);
63104 /**
63105  * @class Ext.form.DisplayField
63106  * @extends Ext.form.Field
63107  * A display-only text field which is not validated and not submitted.
63108  * @constructor
63109  * Creates a new DisplayField.
63110  * @param {Object} config Configuration options
63111  * @xtype displayfield
63112  */
63113 Ext.form.DisplayField = Ext.extend(Ext.form.Field,  {
63114     validationEvent : false,
63115     validateOnBlur : false,
63116     defaultAutoCreate : {tag: "div"},
63117     /**
63118      * @cfg {String} fieldClass The default CSS class for the field (defaults to <tt>"x-form-display-field"</tt>)
63119      */
63120     fieldClass : "x-form-display-field",
63121     /**
63122      * @cfg {Boolean} htmlEncode <tt>false</tt> to skip HTML-encoding the text when rendering it (defaults to
63123      * <tt>false</tt>). This might be useful if you want to include tags in the field's innerHTML rather than
63124      * rendering them as string literals per the default logic.
63125      */
63126     htmlEncode: false,
63127
63128     // private
63129     initEvents : Ext.emptyFn,
63130
63131     isValid : function(){
63132         return true;
63133     },
63134
63135     validate : function(){
63136         return true;
63137     },
63138
63139     getRawValue : function(){
63140         var v = this.rendered ? this.el.dom.innerHTML : Ext.value(this.value, '');
63141         if(v === this.emptyText){
63142             v = '';
63143         }
63144         if(this.htmlEncode){
63145             v = Ext.util.Format.htmlDecode(v);
63146         }
63147         return v;
63148     },
63149
63150     getValue : function(){
63151         return this.getRawValue();
63152     },
63153     
63154     getName: function() {
63155         return this.name;
63156     },
63157
63158     setRawValue : function(v){
63159         if(this.htmlEncode){
63160             v = Ext.util.Format.htmlEncode(v);
63161         }
63162         return this.rendered ? (this.el.dom.innerHTML = (Ext.isEmpty(v) ? '' : v)) : (this.value = v);
63163     },
63164
63165     setValue : function(v){
63166         this.setRawValue(v);
63167         return this;
63168     }
63169     /** 
63170      * @cfg {String} inputType 
63171      * @hide
63172      */
63173     /** 
63174      * @cfg {Boolean} disabled 
63175      * @hide
63176      */
63177     /** 
63178      * @cfg {Boolean} readOnly 
63179      * @hide
63180      */
63181     /** 
63182      * @cfg {Boolean} validateOnBlur 
63183      * @hide
63184      */
63185     /** 
63186      * @cfg {Number} validationDelay 
63187      * @hide
63188      */
63189     /** 
63190      * @cfg {String/Boolean} validationEvent 
63191      * @hide
63192      */
63193 });
63194
63195 Ext.reg('displayfield', Ext.form.DisplayField);
63196 /**
63197  * @class Ext.form.ComboBox
63198  * @extends Ext.form.TriggerField
63199  * <p>A combobox control with support for autocomplete, remote-loading, paging and many other features.</p>
63200  * <p>A ComboBox works in a similar manner to a traditional HTML &lt;select> field. The difference is
63201  * that to submit the {@link #valueField}, you must specify a {@link #hiddenName} to create a hidden input
63202  * field to hold the value of the valueField. The <i>{@link #displayField}</i> is shown in the text field
63203  * which is named according to the {@link #name}.</p>
63204  * <p><b><u>Events</u></b></p>
63205  * <p>To do something when something in ComboBox is selected, configure the select event:<pre><code>
63206 var cb = new Ext.form.ComboBox({
63207     // all of your config options
63208     listeners:{
63209          scope: yourScope,
63210          'select': yourFunction
63211     }
63212 });
63213
63214 // Alternatively, you can assign events after the object is created:
63215 var cb = new Ext.form.ComboBox(yourOptions);
63216 cb.on('select', yourFunction, yourScope);
63217  * </code></pre></p>
63218  *
63219  * <p><b><u>ComboBox in Grid</u></b></p>
63220  * <p>If using a ComboBox in an {@link Ext.grid.EditorGridPanel Editor Grid} a {@link Ext.grid.Column#renderer renderer}
63221  * will be needed to show the displayField when the editor is not active.  Set up the renderer manually, or implement
63222  * a reusable render, for example:<pre><code>
63223 // create reusable renderer
63224 Ext.util.Format.comboRenderer = function(combo){
63225     return function(value){
63226         var record = combo.findRecord(combo.{@link #valueField}, value);
63227         return record ? record.get(combo.{@link #displayField}) : combo.{@link #valueNotFoundText};
63228     }
63229 }
63230
63231 // create the combo instance
63232 var combo = new Ext.form.ComboBox({
63233     {@link #typeAhead}: true,
63234     {@link #triggerAction}: 'all',
63235     {@link #lazyRender}:true,
63236     {@link #mode}: 'local',
63237     {@link #store}: new Ext.data.ArrayStore({
63238         id: 0,
63239         fields: [
63240             'myId',
63241             'displayText'
63242         ],
63243         data: [[1, 'item1'], [2, 'item2']]
63244     }),
63245     {@link #valueField}: 'myId',
63246     {@link #displayField}: 'displayText'
63247 });
63248
63249 // snippet of column model used within grid
63250 var cm = new Ext.grid.ColumnModel([{
63251        ...
63252     },{
63253        header: "Some Header",
63254        dataIndex: 'whatever',
63255        width: 130,
63256        editor: combo, // specify reference to combo instance
63257        renderer: Ext.util.Format.comboRenderer(combo) // pass combo instance to reusable renderer
63258     },
63259     ...
63260 ]);
63261  * </code></pre></p>
63262  *
63263  * <p><b><u>Filtering</u></b></p>
63264  * <p>A ComboBox {@link #doQuery uses filtering itself}, for information about filtering the ComboBox
63265  * store manually see <tt>{@link #lastQuery}</tt>.</p>
63266  * @constructor
63267  * Create a new ComboBox.
63268  * @param {Object} config Configuration options
63269  * @xtype combo
63270  */
63271 Ext.form.ComboBox = Ext.extend(Ext.form.TriggerField, {
63272     /**
63273      * @cfg {Mixed} transform The id, DOM node or element of an existing HTML SELECT to convert to a ComboBox.
63274      * Note that if you specify this and the combo is going to be in an {@link Ext.form.BasicForm} or
63275      * {@link Ext.form.FormPanel}, you must also set <tt>{@link #lazyRender} = true</tt>.
63276      */
63277     /**
63278      * @cfg {Boolean} lazyRender <tt>true</tt> to prevent the ComboBox from rendering until requested
63279      * (should always be used when rendering into an {@link Ext.Editor} (e.g. {@link Ext.grid.EditorGridPanel Grids}),
63280      * defaults to <tt>false</tt>).
63281      */
63282     /**
63283      * @cfg {String/Object} autoCreate <p>A {@link Ext.DomHelper DomHelper} element spec, or <tt>true</tt> for a default
63284      * element spec. Used to create the {@link Ext.Component#getEl Element} which will encapsulate this Component.
63285      * See <tt>{@link Ext.Component#autoEl autoEl}</tt> for details.  Defaults to:</p>
63286      * <pre><code>{tag: "input", type: "text", size: "24", autocomplete: "off"}</code></pre>
63287      */
63288     /**
63289      * @cfg {Ext.data.Store/Array} store The data source to which this combo is bound (defaults to <tt>undefined</tt>).
63290      * Acceptable values for this property are:
63291      * <div class="mdetail-params"><ul>
63292      * <li><b>any {@link Ext.data.Store Store} subclass</b></li>
63293      * <li><b>an Array</b> : Arrays will be converted to a {@link Ext.data.ArrayStore} internally,
63294      * automatically generating {@link Ext.data.Field#name field names} to work with all data components.
63295      * <div class="mdetail-params"><ul>
63296      * <li><b>1-dimensional array</b> : (e.g., <tt>['Foo','Bar']</tt>)<div class="sub-desc">
63297      * A 1-dimensional array will automatically be expanded (each array item will be used for both the combo
63298      * {@link #valueField} and {@link #displayField})</div></li>
63299      * <li><b>2-dimensional array</b> : (e.g., <tt>[['f','Foo'],['b','Bar']]</tt>)<div class="sub-desc">
63300      * For a multi-dimensional array, the value in index 0 of each item will be assumed to be the combo
63301      * {@link #valueField}, while the value at index 1 is assumed to be the combo {@link #displayField}.
63302      * </div></li></ul></div></li></ul></div>
63303      * <p>See also <tt>{@link #mode}</tt>.</p>
63304      */
63305     /**
63306      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
63307      * the dropdown list (defaults to undefined, with no header element)
63308      */
63309
63310     // private
63311     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
63312     /**
63313      * @cfg {Number} listWidth The width (used as a parameter to {@link Ext.Element#setWidth}) of the dropdown
63314      * list (defaults to the width of the ComboBox field).  See also <tt>{@link #minListWidth}
63315      */
63316     /**
63317      * @cfg {String} displayField The underlying {@link Ext.data.Field#name data field name} to bind to this
63318      * ComboBox (defaults to undefined if <tt>{@link #mode} = 'remote'</tt> or <tt>'field1'</tt> if
63319      * {@link #transform transforming a select} or if the {@link #store field name is autogenerated based on
63320      * the store configuration}).
63321      * <p>See also <tt>{@link #valueField}</tt>.</p>
63322      * <p><b>Note</b>: if using a ComboBox in an {@link Ext.grid.EditorGridPanel Editor Grid} a
63323      * {@link Ext.grid.Column#renderer renderer} will be needed to show the displayField when the editor is not
63324      * active.</p>
63325      */
63326     /**
63327      * @cfg {String} valueField The underlying {@link Ext.data.Field#name data value name} to bind to this
63328      * ComboBox (defaults to undefined if <tt>{@link #mode} = 'remote'</tt> or <tt>'field2'</tt> if
63329      * {@link #transform transforming a select} or if the {@link #store field name is autogenerated based on
63330      * the store configuration}).
63331      * <p><b>Note</b>: use of a <tt>valueField</tt> requires the user to make a selection in order for a value to be
63332      * mapped.  See also <tt>{@link #hiddenName}</tt>, <tt>{@link #hiddenValue}</tt>, and <tt>{@link #displayField}</tt>.</p>
63333      */
63334     /**
63335      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
63336      * field's data value (defaults to the underlying DOM element's name). Required for the combo's value to automatically
63337      * post during a form submission.  See also {@link #valueField}.
63338      */
63339     /**
63340      * @cfg {String} hiddenId If <tt>{@link #hiddenName}</tt> is specified, <tt>hiddenId</tt> can also be provided
63341      * to give the hidden field a unique id.  The <tt>hiddenId</tt> and combo {@link Ext.Component#id id} should be 
63342      * different, since no two DOM nodes should share the same id.
63343      */
63344     /**
63345      * @cfg {String} hiddenValue Sets the initial value of the hidden field if {@link #hiddenName} is
63346      * specified to contain the selected {@link #valueField}, from the Store. Defaults to the configured
63347      * <tt>{@link Ext.form.Field#value value}</tt>.
63348      */
63349     /**
63350      * @cfg {String} listClass The CSS class to add to the predefined <tt>'x-combo-list'</tt> class
63351      * applied the dropdown list element (defaults to '').
63352      */
63353     listClass : '',
63354     /**
63355      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list
63356      * (defaults to <tt>'x-combo-selected'</tt>)
63357      */
63358     selectedClass : 'x-combo-selected',
63359     /**
63360      * @cfg {String} listEmptyText The empty text to display in the data view if no items are found.
63361      * (defaults to '')
63362      */
63363     listEmptyText: '',
63364     /**
63365      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always
63366      * get the class <tt>'x-form-trigger'</tt> and <tt>triggerClass</tt> will be <b>appended</b> if specified
63367      * (defaults to <tt>'x-form-arrow-trigger'</tt> which displays a downward arrow icon).
63368      */
63369     triggerClass : 'x-form-arrow-trigger',
63370     /**
63371      * @cfg {Boolean/String} shadow <tt>true</tt> or <tt>"sides"</tt> for the default effect, <tt>"frame"</tt> for
63372      * 4-way shadow, and <tt>"drop"</tt> for bottom-right
63373      */
63374     shadow : 'sides',
63375     /**
63376      * @cfg {String/Array} listAlign A valid anchor position value. See <tt>{@link Ext.Element#alignTo}</tt> for details
63377      * on supported anchor positions and offsets. To specify x/y offsets as well, this value
63378      * may be specified as an Array of <tt>{@link Ext.Element#alignTo}</tt> method arguments.</p>
63379      * <pre><code>[ 'tl-bl?', [6,0] ]</code></pre>(defaults to <tt>'tl-bl?'</tt>)
63380      */
63381     listAlign : 'tl-bl?',
63382     /**
63383      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown
63384      * (defaults to <tt>300</tt>)
63385      */
63386     maxHeight : 300,
63387     /**
63388      * @cfg {Number} minHeight The minimum height in pixels of the dropdown list when the list is constrained by its
63389      * distance to the viewport edges (defaults to <tt>90</tt>)
63390      */
63391     minHeight : 90,
63392     /**
63393      * @cfg {String} triggerAction The action to execute when the trigger is clicked.
63394      * <div class="mdetail-params"><ul>
63395      * <li><b><tt>'query'</tt></b> : <b>Default</b>
63396      * <p class="sub-desc">{@link #doQuery run the query} using the {@link Ext.form.Field#getRawValue raw value}.</p></li>
63397      * <li><b><tt>'all'</tt></b> :
63398      * <p class="sub-desc">{@link #doQuery run the query} specified by the <tt>{@link #allQuery}</tt> config option</p></li>
63399      * </ul></div>
63400      * <p>See also <code>{@link #queryParam}</code>.</p>
63401      */
63402     triggerAction : 'query',
63403     /**
63404      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and
63405      * {@link #typeAhead} activate (defaults to <tt>4</tt> if <tt>{@link #mode} = 'remote'</tt> or <tt>0</tt> if
63406      * <tt>{@link #mode} = 'local'</tt>, does not apply if
63407      * <tt>{@link Ext.form.TriggerField#editable editable} = false</tt>).
63408      */
63409     minChars : 4,
63410     /**
63411      * @cfg {Boolean} autoSelect <tt>true</tt> to select the first result gathered by the data store (defaults
63412      * to <tt>true</tt>).  A false value would require a manual selection from the dropdown list to set the components value
63413      * unless the value of ({@link #typeAheadDelay}) were true.
63414      */
63415     autoSelect : true,
63416     /**
63417      * @cfg {Boolean} typeAhead <tt>true</tt> to populate and autoselect the remainder of the text being
63418      * typed after a configurable delay ({@link #typeAheadDelay}) if it matches a known value (defaults
63419      * to <tt>false</tt>)
63420      */
63421     typeAhead : false,
63422     /**
63423      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and
63424      * sending the query to filter the dropdown list (defaults to <tt>500</tt> if <tt>{@link #mode} = 'remote'</tt>
63425      * or <tt>10</tt> if <tt>{@link #mode} = 'local'</tt>)
63426      */
63427     queryDelay : 500,
63428     /**
63429      * @cfg {Number} pageSize If greater than <tt>0</tt>, a {@link Ext.PagingToolbar} is displayed in the
63430      * footer of the dropdown list and the {@link #doQuery filter queries} will execute with page start and
63431      * {@link Ext.PagingToolbar#pageSize limit} parameters. Only applies when <tt>{@link #mode} = 'remote'</tt>
63432      * (defaults to <tt>0</tt>).
63433      */
63434     pageSize : 0,
63435     /**
63436      * @cfg {Boolean} selectOnFocus <tt>true</tt> to select any existing text in the field immediately on focus.
63437      * Only applies when <tt>{@link Ext.form.TriggerField#editable editable} = true</tt> (defaults to
63438      * <tt>false</tt>).
63439      */
63440     selectOnFocus : false,
63441     /**
63442      * @cfg {String} queryParam Name of the query ({@link Ext.data.Store#baseParam baseParam} name for the store)
63443      * as it will be passed on the querystring (defaults to <tt>'query'</tt>)
63444      */
63445     queryParam : 'query',
63446     /**
63447      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
63448      * when <tt>{@link #mode} = 'remote'</tt> (defaults to <tt>'Loading...'</tt>)
63449      */
63450     loadingText : 'Loading...',
63451     /**
63452      * @cfg {Boolean} resizable <tt>true</tt> to add a resize handle to the bottom of the dropdown list
63453      * (creates an {@link Ext.Resizable} with 'se' {@link Ext.Resizable#pinned pinned} handles).
63454      * Defaults to <tt>false</tt>.
63455      */
63456     resizable : false,
63457     /**
63458      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if
63459      * <tt>{@link #resizable} = true</tt> (defaults to <tt>8</tt>)
63460      */
63461     handleHeight : 8,
63462     /**
63463      * @cfg {String} allQuery The text query to send to the server to return all records for the list
63464      * with no filtering (defaults to '')
63465      */
63466     allQuery: '',
63467     /**
63468      * @cfg {String} mode Acceptable values are:
63469      * <div class="mdetail-params"><ul>
63470      * <li><b><tt>'remote'</tt></b> : <b>Default</b>
63471      * <p class="sub-desc">Automatically loads the <tt>{@link #store}</tt> the <b>first</b> time the trigger
63472      * is clicked. If you do not want the store to be automatically loaded the first time the trigger is
63473      * clicked, set to <tt>'local'</tt> and manually load the store.  To force a requery of the store
63474      * <b>every</b> time the trigger is clicked see <tt>{@link #lastQuery}</tt>.</p></li>
63475      * <li><b><tt>'local'</tt></b> :
63476      * <p class="sub-desc">ComboBox loads local data</p>
63477      * <pre><code>
63478 var combo = new Ext.form.ComboBox({
63479     renderTo: document.body,
63480     mode: 'local',
63481     store: new Ext.data.ArrayStore({
63482         id: 0,
63483         fields: [
63484             'myId',  // numeric value is the key
63485             'displayText'
63486         ],
63487         data: [[1, 'item1'], [2, 'item2']]  // data is local
63488     }),
63489     valueField: 'myId',
63490     displayField: 'displayText',
63491     triggerAction: 'all'
63492 });
63493      * </code></pre></li>
63494      * </ul></div>
63495      */
63496     mode: 'remote',
63497     /**
63498      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to <tt>70</tt>, will
63499      * be ignored if <tt>{@link #listWidth}</tt> has a higher value)
63500      */
63501     minListWidth : 70,
63502     /**
63503      * @cfg {Boolean} forceSelection <tt>true</tt> to restrict the selected value to one of the values in the list,
63504      * <tt>false</tt> to allow the user to set arbitrary text into the field (defaults to <tt>false</tt>)
63505      */
63506     forceSelection : false,
63507     /**
63508      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
63509      * if <tt>{@link #typeAhead} = true</tt> (defaults to <tt>250</tt>)
63510      */
63511     typeAheadDelay : 250,
63512     /**
63513      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
63514      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined). If this
63515      * default text is used, it means there is no value set and no validation will occur on this field.
63516      */
63517
63518     /**
63519      * @cfg {Boolean} lazyInit <tt>true</tt> to not initialize the list for this combo until the field is focused
63520      * (defaults to <tt>true</tt>)
63521      */
63522     lazyInit : true,
63523
63524     /**
63525      * @cfg {Boolean} clearFilterOnReset <tt>true</tt> to clear any filters on the store (when in local mode) when reset is called
63526      * (defaults to <tt>true</tt>)
63527      */
63528     clearFilterOnReset : true,
63529
63530     /**
63531      * @cfg {Boolean} submitValue False to clear the name attribute on the field so that it is not submitted during a form post.
63532      * If a hiddenName is specified, setting this to true will cause both the hidden field and the element to be submitted.
63533      * Defaults to <tt>undefined</tt>.
63534      */
63535     submitValue: undefined,
63536
63537     /**
63538      * The value of the match string used to filter the store. Delete this property to force a requery.
63539      * Example use:
63540      * <pre><code>
63541 var combo = new Ext.form.ComboBox({
63542     ...
63543     mode: 'remote',
63544     ...
63545     listeners: {
63546         // delete the previous query in the beforequery event or set
63547         // combo.lastQuery = null (this will reload the store the next time it expands)
63548         beforequery: function(qe){
63549             delete qe.combo.lastQuery;
63550         }
63551     }
63552 });
63553      * </code></pre>
63554      * To make sure the filter in the store is not cleared the first time the ComboBox trigger is used
63555      * configure the combo with <tt>lastQuery=''</tt>. Example use:
63556      * <pre><code>
63557 var combo = new Ext.form.ComboBox({
63558     ...
63559     mode: 'local',
63560     triggerAction: 'all',
63561     lastQuery: ''
63562 });
63563      * </code></pre>
63564      * @property lastQuery
63565      * @type String
63566      */
63567
63568     // private
63569     initComponent : function(){
63570         Ext.form.ComboBox.superclass.initComponent.call(this);
63571         this.addEvents(
63572             /**
63573              * @event expand
63574              * Fires when the dropdown list is expanded
63575              * @param {Ext.form.ComboBox} combo This combo box
63576              */
63577             'expand',
63578             /**
63579              * @event collapse
63580              * Fires when the dropdown list is collapsed
63581              * @param {Ext.form.ComboBox} combo This combo box
63582              */
63583             'collapse',
63584
63585             /**
63586              * @event beforeselect
63587              * Fires before a list item is selected. Return false to cancel the selection.
63588              * @param {Ext.form.ComboBox} combo This combo box
63589              * @param {Ext.data.Record} record The data record returned from the underlying store
63590              * @param {Number} index The index of the selected item in the dropdown list
63591              */
63592             'beforeselect',
63593             /**
63594              * @event select
63595              * Fires when a list item is selected
63596              * @param {Ext.form.ComboBox} combo This combo box
63597              * @param {Ext.data.Record} record The data record returned from the underlying store
63598              * @param {Number} index The index of the selected item in the dropdown list
63599              */
63600             'select',
63601             /**
63602              * @event beforequery
63603              * Fires before all queries are processed. Return false to cancel the query or set the queryEvent's
63604              * cancel property to true.
63605              * @param {Object} queryEvent An object that has these properties:<ul>
63606              * <li><code>combo</code> : Ext.form.ComboBox <div class="sub-desc">This combo box</div></li>
63607              * <li><code>query</code> : String <div class="sub-desc">The query</div></li>
63608              * <li><code>forceAll</code> : Boolean <div class="sub-desc">True to force "all" query</div></li>
63609              * <li><code>cancel</code> : Boolean <div class="sub-desc">Set to true to cancel the query</div></li>
63610              * </ul>
63611              */
63612             'beforequery'
63613         );
63614         if(this.transform){
63615             var s = Ext.getDom(this.transform);
63616             if(!this.hiddenName){
63617                 this.hiddenName = s.name;
63618             }
63619             if(!this.store){
63620                 this.mode = 'local';
63621                 var d = [], opts = s.options;
63622                 for(var i = 0, len = opts.length;i < len; i++){
63623                     var o = opts[i],
63624                         value = (o.hasAttribute ? o.hasAttribute('value') : o.getAttributeNode('value').specified) ? o.value : o.text;
63625                     if(o.selected && Ext.isEmpty(this.value, true)) {
63626                         this.value = value;
63627                     }
63628                     d.push([value, o.text]);
63629                 }
63630                 this.store = new Ext.data.ArrayStore({
63631                     idIndex: 0,
63632                     fields: ['value', 'text'],
63633                     data : d,
63634                     autoDestroy: true
63635                 });
63636                 this.valueField = 'value';
63637                 this.displayField = 'text';
63638             }
63639             s.name = Ext.id(); // wipe out the name in case somewhere else they have a reference
63640             if(!this.lazyRender){
63641                 this.target = true;
63642                 this.el = Ext.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
63643                 this.render(this.el.parentNode, s);
63644             }
63645             Ext.removeNode(s);
63646         }
63647         //auto-configure store from local array data
63648         else if(this.store){
63649             this.store = Ext.StoreMgr.lookup(this.store);
63650             if(this.store.autoCreated){
63651                 this.displayField = this.valueField = 'field1';
63652                 if(!this.store.expandData){
63653                     this.displayField = 'field2';
63654                 }
63655                 this.mode = 'local';
63656             }
63657         }
63658
63659         this.selectedIndex = -1;
63660         if(this.mode == 'local'){
63661             if(!Ext.isDefined(this.initialConfig.queryDelay)){
63662                 this.queryDelay = 10;
63663             }
63664             if(!Ext.isDefined(this.initialConfig.minChars)){
63665                 this.minChars = 0;
63666             }
63667         }
63668     },
63669
63670     // private
63671     onRender : function(ct, position){
63672         if(this.hiddenName && !Ext.isDefined(this.submitValue)){
63673             this.submitValue = false;
63674         }
63675         Ext.form.ComboBox.superclass.onRender.call(this, ct, position);
63676         if(this.hiddenName){
63677             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName,
63678                     id: (this.hiddenId || Ext.id())}, 'before', true);
63679
63680         }
63681         if(Ext.isGecko){
63682             this.el.dom.setAttribute('autocomplete', 'off');
63683         }
63684
63685         if(!this.lazyInit){
63686             this.initList();
63687         }else{
63688             this.on('focus', this.initList, this, {single: true});
63689         }
63690     },
63691
63692     // private
63693     initValue : function(){
63694         Ext.form.ComboBox.superclass.initValue.call(this);
63695         if(this.hiddenField){
63696             this.hiddenField.value =
63697                 Ext.value(Ext.isDefined(this.hiddenValue) ? this.hiddenValue : this.value, '');
63698         }
63699     },
63700
63701     getParentZIndex : function(){
63702         var zindex;
63703         if (this.ownerCt){
63704             this.findParentBy(function(ct){
63705                 zindex = parseInt(ct.getPositionEl().getStyle('z-index'), 10);
63706                 return !!zindex;
63707             });
63708         }
63709         return zindex;
63710     },
63711     
63712     getZIndex : function(listParent){
63713         listParent = listParent || Ext.getDom(this.getListParent() || Ext.getBody());
63714         var zindex = parseInt(Ext.fly(listParent).getStyle('z-index'), 10);
63715         if(!zindex){
63716             zindex = this.getParentZIndex();
63717         }
63718         return (zindex || 12000) + 5;
63719     },
63720
63721     // private
63722     initList : function(){
63723         if(!this.list){
63724             var cls = 'x-combo-list',
63725                 listParent = Ext.getDom(this.getListParent() || Ext.getBody());
63726
63727             this.list = new Ext.Layer({
63728                 parentEl: listParent,
63729                 shadow: this.shadow,
63730                 cls: [cls, this.listClass].join(' '),
63731                 constrain:false,
63732                 zindex: this.getZIndex(listParent)
63733             });
63734
63735             var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
63736             this.list.setSize(lw, 0);
63737             this.list.swallowEvent('mousewheel');
63738             this.assetHeight = 0;
63739             if(this.syncFont !== false){
63740                 this.list.setStyle('font-size', this.el.getStyle('font-size'));
63741             }
63742             if(this.title){
63743                 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
63744                 this.assetHeight += this.header.getHeight();
63745             }
63746
63747             this.innerList = this.list.createChild({cls:cls+'-inner'});
63748             this.mon(this.innerList, 'mouseover', this.onViewOver, this);
63749             this.mon(this.innerList, 'mousemove', this.onViewMove, this);
63750             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
63751
63752             if(this.pageSize){
63753                 this.footer = this.list.createChild({cls:cls+'-ft'});
63754                 this.pageTb = new Ext.PagingToolbar({
63755                     store: this.store,
63756                     pageSize: this.pageSize,
63757                     renderTo:this.footer
63758                 });
63759                 this.assetHeight += this.footer.getHeight();
63760             }
63761
63762             if(!this.tpl){
63763                 /**
63764                 * @cfg {String/Ext.XTemplate} tpl <p>The template string, or {@link Ext.XTemplate} instance to
63765                 * use to display each item in the dropdown list. The dropdown list is displayed in a
63766                 * DataView. See {@link #view}.</p>
63767                 * <p>The default template string is:</p><pre><code>
63768                   '&lt;tpl for=".">&lt;div class="x-combo-list-item">{' + this.displayField + '}&lt;/div>&lt;/tpl>'
63769                 * </code></pre>
63770                 * <p>Override the default value to create custom UI layouts for items in the list.
63771                 * For example:</p><pre><code>
63772                   '&lt;tpl for=".">&lt;div ext:qtip="{state}. {nick}" class="x-combo-list-item">{state}&lt;/div>&lt;/tpl>'
63773                 * </code></pre>
63774                 * <p>The template <b>must</b> contain one or more substitution parameters using field
63775                 * names from the Combo's</b> {@link #store Store}. In the example above an
63776                 * <pre>ext:qtip</pre> attribute is added to display other fields from the Store.</p>
63777                 * <p>To preserve the default visual look of list items, add the CSS class name
63778                 * <pre>x-combo-list-item</pre> to the template's container element.</p>
63779                 * <p>Also see {@link #itemSelector} for additional details.</p>
63780                 */
63781                 this.tpl = '<tpl for="."><div class="'+cls+'-item">{' + this.displayField + '}</div></tpl>';
63782                 /**
63783                  * @cfg {String} itemSelector
63784                  * <p>A simple CSS selector (e.g. div.some-class or span:first-child) that will be
63785                  * used to determine what nodes the {@link #view Ext.DataView} which handles the dropdown
63786                  * display will be working with.</p>
63787                  * <p><b>Note</b>: this setting is <b>required</b> if a custom XTemplate has been
63788                  * specified in {@link #tpl} which assigns a class other than <pre>'x-combo-list-item'</pre>
63789                  * to dropdown list items</b>
63790                  */
63791             }
63792
63793             /**
63794             * The {@link Ext.DataView DataView} used to display the ComboBox's options.
63795             * @type Ext.DataView
63796             */
63797             this.view = new Ext.DataView({
63798                 applyTo: this.innerList,
63799                 tpl: this.tpl,
63800                 singleSelect: true,
63801                 selectedClass: this.selectedClass,
63802                 itemSelector: this.itemSelector || '.' + cls + '-item',
63803                 emptyText: this.listEmptyText,
63804                 deferEmptyText: false
63805             });
63806
63807             this.mon(this.view, {
63808                 containerclick : this.onViewClick,
63809                 click : this.onViewClick,
63810                 scope :this
63811             });
63812
63813             this.bindStore(this.store, true);
63814
63815             if(this.resizable){
63816                 this.resizer = new Ext.Resizable(this.list,  {
63817                    pinned:true, handles:'se'
63818                 });
63819                 this.mon(this.resizer, 'resize', function(r, w, h){
63820                     this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
63821                     this.listWidth = w;
63822                     this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
63823                     this.restrictHeight();
63824                 }, this);
63825
63826                 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
63827             }
63828         }
63829     },
63830
63831     /**
63832      * <p>Returns the element used to house this ComboBox's pop-up list. Defaults to the document body.</p>
63833      * A custom implementation may be provided as a configuration option if the floating list needs to be rendered
63834      * to a different Element. An example might be rendering the list inside a Menu so that clicking
63835      * the list does not hide the Menu:<pre><code>
63836 var store = new Ext.data.ArrayStore({
63837     autoDestroy: true,
63838     fields: ['initials', 'fullname'],
63839     data : [
63840         ['FF', 'Fred Flintstone'],
63841         ['BR', 'Barney Rubble']
63842     ]
63843 });
63844
63845 var combo = new Ext.form.ComboBox({
63846     store: store,
63847     displayField: 'fullname',
63848     emptyText: 'Select a name...',
63849     forceSelection: true,
63850     getListParent: function() {
63851         return this.el.up('.x-menu');
63852     },
63853     iconCls: 'no-icon', //use iconCls if placing within menu to shift to right side of menu
63854     mode: 'local',
63855     selectOnFocus: true,
63856     triggerAction: 'all',
63857     typeAhead: true,
63858     width: 135
63859 });
63860
63861 var menu = new Ext.menu.Menu({
63862     id: 'mainMenu',
63863     items: [
63864         combo // A Field in a Menu
63865     ]
63866 });
63867 </code></pre>
63868      */
63869     getListParent : function() {
63870         return document.body;
63871     },
63872
63873     /**
63874      * Returns the store associated with this combo.
63875      * @return {Ext.data.Store} The store
63876      */
63877     getStore : function(){
63878         return this.store;
63879     },
63880
63881     // private
63882     bindStore : function(store, initial){
63883         if(this.store && !initial){
63884             if(this.store !== store && this.store.autoDestroy){
63885                 this.store.destroy();
63886             }else{
63887                 this.store.un('beforeload', this.onBeforeLoad, this);
63888                 this.store.un('load', this.onLoad, this);
63889                 this.store.un('exception', this.collapse, this);
63890             }
63891             if(!store){
63892                 this.store = null;
63893                 if(this.view){
63894                     this.view.bindStore(null);
63895                 }
63896                 if(this.pageTb){
63897                     this.pageTb.bindStore(null);
63898                 }
63899             }
63900         }
63901         if(store){
63902             if(!initial) {
63903                 this.lastQuery = null;
63904                 if(this.pageTb) {
63905                     this.pageTb.bindStore(store);
63906                 }
63907             }
63908
63909             this.store = Ext.StoreMgr.lookup(store);
63910             this.store.on({
63911                 scope: this,
63912                 beforeload: this.onBeforeLoad,
63913                 load: this.onLoad,
63914                 exception: this.collapse
63915             });
63916
63917             if(this.view){
63918                 this.view.bindStore(store);
63919             }
63920         }
63921     },
63922
63923     reset : function(){
63924         if(this.clearFilterOnReset && this.mode == 'local'){
63925             this.store.clearFilter();
63926         }
63927         Ext.form.ComboBox.superclass.reset.call(this);
63928     },
63929
63930     // private
63931     initEvents : function(){
63932         Ext.form.ComboBox.superclass.initEvents.call(this);
63933
63934         /**
63935          * @property keyNav
63936          * @type Ext.KeyNav
63937          * <p>A {@link Ext.KeyNav KeyNav} object which handles navigation keys for this ComboBox. This performs actions
63938          * based on keystrokes typed when the input field is focused.</p>
63939          * <p><b>After the ComboBox has been rendered</b>, you may override existing navigation key functionality,
63940          * or add your own based upon key names as specified in the {@link Ext.KeyNav KeyNav} class.</p>
63941          * <p>The function is executed in the scope (<code>this</code> reference of the ComboBox. Example:</p><pre><code>
63942 myCombo.keyNav.esc = function(e) {  // Override ESC handling function
63943     this.collapse();                // Standard behaviour of Ext's ComboBox.
63944     this.setValue(this.startValue); // We reset to starting value on ESC
63945 };
63946 myCombo.keyNav.tab = function() {   // Override TAB handling function
63947     this.onViewClick(false);        // Select the currently highlighted row
63948 };
63949 </code></pre>
63950          */
63951         this.keyNav = new Ext.KeyNav(this.el, {
63952             "up" : function(e){
63953                 this.inKeyMode = true;
63954                 this.selectPrev();
63955             },
63956
63957             "down" : function(e){
63958                 if(!this.isExpanded()){
63959                     this.onTriggerClick();
63960                 }else{
63961                     this.inKeyMode = true;
63962                     this.selectNext();
63963                 }
63964             },
63965
63966             "enter" : function(e){
63967                 this.onViewClick();
63968             },
63969
63970             "esc" : function(e){
63971                 this.collapse();
63972             },
63973
63974             "tab" : function(e){
63975                 if (this.forceSelection === true) {
63976                     this.collapse();
63977                 } else {
63978                     this.onViewClick(false);
63979                 }
63980                 return true;
63981             },
63982
63983             scope : this,
63984
63985             doRelay : function(e, h, hname){
63986                 if(hname == 'down' || this.scope.isExpanded()){
63987                     // this MUST be called before ComboBox#fireKey()
63988                     var relay = Ext.KeyNav.prototype.doRelay.apply(this, arguments);
63989                     if(!Ext.isIE && Ext.EventManager.useKeydown){
63990                         // call Combo#fireKey() for browsers which use keydown event (except IE)
63991                         this.scope.fireKey(e);
63992                     }
63993                     return relay;
63994                 }
63995                 return true;
63996             },
63997
63998             forceKeyDown : true,
63999             defaultEventAction: 'stopEvent'
64000         });
64001         this.queryDelay = Math.max(this.queryDelay || 10,
64002                 this.mode == 'local' ? 10 : 250);
64003         this.dqTask = new Ext.util.DelayedTask(this.initQuery, this);
64004         if(this.typeAhead){
64005             this.taTask = new Ext.util.DelayedTask(this.onTypeAhead, this);
64006         }
64007         if(!this.enableKeyEvents){
64008             this.mon(this.el, 'keyup', this.onKeyUp, this);
64009         }
64010     },
64011
64012
64013     // private
64014     onDestroy : function(){
64015         if (this.dqTask){
64016             this.dqTask.cancel();
64017             this.dqTask = null;
64018         }
64019         this.bindStore(null);
64020         Ext.destroy(
64021             this.resizer,
64022             this.view,
64023             this.pageTb,
64024             this.list
64025         );
64026         Ext.destroyMembers(this, 'hiddenField');
64027         Ext.form.ComboBox.superclass.onDestroy.call(this);
64028     },
64029
64030     // private
64031     fireKey : function(e){
64032         if (!this.isExpanded()) {
64033             Ext.form.ComboBox.superclass.fireKey.call(this, e);
64034         }
64035     },
64036
64037     // private
64038     onResize : function(w, h){
64039         Ext.form.ComboBox.superclass.onResize.apply(this, arguments);
64040         if(!isNaN(w) && this.isVisible() && this.list){
64041             this.doResize(w);
64042         }else{
64043             this.bufferSize = w;
64044         }
64045     },
64046
64047     doResize: function(w){
64048         if(!Ext.isDefined(this.listWidth)){
64049             var lw = Math.max(w, this.minListWidth);
64050             this.list.setWidth(lw);
64051             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
64052         }
64053     },
64054
64055     // private
64056     onEnable : function(){
64057         Ext.form.ComboBox.superclass.onEnable.apply(this, arguments);
64058         if(this.hiddenField){
64059             this.hiddenField.disabled = false;
64060         }
64061     },
64062
64063     // private
64064     onDisable : function(){
64065         Ext.form.ComboBox.superclass.onDisable.apply(this, arguments);
64066         if(this.hiddenField){
64067             this.hiddenField.disabled = true;
64068         }
64069     },
64070
64071     // private
64072     onBeforeLoad : function(){
64073         if(!this.hasFocus){
64074             return;
64075         }
64076         this.innerList.update(this.loadingText ?
64077                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
64078         this.restrictHeight();
64079         this.selectedIndex = -1;
64080     },
64081
64082     // private
64083     onLoad : function(){
64084         if(!this.hasFocus){
64085             return;
64086         }
64087         if(this.store.getCount() > 0 || this.listEmptyText){
64088             this.expand();
64089             this.restrictHeight();
64090             if(this.lastQuery == this.allQuery){
64091                 if(this.editable){
64092                     this.el.dom.select();
64093                 }
64094
64095                 if(this.autoSelect !== false && !this.selectByValue(this.value, true)){
64096                     this.select(0, true);
64097                 }
64098             }else{
64099                 if(this.autoSelect !== false){
64100                     this.selectNext();
64101                 }
64102                 if(this.typeAhead && this.lastKey != Ext.EventObject.BACKSPACE && this.lastKey != Ext.EventObject.DELETE){
64103                     this.taTask.delay(this.typeAheadDelay);
64104                 }
64105             }
64106         }else{
64107             this.collapse();
64108         }
64109
64110     },
64111
64112     // private
64113     onTypeAhead : function(){
64114         if(this.store.getCount() > 0){
64115             var r = this.store.getAt(0);
64116             var newValue = r.data[this.displayField];
64117             var len = newValue.length;
64118             var selStart = this.getRawValue().length;
64119             if(selStart != len){
64120                 this.setRawValue(newValue);
64121                 this.selectText(selStart, newValue.length);
64122             }
64123         }
64124     },
64125
64126     // private
64127     assertValue : function(){
64128         var val = this.getRawValue(),
64129             rec;
64130
64131         if(this.valueField && Ext.isDefined(this.value)){
64132             rec = this.findRecord(this.valueField, this.value);
64133         }
64134         if(!rec || rec.get(this.displayField) != val){
64135             rec = this.findRecord(this.displayField, val);
64136         }
64137         if(!rec && this.forceSelection){
64138             if(val.length > 0 && val != this.emptyText){
64139                 this.el.dom.value = Ext.value(this.lastSelectionText, '');
64140                 this.applyEmptyText();
64141             }else{
64142                 this.clearValue();
64143             }
64144         }else{
64145             if(rec && this.valueField){
64146                 // onSelect may have already set the value and by doing so
64147                 // set the display field properly.  Let's not wipe out the
64148                 // valueField here by just sending the displayField.
64149                 if (this.value == val){
64150                     return;
64151                 }
64152                 val = rec.get(this.valueField || this.displayField);
64153             }
64154             this.setValue(val);
64155         }
64156     },
64157
64158     // private
64159     onSelect : function(record, index){
64160         if(this.fireEvent('beforeselect', this, record, index) !== false){
64161             this.setValue(record.data[this.valueField || this.displayField]);
64162             this.collapse();
64163             this.fireEvent('select', this, record, index);
64164         }
64165     },
64166
64167     // inherit docs
64168     getName: function(){
64169         var hf = this.hiddenField;
64170         return hf && hf.name ? hf.name : this.hiddenName || Ext.form.ComboBox.superclass.getName.call(this);
64171     },
64172
64173     /**
64174      * Returns the currently selected field value or empty string if no value is set.
64175      * @return {String} value The selected value
64176      */
64177     getValue : function(){
64178         if(this.valueField){
64179             return Ext.isDefined(this.value) ? this.value : '';
64180         }else{
64181             return Ext.form.ComboBox.superclass.getValue.call(this);
64182         }
64183     },
64184
64185     /**
64186      * Clears any text/value currently set in the field
64187      */
64188     clearValue : function(){
64189         if(this.hiddenField){
64190             this.hiddenField.value = '';
64191         }
64192         this.setRawValue('');
64193         this.lastSelectionText = '';
64194         this.applyEmptyText();
64195         this.value = '';
64196     },
64197
64198     /**
64199      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
64200      * will be displayed in the field.  If the value does not match the data value of an existing item,
64201      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
64202      * Otherwise the field will be blank (although the value will still be set).
64203      * @param {String} value The value to match
64204      * @return {Ext.form.Field} this
64205      */
64206     setValue : function(v){
64207         var text = v;
64208         if(this.valueField){
64209             var r = this.findRecord(this.valueField, v);
64210             if(r){
64211                 text = r.data[this.displayField];
64212             }else if(Ext.isDefined(this.valueNotFoundText)){
64213                 text = this.valueNotFoundText;
64214             }
64215         }
64216         this.lastSelectionText = text;
64217         if(this.hiddenField){
64218             this.hiddenField.value = Ext.value(v, '');
64219         }
64220         Ext.form.ComboBox.superclass.setValue.call(this, text);
64221         this.value = v;
64222         return this;
64223     },
64224
64225     // private
64226     findRecord : function(prop, value){
64227         var record;
64228         if(this.store.getCount() > 0){
64229             this.store.each(function(r){
64230                 if(r.data[prop] == value){
64231                     record = r;
64232                     return false;
64233                 }
64234             });
64235         }
64236         return record;
64237     },
64238
64239     // private
64240     onViewMove : function(e, t){
64241         this.inKeyMode = false;
64242     },
64243
64244     // private
64245     onViewOver : function(e, t){
64246         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
64247             return;
64248         }
64249         var item = this.view.findItemFromChild(t);
64250         if(item){
64251             var index = this.view.indexOf(item);
64252             this.select(index, false);
64253         }
64254     },
64255
64256     // private
64257     onViewClick : function(doFocus){
64258         var index = this.view.getSelectedIndexes()[0],
64259             s = this.store,
64260             r = s.getAt(index);
64261         if(r){
64262             this.onSelect(r, index);
64263         }else {
64264             this.collapse();
64265         }
64266         if(doFocus !== false){
64267             this.el.focus();
64268         }
64269     },
64270
64271
64272     // private
64273     restrictHeight : function(){
64274         this.innerList.dom.style.height = '';
64275         var inner = this.innerList.dom,
64276             pad = this.list.getFrameWidth('tb') + (this.resizable ? this.handleHeight : 0) + this.assetHeight,
64277             h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight),
64278             ha = this.getPosition()[1]-Ext.getBody().getScroll().top,
64279             hb = Ext.lib.Dom.getViewHeight()-ha-this.getSize().height,
64280             space = Math.max(ha, hb, this.minHeight || 0)-this.list.shadowOffset-pad-5;
64281
64282         h = Math.min(h, space, this.maxHeight);
64283
64284         this.innerList.setHeight(h);
64285         this.list.beginUpdate();
64286         this.list.setHeight(h+pad);
64287         this.list.alignTo.apply(this.list, [this.el].concat(this.listAlign));
64288         this.list.endUpdate();
64289     },
64290
64291     /**
64292      * Returns true if the dropdown list is expanded, else false.
64293      */
64294     isExpanded : function(){
64295         return this.list && this.list.isVisible();
64296     },
64297
64298     /**
64299      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
64300      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
64301      * @param {String} value The data value of the item to select
64302      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
64303      * selected item if it is not currently in view (defaults to true)
64304      * @return {Boolean} True if the value matched an item in the list, else false
64305      */
64306     selectByValue : function(v, scrollIntoView){
64307         if(!Ext.isEmpty(v, true)){
64308             var r = this.findRecord(this.valueField || this.displayField, v);
64309             if(r){
64310                 this.select(this.store.indexOf(r), scrollIntoView);
64311                 return true;
64312             }
64313         }
64314         return false;
64315     },
64316
64317     /**
64318      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
64319      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
64320      * @param {Number} index The zero-based index of the list item to select
64321      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
64322      * selected item if it is not currently in view (defaults to true)
64323      */
64324     select : function(index, scrollIntoView){
64325         this.selectedIndex = index;
64326         this.view.select(index);
64327         if(scrollIntoView !== false){
64328             var el = this.view.getNode(index);
64329             if(el){
64330                 this.innerList.scrollChildIntoView(el, false);
64331             }
64332         }
64333
64334     },
64335
64336     // private
64337     selectNext : function(){
64338         var ct = this.store.getCount();
64339         if(ct > 0){
64340             if(this.selectedIndex == -1){
64341                 this.select(0);
64342             }else if(this.selectedIndex < ct-1){
64343                 this.select(this.selectedIndex+1);
64344             }
64345         }
64346     },
64347
64348     // private
64349     selectPrev : function(){
64350         var ct = this.store.getCount();
64351         if(ct > 0){
64352             if(this.selectedIndex == -1){
64353                 this.select(0);
64354             }else if(this.selectedIndex !== 0){
64355                 this.select(this.selectedIndex-1);
64356             }
64357         }
64358     },
64359
64360     // private
64361     onKeyUp : function(e){
64362         var k = e.getKey();
64363         if(this.editable !== false && this.readOnly !== true && (k == e.BACKSPACE || !e.isSpecialKey())){
64364
64365             this.lastKey = k;
64366             this.dqTask.delay(this.queryDelay);
64367         }
64368         Ext.form.ComboBox.superclass.onKeyUp.call(this, e);
64369     },
64370
64371     // private
64372     validateBlur : function(){
64373         return !this.list || !this.list.isVisible();
64374     },
64375
64376     // private
64377     initQuery : function(){
64378         this.doQuery(this.getRawValue());
64379     },
64380
64381     // private
64382     beforeBlur : function(){
64383         this.assertValue();
64384     },
64385
64386     // private
64387     postBlur  : function(){
64388         Ext.form.ComboBox.superclass.postBlur.call(this);
64389         this.collapse();
64390         this.inKeyMode = false;
64391     },
64392
64393     /**
64394      * Execute a query to filter the dropdown list.  Fires the {@link #beforequery} event prior to performing the
64395      * query allowing the query action to be canceled if needed.
64396      * @param {String} query The SQL query to execute
64397      * @param {Boolean} forceAll <tt>true</tt> to force the query to execute even if there are currently fewer
64398      * characters in the field than the minimum specified by the <tt>{@link #minChars}</tt> config option.  It
64399      * also clears any filter previously saved in the current store (defaults to <tt>false</tt>)
64400      */
64401     doQuery : function(q, forceAll){
64402         q = Ext.isEmpty(q) ? '' : q;
64403         var qe = {
64404             query: q,
64405             forceAll: forceAll,
64406             combo: this,
64407             cancel:false
64408         };
64409         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
64410             return false;
64411         }
64412         q = qe.query;
64413         forceAll = qe.forceAll;
64414         if(forceAll === true || (q.length >= this.minChars)){
64415             if(this.lastQuery !== q){
64416                 this.lastQuery = q;
64417                 if(this.mode == 'local'){
64418                     this.selectedIndex = -1;
64419                     if(forceAll){
64420                         this.store.clearFilter();
64421                     }else{
64422                         this.store.filter(this.displayField, q);
64423                     }
64424                     this.onLoad();
64425                 }else{
64426                     this.store.baseParams[this.queryParam] = q;
64427                     this.store.load({
64428                         params: this.getParams(q)
64429                     });
64430                     this.expand();
64431                 }
64432             }else{
64433                 this.selectedIndex = -1;
64434                 this.onLoad();
64435             }
64436         }
64437     },
64438
64439     // private
64440     getParams : function(q){
64441         var params = {},
64442             paramNames = this.store.paramNames;
64443         if(this.pageSize){
64444             params[paramNames.start] = 0;
64445             params[paramNames.limit] = this.pageSize;
64446         }
64447         return params;
64448     },
64449
64450     /**
64451      * Hides the dropdown list if it is currently expanded. Fires the {@link #collapse} event on completion.
64452      */
64453     collapse : function(){
64454         if(!this.isExpanded()){
64455             return;
64456         }
64457         this.list.hide();
64458         Ext.getDoc().un('mousewheel', this.collapseIf, this);
64459         Ext.getDoc().un('mousedown', this.collapseIf, this);
64460         this.fireEvent('collapse', this);
64461     },
64462
64463     // private
64464     collapseIf : function(e){
64465         if(!this.isDestroyed && !e.within(this.wrap) && !e.within(this.list)){
64466             this.collapse();
64467         }
64468     },
64469
64470     /**
64471      * Expands the dropdown list if it is currently hidden. Fires the {@link #expand} event on completion.
64472      */
64473     expand : function(){
64474         if(this.isExpanded() || !this.hasFocus){
64475             return;
64476         }
64477
64478         if(this.title || this.pageSize){
64479             this.assetHeight = 0;
64480             if(this.title){
64481                 this.assetHeight += this.header.getHeight();
64482             }
64483             if(this.pageSize){
64484                 this.assetHeight += this.footer.getHeight();
64485             }
64486         }
64487
64488         if(this.bufferSize){
64489             this.doResize(this.bufferSize);
64490             delete this.bufferSize;
64491         }
64492         this.list.alignTo.apply(this.list, [this.el].concat(this.listAlign));
64493
64494         // zindex can change, re-check it and set it if necessary
64495         this.list.setZIndex(this.getZIndex());
64496         this.list.show();
64497         if(Ext.isGecko2){
64498             this.innerList.setOverflow('auto'); // necessary for FF 2.0/Mac
64499         }
64500         this.mon(Ext.getDoc(), {
64501             scope: this,
64502             mousewheel: this.collapseIf,
64503             mousedown: this.collapseIf
64504         });
64505         this.fireEvent('expand', this);
64506     },
64507
64508     /**
64509      * @method onTriggerClick
64510      * @hide
64511      */
64512     // private
64513     // Implements the default empty TriggerField.onTriggerClick function
64514     onTriggerClick : function(){
64515         if(this.readOnly || this.disabled){
64516             return;
64517         }
64518         if(this.isExpanded()){
64519             this.collapse();
64520             this.el.focus();
64521         }else {
64522             this.onFocus({});
64523             if(this.triggerAction == 'all') {
64524                 this.doQuery(this.allQuery, true);
64525             } else {
64526                 this.doQuery(this.getRawValue());
64527             }
64528             this.el.focus();
64529         }
64530     }
64531
64532     /**
64533      * @hide
64534      * @method autoSize
64535      */
64536     /**
64537      * @cfg {Boolean} grow @hide
64538      */
64539     /**
64540      * @cfg {Number} growMin @hide
64541      */
64542     /**
64543      * @cfg {Number} growMax @hide
64544      */
64545
64546 });
64547 Ext.reg('combo', Ext.form.ComboBox);
64548 /**
64549  * @class Ext.form.Checkbox
64550  * @extends Ext.form.Field
64551  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
64552  * @constructor
64553  * Creates a new Checkbox
64554  * @param {Object} config Configuration options
64555  * @xtype checkbox
64556  */
64557 Ext.form.Checkbox = Ext.extend(Ext.form.Field,  {
64558     /**
64559      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
64560      */
64561     focusClass : undefined,
64562     /**
64563      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to 'x-form-field')
64564      */
64565     fieldClass : 'x-form-field',
64566     /**
64567      * @cfg {Boolean} checked <tt>true</tt> if the checkbox should render initially checked (defaults to <tt>false</tt>)
64568      */
64569     checked : false,
64570     /**
64571      * @cfg {String} boxLabel The text that appears beside the checkbox
64572      */
64573     boxLabel: '&#160;',
64574     /**
64575      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
64576      * {tag: 'input', type: 'checkbox', autocomplete: 'off'})
64577      */
64578     defaultAutoCreate : { tag: 'input', type: 'checkbox', autocomplete: 'off'},
64579     /**
64580      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
64581      */
64582     /**
64583      * @cfg {Function} handler A function called when the {@link #checked} value changes (can be used instead of
64584      * handling the check event). The handler is passed the following parameters:
64585      * <div class="mdetail-params"><ul>
64586      * <li><b>checkbox</b> : Ext.form.Checkbox<div class="sub-desc">The Checkbox being toggled.</div></li>
64587      * <li><b>checked</b> : Boolean<div class="sub-desc">The new checked state of the checkbox.</div></li>
64588      * </ul></div>
64589      */
64590     /**
64591      * @cfg {Object} scope An object to use as the scope ('this' reference) of the {@link #handler} function
64592      * (defaults to this Checkbox).
64593      */
64594
64595     // private
64596     actionMode : 'wrap',
64597
64598         // private
64599     initComponent : function(){
64600         Ext.form.Checkbox.superclass.initComponent.call(this);
64601         this.addEvents(
64602             /**
64603              * @event check
64604              * Fires when the checkbox is checked or unchecked.
64605              * @param {Ext.form.Checkbox} this This checkbox
64606              * @param {Boolean} checked The new checked value
64607              */
64608             'check'
64609         );
64610     },
64611
64612     // private
64613     onResize : function(){
64614         Ext.form.Checkbox.superclass.onResize.apply(this, arguments);
64615         if(!this.boxLabel && !this.fieldLabel){
64616             this.el.alignTo(this.wrap, 'c-c');
64617         }
64618     },
64619
64620     // private
64621     initEvents : function(){
64622         Ext.form.Checkbox.superclass.initEvents.call(this);
64623         this.mon(this.el, {
64624             scope: this,
64625             click: this.onClick,
64626             change: this.onClick
64627         });
64628     },
64629
64630     /**
64631      * @hide
64632      * Overridden and disabled. The editor element does not support standard valid/invalid marking.
64633      * @method
64634      */
64635     markInvalid : Ext.emptyFn,
64636     /**
64637      * @hide
64638      * Overridden and disabled. The editor element does not support standard valid/invalid marking.
64639      * @method
64640      */
64641     clearInvalid : Ext.emptyFn,
64642
64643     // private
64644     onRender : function(ct, position){
64645         Ext.form.Checkbox.superclass.onRender.call(this, ct, position);
64646         if(this.inputValue !== undefined){
64647             this.el.dom.value = this.inputValue;
64648         }
64649         this.wrap = this.el.wrap({cls: 'x-form-check-wrap'});
64650         if(this.boxLabel){
64651             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
64652         }
64653         if(this.checked){
64654             this.setValue(true);
64655         }else{
64656             this.checked = this.el.dom.checked;
64657         }
64658         // Need to repaint for IE, otherwise positioning is broken
64659         if (Ext.isIE && !Ext.isStrict) {
64660             this.wrap.repaint();
64661         }
64662         this.resizeEl = this.positionEl = this.wrap;
64663     },
64664
64665     // private
64666     onDestroy : function(){
64667         Ext.destroy(this.wrap);
64668         Ext.form.Checkbox.superclass.onDestroy.call(this);
64669     },
64670
64671     // private
64672     initValue : function() {
64673         this.originalValue = this.getValue();
64674     },
64675
64676     /**
64677      * Returns the checked state of the checkbox.
64678      * @return {Boolean} True if checked, else false
64679      */
64680     getValue : function(){
64681         if(this.rendered){
64682             return this.el.dom.checked;
64683         }
64684         return this.checked;
64685     },
64686
64687         // private
64688     onClick : function(){
64689         if(this.el.dom.checked != this.checked){
64690             this.setValue(this.el.dom.checked);
64691         }
64692     },
64693
64694     /**
64695      * Sets the checked state of the checkbox, fires the 'check' event, and calls a
64696      * <code>{@link #handler}</code> (if configured).
64697      * @param {Boolean/String} checked The following values will check the checkbox:
64698      * <code>true, 'true', '1', or 'on'</code>. Any other value will uncheck the checkbox.
64699      * @return {Ext.form.Field} this
64700      */
64701     setValue : function(v){
64702         var checked = this.checked,
64703             inputVal = this.inputValue;
64704             
64705         this.checked = (v === true || v === 'true' || v == '1' || (inputVal ? v == inputVal : String(v).toLowerCase() == 'on'));
64706         if(this.rendered){
64707             this.el.dom.checked = this.checked;
64708             this.el.dom.defaultChecked = this.checked;
64709         }
64710         if(checked != this.checked){
64711             this.fireEvent('check', this, this.checked);
64712             if(this.handler){
64713                 this.handler.call(this.scope || this, this, this.checked);
64714             }
64715         }
64716         return this;
64717     }
64718 });
64719 Ext.reg('checkbox', Ext.form.Checkbox);
64720 /**
64721  * @class Ext.form.CheckboxGroup
64722  * @extends Ext.form.Field
64723  * <p>A grouping container for {@link Ext.form.Checkbox} controls.</p>
64724  * <p>Sample usage:</p>
64725  * <pre><code>
64726 var myCheckboxGroup = new Ext.form.CheckboxGroup({
64727     id:'myGroup',
64728     xtype: 'checkboxgroup',
64729     fieldLabel: 'Single Column',
64730     itemCls: 'x-check-group-alt',
64731     // Put all controls in a single column with width 100%
64732     columns: 1,
64733     items: [
64734         {boxLabel: 'Item 1', name: 'cb-col-1'},
64735         {boxLabel: 'Item 2', name: 'cb-col-2', checked: true},
64736         {boxLabel: 'Item 3', name: 'cb-col-3'}
64737     ]
64738 });
64739  * </code></pre>
64740  * @constructor
64741  * Creates a new CheckboxGroup
64742  * @param {Object} config Configuration options
64743  * @xtype checkboxgroup
64744  */
64745 Ext.form.CheckboxGroup = Ext.extend(Ext.form.Field, {
64746     /**
64747      * @cfg {Array} items An Array of {@link Ext.form.Checkbox Checkbox}es or Checkbox config objects
64748      * to arrange in the group.
64749      */
64750     /**
64751      * @cfg {String/Number/Array} columns Specifies the number of columns to use when displaying grouped
64752      * checkbox/radio controls using automatic layout.  This config can take several types of values:
64753      * <ul><li><b>'auto'</b> : <p class="sub-desc">The controls will be rendered one per column on one row and the width
64754      * of each column will be evenly distributed based on the width of the overall field container. This is the default.</p></li>
64755      * <li><b>Number</b> : <p class="sub-desc">If you specific a number (e.g., 3) that number of columns will be
64756      * created and the contained controls will be automatically distributed based on the value of {@link #vertical}.</p></li>
64757      * <li><b>Array</b> : Object<p class="sub-desc">You can also specify an array of column widths, mixing integer
64758      * (fixed width) and float (percentage width) values as needed (e.g., [100, .25, .75]). Any integer values will
64759      * be rendered first, then any float values will be calculated as a percentage of the remaining space. Float
64760      * values do not have to add up to 1 (100%) although if you want the controls to take up the entire field
64761      * container you should do so.</p></li></ul>
64762      */
64763     columns : 'auto',
64764     /**
64765      * @cfg {Boolean} vertical True to distribute contained controls across columns, completely filling each column
64766      * top to bottom before starting on the next column.  The number of controls in each column will be automatically
64767      * calculated to keep columns as even as possible.  The default value is false, so that controls will be added
64768      * to columns one at a time, completely filling each row left to right before starting on the next row.
64769      */
64770     vertical : false,
64771     /**
64772      * @cfg {Boolean} allowBlank False to validate that at least one item in the group is checked (defaults to true).
64773      * If no items are selected at validation time, {@link @blankText} will be used as the error text.
64774      */
64775     allowBlank : true,
64776     /**
64777      * @cfg {String} blankText Error text to display if the {@link #allowBlank} validation fails (defaults to "You must
64778      * select at least one item in this group")
64779      */
64780     blankText : "You must select at least one item in this group",
64781
64782     // private
64783     defaultType : 'checkbox',
64784
64785     // private
64786     groupCls : 'x-form-check-group',
64787
64788     // private
64789     initComponent: function(){
64790         this.addEvents(
64791             /**
64792              * @event change
64793              * Fires when the state of a child checkbox changes.
64794              * @param {Ext.form.CheckboxGroup} this
64795              * @param {Array} checked An array containing the checked boxes.
64796              */
64797             'change'
64798         );
64799         this.on('change', this.validate, this);
64800         Ext.form.CheckboxGroup.superclass.initComponent.call(this);
64801     },
64802
64803     // private
64804     onRender : function(ct, position){
64805         if(!this.el){
64806             var panelCfg = {
64807                 autoEl: {
64808                     id: this.id
64809                 },
64810                 cls: this.groupCls,
64811                 layout: 'column',
64812                 renderTo: ct,
64813                 bufferResize: false // Default this to false, since it doesn't really have a proper ownerCt.
64814             };
64815             var colCfg = {
64816                 xtype: 'container',
64817                 defaultType: this.defaultType,
64818                 layout: 'form',
64819                 defaults: {
64820                     hideLabel: true,
64821                     anchor: '100%'
64822                 }
64823             };
64824
64825             if(this.items[0].items){
64826
64827                 // The container has standard ColumnLayout configs, so pass them in directly
64828
64829                 Ext.apply(panelCfg, {
64830                     layoutConfig: {columns: this.items.length},
64831                     defaults: this.defaults,
64832                     items: this.items
64833                 });
64834                 for(var i=0, len=this.items.length; i<len; i++){
64835                     Ext.applyIf(this.items[i], colCfg);
64836                 }
64837
64838             }else{
64839
64840                 // The container has field item configs, so we have to generate the column
64841                 // panels first then move the items into the columns as needed.
64842
64843                 var numCols, cols = [];
64844
64845                 if(typeof this.columns == 'string'){ // 'auto' so create a col per item
64846                     this.columns = this.items.length;
64847                 }
64848                 if(!Ext.isArray(this.columns)){
64849                     var cs = [];
64850                     for(var i=0; i<this.columns; i++){
64851                         cs.push((100/this.columns)*.01); // distribute by even %
64852                     }
64853                     this.columns = cs;
64854                 }
64855
64856                 numCols = this.columns.length;
64857
64858                 // Generate the column configs with the correct width setting
64859                 for(var i=0; i<numCols; i++){
64860                     var cc = Ext.apply({items:[]}, colCfg);
64861                     cc[this.columns[i] <= 1 ? 'columnWidth' : 'width'] = this.columns[i];
64862                     if(this.defaults){
64863                         cc.defaults = Ext.apply(cc.defaults || {}, this.defaults);
64864                     }
64865                     cols.push(cc);
64866                 };
64867
64868                 // Distribute the original items into the columns
64869                 if(this.vertical){
64870                     var rows = Math.ceil(this.items.length / numCols), ri = 0;
64871                     for(var i=0, len=this.items.length; i<len; i++){
64872                         if(i>0 && i%rows==0){
64873                             ri++;
64874                         }
64875                         if(this.items[i].fieldLabel){
64876                             this.items[i].hideLabel = false;
64877                         }
64878                         cols[ri].items.push(this.items[i]);
64879                     };
64880                 }else{
64881                     for(var i=0, len=this.items.length; i<len; i++){
64882                         var ci = i % numCols;
64883                         if(this.items[i].fieldLabel){
64884                             this.items[i].hideLabel = false;
64885                         }
64886                         cols[ci].items.push(this.items[i]);
64887                     };
64888                 }
64889
64890                 Ext.apply(panelCfg, {
64891                     layoutConfig: {columns: numCols},
64892                     items: cols
64893                 });
64894             }
64895
64896             this.panel = new Ext.Container(panelCfg);
64897             this.panel.ownerCt = this;
64898             this.el = this.panel.getEl();
64899
64900             if(this.forId && this.itemCls){
64901                 var l = this.el.up(this.itemCls).child('label', true);
64902                 if(l){
64903                     l.setAttribute('htmlFor', this.forId);
64904                 }
64905             }
64906
64907             var fields = this.panel.findBy(function(c){
64908                 return c.isFormField;
64909             }, this);
64910
64911             this.items = new Ext.util.MixedCollection();
64912             this.items.addAll(fields);
64913         }
64914         Ext.form.CheckboxGroup.superclass.onRender.call(this, ct, position);
64915     },
64916
64917     initValue : function(){
64918         if(this.value){
64919             this.setValue.apply(this, this.buffered ? this.value : [this.value]);
64920             delete this.buffered;
64921             delete this.value;
64922         }
64923     },
64924
64925     afterRender : function(){
64926         Ext.form.CheckboxGroup.superclass.afterRender.call(this);
64927         this.eachItem(function(item){
64928             item.on('check', this.fireChecked, this);
64929             item.inGroup = true;
64930         });
64931     },
64932
64933     // private
64934     doLayout: function(){
64935         //ugly method required to layout hidden items
64936         if(this.rendered){
64937             this.panel.forceLayout = this.ownerCt.forceLayout;
64938             this.panel.doLayout();
64939         }
64940     },
64941
64942     // private
64943     fireChecked: function(){
64944         var arr = [];
64945         this.eachItem(function(item){
64946             if(item.checked){
64947                 arr.push(item);
64948             }
64949         });
64950         this.fireEvent('change', this, arr);
64951     },
64952     
64953     /**
64954      * Runs CheckboxGroup's validations and returns an array of any errors. The only error by default
64955      * is if allowBlank is set to true and no items are checked.
64956      * @return {Array} Array of all validation errors
64957      */
64958     getErrors: function() {
64959         var errors = Ext.form.CheckboxGroup.superclass.getErrors.apply(this, arguments);
64960         
64961         if (!this.allowBlank) {
64962             var blank = true;
64963             
64964             this.eachItem(function(f){
64965                 if (f.checked) {
64966                     return (blank = false);
64967                 }
64968             });
64969             
64970             if (blank) errors.push(this.blankText);
64971         }
64972         
64973         return errors;
64974     },
64975
64976     // private
64977     isDirty: function(){
64978         //override the behaviour to check sub items.
64979         if (this.disabled || !this.rendered) {
64980             return false;
64981         }
64982
64983         var dirty = false;
64984         
64985         this.eachItem(function(item){
64986             if(item.isDirty()){
64987                 dirty = true;
64988                 return false;
64989             }
64990         });
64991         
64992         return dirty;
64993     },
64994
64995     // private
64996     setReadOnly : function(readOnly){
64997         if(this.rendered){
64998             this.eachItem(function(item){
64999                 item.setReadOnly(readOnly);
65000             });
65001         }
65002         this.readOnly = readOnly;
65003     },
65004
65005     // private
65006     onDisable : function(){
65007         this.eachItem(function(item){
65008             item.disable();
65009         });
65010     },
65011
65012     // private
65013     onEnable : function(){
65014         this.eachItem(function(item){
65015             item.enable();
65016         });
65017     },
65018
65019     // private
65020     onResize : function(w, h){
65021         this.panel.setSize(w, h);
65022         this.panel.doLayout();
65023     },
65024
65025     // inherit docs from Field
65026     reset : function(){
65027         if (this.originalValue) {
65028             // Clear all items
65029             this.eachItem(function(c){
65030                 if(c.setValue){
65031                     c.setValue(false);
65032                     c.originalValue = c.getValue();
65033                 }
65034             });
65035             // Set items stored in originalValue, ugly - set a flag to reset the originalValue
65036             // during the horrible onSetValue.  This will allow trackResetOnLoad to function.
65037             this.resetOriginal = true;
65038             this.setValue(this.originalValue);
65039             delete this.resetOriginal;
65040         } else {
65041             this.eachItem(function(c){
65042                 if(c.reset){
65043                     c.reset();
65044                 }
65045             });
65046         }
65047         // Defer the clearInvalid so if BaseForm's collection is being iterated it will be called AFTER it is complete.
65048         // Important because reset is being called on both the group and the individual items.
65049         (function() {
65050             this.clearInvalid();
65051         }).defer(50, this);
65052     },
65053
65054     /**
65055      * {@link Ext.form.Checkbox#setValue Set the value(s)} of an item or items
65056      * in the group. Examples illustrating how this method may be called:
65057      * <pre><code>
65058 // call with name and value
65059 myCheckboxGroup.setValue('cb-col-1', true);
65060 // call with an array of boolean values
65061 myCheckboxGroup.setValue([true, false, false]);
65062 // call with an object literal specifying item:value pairs
65063 myCheckboxGroup.setValue({
65064     'cb-col-2': false,
65065     'cb-col-3': true
65066 });
65067 // use comma separated string to set items with name to true (checked)
65068 myCheckboxGroup.setValue('cb-col-1,cb-col-3');
65069      * </code></pre>
65070      * See {@link Ext.form.Checkbox#setValue} for additional information.
65071      * @param {Mixed} id The checkbox to check, or as described by example shown.
65072      * @param {Boolean} value (optional) The value to set the item.
65073      * @return {Ext.form.CheckboxGroup} this
65074      */
65075     setValue: function(){
65076         if(this.rendered){
65077             this.onSetValue.apply(this, arguments);
65078         }else{
65079             this.buffered = true;
65080             this.value = arguments;
65081         }
65082         return this;
65083     },
65084
65085     /**
65086      * @private
65087      * Sets the values of one or more of the items within the CheckboxGroup
65088      * @param {String|Array|Object} id Can take multiple forms. Can be optionally:
65089      * <ul>
65090      *   <li>An ID string to be used with a second argument</li>
65091      *   <li>An array of the form ['some', 'list', 'of', 'ids', 'to', 'mark', 'checked']</li>
65092      *   <li>An array in the form [true, true, false, true, false] etc, where each item relates to the check status of
65093      *       the checkbox at the same index</li>
65094      *   <li>An object containing ids of the checkboxes as keys and check values as properties</li>
65095      * </ul>
65096      * @param {String} value The value to set the field to if the first argument was a string
65097      */
65098     onSetValue: function(id, value){
65099         if(arguments.length == 1){
65100             if(Ext.isArray(id)){
65101                 Ext.each(id, function(val, idx){
65102                     if (Ext.isObject(val) && val.setValue){ // array of checkbox components to be checked
65103                         val.setValue(true);
65104                         if (this.resetOriginal === true) {
65105                             val.originalValue = val.getValue();
65106                         }
65107                     } else { // an array of boolean values
65108                         var item = this.items.itemAt(idx);
65109                         if(item){
65110                             item.setValue(val);
65111                         }
65112                     }
65113                 }, this);
65114             }else if(Ext.isObject(id)){
65115                 // set of name/value pairs
65116                 for(var i in id){
65117                     var f = this.getBox(i);
65118                     if(f){
65119                         f.setValue(id[i]);
65120                     }
65121                 }
65122             }else{
65123                 this.setValueForItem(id);
65124             }
65125         }else{
65126             var f = this.getBox(id);
65127             if(f){
65128                 f.setValue(value);
65129             }
65130         }
65131     },
65132
65133     // private
65134     beforeDestroy: function(){
65135         Ext.destroy(this.panel);
65136         if (!this.rendered) {
65137             Ext.destroy(this.items);
65138         }
65139         Ext.form.CheckboxGroup.superclass.beforeDestroy.call(this);
65140
65141     },
65142
65143     setValueForItem : function(val){
65144         val = String(val).split(',');
65145         this.eachItem(function(item){
65146             if(val.indexOf(item.inputValue)> -1){
65147                 item.setValue(true);
65148             }
65149         });
65150     },
65151
65152     // private
65153     getBox : function(id){
65154         var box = null;
65155         this.eachItem(function(f){
65156             if(id == f || f.dataIndex == id || f.id == id || f.getName() == id){
65157                 box = f;
65158                 return false;
65159             }
65160         });
65161         return box;
65162     },
65163
65164     /**
65165      * Gets an array of the selected {@link Ext.form.Checkbox} in the group.
65166      * @return {Array} An array of the selected checkboxes.
65167      */
65168     getValue : function(){
65169         var out = [];
65170         this.eachItem(function(item){
65171             if(item.checked){
65172                 out.push(item);
65173             }
65174         });
65175         return out;
65176     },
65177
65178     /**
65179      * @private
65180      * Convenience function which passes the given function to every item in the composite
65181      * @param {Function} fn The function to call
65182      * @param {Object} scope Optional scope object
65183      */
65184     eachItem: function(fn, scope) {
65185         if(this.items && this.items.each){
65186             this.items.each(fn, scope || this);
65187         }
65188     },
65189
65190     /**
65191      * @cfg {String} name
65192      * @hide
65193      */
65194
65195     /**
65196      * @method getRawValue
65197      * @hide
65198      */
65199     getRawValue : Ext.emptyFn,
65200
65201     /**
65202      * @method setRawValue
65203      * @hide
65204      */
65205     setRawValue : Ext.emptyFn
65206
65207 });
65208
65209 Ext.reg('checkboxgroup', Ext.form.CheckboxGroup);
65210 /**
65211  * @class Ext.form.CompositeField
65212  * @extends Ext.form.Field
65213  * Composite field allowing a number of form Fields to be rendered on the same row. The fields are rendered
65214  * using an hbox layout internally, so all of the normal HBox layout config items are available. Example usage:
65215  * <pre>
65216 {
65217     xtype: 'compositefield',
65218     labelWidth: 120
65219     items: [
65220         {
65221             xtype     : 'textfield',
65222             fieldLabel: 'Title',
65223             width     : 20
65224         },
65225         {
65226             xtype     : 'textfield',
65227             fieldLabel: 'First',
65228             flex      : 1
65229         },
65230         {
65231             xtype     : 'textfield',
65232             fieldLabel: 'Last',
65233             flex      : 1
65234         }
65235     ]
65236 }
65237  * </pre>
65238  * In the example above the composite's fieldLabel will be set to 'Title, First, Last' as it groups the fieldLabels
65239  * of each of its children. This can be overridden by setting a fieldLabel on the compositefield itself:
65240  * <pre>
65241 {
65242     xtype: 'compositefield',
65243     fieldLabel: 'Custom label',
65244     items: [...]
65245 }
65246  * </pre>
65247  * Any Ext.form.* component can be placed inside a composite field.
65248  */
65249 Ext.form.CompositeField = Ext.extend(Ext.form.Field, {
65250
65251     /**
65252      * @property defaultMargins
65253      * @type String
65254      * The margins to apply by default to each field in the composite
65255      */
65256     defaultMargins: '0 5 0 0',
65257
65258     /**
65259      * @property skipLastItemMargin
65260      * @type Boolean
65261      * If true, the defaultMargins are not applied to the last item in the composite field set (defaults to true)
65262      */
65263     skipLastItemMargin: true,
65264
65265     /**
65266      * @property isComposite
65267      * @type Boolean
65268      * Signifies that this is a Composite field
65269      */
65270     isComposite: true,
65271
65272     /**
65273      * @property combineErrors
65274      * @type Boolean
65275      * True to combine errors from the individual fields into a single error message at the CompositeField level (defaults to true)
65276      */
65277     combineErrors: true,
65278     
65279     /**
65280      * @cfg {String} labelConnector The string to use when joining segments of the built label together (defaults to ', ')
65281      */
65282     labelConnector: ', ',
65283     
65284     /**
65285      * @cfg {Object} defaults Any default properties to assign to the child fields.
65286      */
65287
65288     //inherit docs
65289     //Builds the composite field label
65290     initComponent: function() {
65291         var labels = [],
65292             items  = this.items,
65293             item;
65294
65295         for (var i=0, j = items.length; i < j; i++) {
65296             item = items[i];
65297             
65298             if (!Ext.isEmpty(item.ref)){
65299                 item.ref = '../' + item.ref;
65300             }
65301
65302             labels.push(item.fieldLabel);
65303
65304             //apply any defaults
65305             Ext.applyIf(item, this.defaults);
65306
65307             //apply default margins to each item except the last
65308             if (!(i == j - 1 && this.skipLastItemMargin)) {
65309                 Ext.applyIf(item, {margins: this.defaultMargins});
65310             }
65311         }
65312
65313         this.fieldLabel = this.fieldLabel || this.buildLabel(labels);
65314
65315         /**
65316          * @property fieldErrors
65317          * @type Ext.util.MixedCollection
65318          * MixedCollection of current errors on the Composite's subfields. This is used internally to track when
65319          * to show and hide error messages at the Composite level. Listeners are attached to the MixedCollection's
65320          * add, remove and replace events to update the error icon in the UI as errors are added or removed.
65321          */
65322         this.fieldErrors = new Ext.util.MixedCollection(true, function(item) {
65323             return item.field;
65324         });
65325
65326         this.fieldErrors.on({
65327             scope  : this,
65328             add    : this.updateInvalidMark,
65329             remove : this.updateInvalidMark,
65330             replace: this.updateInvalidMark
65331         });
65332
65333         Ext.form.CompositeField.superclass.initComponent.apply(this, arguments);
65334         
65335         this.innerCt = new Ext.Container({
65336             layout  : 'hbox',
65337             items   : this.items,
65338             cls     : 'x-form-composite',
65339             defaultMargins: '0 3 0 0',
65340             ownerCt: this
65341         });
65342         this.innerCt.ownerCt = undefined;
65343         
65344         var fields = this.innerCt.findBy(function(c) {
65345             return c.isFormField;
65346         }, this);
65347
65348         /**
65349          * @property items
65350          * @type Ext.util.MixedCollection
65351          * Internal collection of all of the subfields in this Composite
65352          */
65353         this.items = new Ext.util.MixedCollection();
65354         this.items.addAll(fields);
65355         
65356     },
65357
65358     /**
65359      * @private
65360      * Creates an internal container using hbox and renders the fields to it
65361      */
65362     onRender: function(ct, position) {
65363         if (!this.el) {
65364             /**
65365              * @property innerCt
65366              * @type Ext.Container
65367              * A container configured with hbox layout which is responsible for laying out the subfields
65368              */
65369             var innerCt = this.innerCt;
65370             innerCt.render(ct);
65371
65372             this.el = innerCt.getEl();
65373
65374             //if we're combining subfield errors into a single message, override the markInvalid and clearInvalid
65375             //methods of each subfield and show them at the Composite level instead
65376             if (this.combineErrors) {
65377                 this.eachItem(function(field) {
65378                     Ext.apply(field, {
65379                         markInvalid : this.onFieldMarkInvalid.createDelegate(this, [field], 0),
65380                         clearInvalid: this.onFieldClearInvalid.createDelegate(this, [field], 0)
65381                     });
65382                 });
65383             }
65384
65385             //set the label 'for' to the first item
65386             var l = this.el.parent().parent().child('label', true);
65387             if (l) {
65388                 l.setAttribute('for', this.items.items[0].id);
65389             }
65390         }
65391
65392         Ext.form.CompositeField.superclass.onRender.apply(this, arguments);
65393     },
65394
65395     /**
65396      * Called if combineErrors is true and a subfield's markInvalid method is called.
65397      * By default this just adds the subfield's error to the internal fieldErrors MixedCollection
65398      * @param {Ext.form.Field} field The field that was marked invalid
65399      * @param {String} message The error message
65400      */
65401     onFieldMarkInvalid: function(field, message) {
65402         var name  = field.getName(),
65403             error = {
65404                 field: name, 
65405                 errorName: field.fieldLabel || name,
65406                 error: message
65407             };
65408
65409         this.fieldErrors.replace(name, error);
65410
65411         field.el.addClass(field.invalidClass);
65412     },
65413
65414     /**
65415      * Called if combineErrors is true and a subfield's clearInvalid method is called.
65416      * By default this just updates the internal fieldErrors MixedCollection.
65417      * @param {Ext.form.Field} field The field that was marked invalid
65418      */
65419     onFieldClearInvalid: function(field) {
65420         this.fieldErrors.removeKey(field.getName());
65421
65422         field.el.removeClass(field.invalidClass);
65423     },
65424
65425     /**
65426      * @private
65427      * Called after a subfield is marked valid or invalid, this checks to see if any of the subfields are
65428      * currently invalid. If any subfields are invalid it builds a combined error message marks the composite
65429      * invalid, otherwise clearInvalid is called
65430      */
65431     updateInvalidMark: function() {
65432         var ieStrict = Ext.isIE6 && Ext.isStrict;
65433
65434         if (this.fieldErrors.length == 0) {
65435             this.clearInvalid();
65436
65437             //IE6 in strict mode has a layout bug when using 'under' as the error message target. This fixes it
65438             if (ieStrict) {
65439                 this.clearInvalid.defer(50, this);
65440             }
65441         } else {
65442             var message = this.buildCombinedErrorMessage(this.fieldErrors.items);
65443
65444             this.sortErrors();
65445             this.markInvalid(message);
65446
65447             //IE6 in strict mode has a layout bug when using 'under' as the error message target. This fixes it
65448             if (ieStrict) {
65449                 this.markInvalid(message);
65450             }
65451         }
65452     },
65453
65454     /**
65455      * Performs validation checks on each subfield and returns false if any of them fail validation.
65456      * @return {Boolean} False if any subfield failed validation
65457      */
65458     validateValue: function() {
65459         var valid = true;
65460
65461         this.eachItem(function(field) {
65462             if (!field.isValid()) valid = false;
65463         });
65464
65465         return valid;
65466     },
65467
65468     /**
65469      * Takes an object containing error messages for contained fields, returning a combined error
65470      * string (defaults to just placing each item on a new line). This can be overridden to provide
65471      * custom combined error message handling.
65472      * @param {Array} errors Array of errors in format: [{field: 'title', error: 'some error'}]
65473      * @return {String} The combined error message
65474      */
65475     buildCombinedErrorMessage: function(errors) {
65476         var combined = [],
65477             error;
65478
65479         for (var i = 0, j = errors.length; i < j; i++) {
65480             error = errors[i];
65481
65482             combined.push(String.format("{0}: {1}", error.errorName, error.error));
65483         }
65484
65485         return combined.join("<br />");
65486     },
65487
65488     /**
65489      * Sorts the internal fieldErrors MixedCollection by the order in which the fields are defined.
65490      * This is called before displaying errors to ensure that the errors are presented in the expected order.
65491      * This function can be overridden to provide a custom sorting order if needed.
65492      */
65493     sortErrors: function() {
65494         var fields = this.items;
65495
65496         this.fieldErrors.sort("ASC", function(a, b) {
65497             var findByName = function(key) {
65498                 return function(field) {
65499                     return field.getName() == key;
65500                 };
65501             };
65502
65503             var aIndex = fields.findIndexBy(findByName(a.field)),
65504                 bIndex = fields.findIndexBy(findByName(b.field));
65505
65506             return aIndex < bIndex ? -1 : 1;
65507         });
65508     },
65509
65510     /**
65511      * Resets each field in the composite to their previous value
65512      */
65513     reset: function() {
65514         this.eachItem(function(item) {
65515             item.reset();
65516         });
65517
65518         // Defer the clearInvalid so if BaseForm's collection is being iterated it will be called AFTER it is complete.
65519         // Important because reset is being called on both the group and the individual items.
65520         (function() {
65521             this.clearInvalid();
65522         }).defer(50, this);
65523     },
65524     
65525     /**
65526      * Calls clearInvalid on all child fields. This is a convenience function and should not often need to be called
65527      * as fields usually take care of clearing themselves
65528      */
65529     clearInvalidChildren: function() {
65530         this.eachItem(function(item) {
65531             item.clearInvalid();
65532         });
65533     },
65534
65535     /**
65536      * Builds a label string from an array of subfield labels.
65537      * By default this just joins the labels together with a comma
65538      * @param {Array} segments Array of each of the labels in the composite field's subfields
65539      * @return {String} The built label
65540      */
65541     buildLabel: function(segments) {
65542         return Ext.clean(segments).join(this.labelConnector);
65543     },
65544
65545     /**
65546      * Checks each field in the composite and returns true if any is dirty
65547      * @return {Boolean} True if any field is dirty
65548      */
65549     isDirty: function(){
65550         //override the behaviour to check sub items.
65551         if (this.disabled || !this.rendered) {
65552             return false;
65553         }
65554
65555         var dirty = false;
65556         this.eachItem(function(item){
65557             if(item.isDirty()){
65558                 dirty = true;
65559                 return false;
65560             }
65561         });
65562         return dirty;
65563     },
65564
65565     /**
65566      * @private
65567      * Convenience function which passes the given function to every item in the composite
65568      * @param {Function} fn The function to call
65569      * @param {Object} scope Optional scope object
65570      */
65571     eachItem: function(fn, scope) {
65572         if(this.items && this.items.each){
65573             this.items.each(fn, scope || this);
65574         }
65575     },
65576
65577     /**
65578      * @private
65579      * Passes the resize call through to the inner panel
65580      */
65581     onResize: function(adjWidth, adjHeight, rawWidth, rawHeight) {
65582         var innerCt = this.innerCt;
65583
65584         if (this.rendered && innerCt.rendered) {
65585             innerCt.setSize(adjWidth, adjHeight);
65586         }
65587
65588         Ext.form.CompositeField.superclass.onResize.apply(this, arguments);
65589     },
65590
65591     /**
65592      * @private
65593      * Forces the internal container to be laid out again
65594      */
65595     doLayout: function(shallow, force) {
65596         if (this.rendered) {
65597             var innerCt = this.innerCt;
65598
65599             innerCt.forceLayout = this.ownerCt.forceLayout;
65600             innerCt.doLayout(shallow, force);
65601         }
65602     },
65603
65604     /**
65605      * @private
65606      */
65607     beforeDestroy: function(){
65608         Ext.destroy(this.innerCt);
65609
65610         Ext.form.CompositeField.superclass.beforeDestroy.call(this);
65611     },
65612
65613     //override the behaviour to check sub items.
65614     setReadOnly : function(readOnly) {
65615         if (readOnly == undefined) {
65616             readOnly = true;
65617         }
65618         readOnly = !!readOnly;
65619
65620         if(this.rendered){
65621             this.eachItem(function(item){
65622                 item.setReadOnly(readOnly);
65623             });
65624         }
65625         this.readOnly = readOnly;
65626     },
65627
65628     onShow : function() {
65629         Ext.form.CompositeField.superclass.onShow.call(this);
65630         this.doLayout();
65631     },
65632
65633     //override the behaviour to check sub items.
65634     onDisable : function(){
65635         this.eachItem(function(item){
65636             item.disable();
65637         });
65638     },
65639
65640     //override the behaviour to check sub items.
65641     onEnable : function(){
65642         this.eachItem(function(item){
65643             item.enable();
65644         });
65645     }
65646 });
65647
65648 Ext.reg('compositefield', Ext.form.CompositeField);/**
65649  * @class Ext.form.Radio
65650  * @extends Ext.form.Checkbox
65651  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
65652  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
65653  * @constructor
65654  * Creates a new Radio
65655  * @param {Object} config Configuration options
65656  * @xtype radio
65657  */
65658 Ext.form.Radio = Ext.extend(Ext.form.Checkbox, {
65659     inputType: 'radio',
65660
65661     /**
65662      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
65663      * @method
65664      */
65665     markInvalid : Ext.emptyFn,
65666     /**
65667      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
65668      * @method
65669      */
65670     clearInvalid : Ext.emptyFn,
65671
65672     /**
65673      * If this radio is part of a group, it will return the selected value
65674      * @return {String}
65675      */
65676     getGroupValue : function(){
65677         var p = this.el.up('form') || Ext.getBody();
65678         var c = p.child('input[name='+this.el.dom.name+']:checked', true);
65679         return c ? c.value : null;
65680     },
65681
65682     /**
65683      * Sets either the checked/unchecked status of this Radio, or, if a string value
65684      * is passed, checks a sibling Radio of the same name whose value is the value specified.
65685      * @param value {String/Boolean} Checked value, or the value of the sibling radio button to check.
65686      * @return {Ext.form.Field} this
65687      */
65688     setValue : function(v){
65689         var checkEl,
65690             els,
65691             radio;
65692         if (typeof v == 'boolean') {
65693             Ext.form.Radio.superclass.setValue.call(this, v);
65694         } else if (this.rendered) {
65695             checkEl = this.getCheckEl();
65696             radio = checkEl.child('input[name=' + this.el.dom.name + '][value=' + v + ']', true);
65697             if(radio){
65698                 Ext.getCmp(radio.id).setValue(true);
65699             }
65700         }
65701         if(this.rendered && this.checked){
65702             checkEl = checkEl || this.getCheckEl();
65703             els = this.getCheckEl().select('input[name=' + this.el.dom.name + ']');
65704                         els.each(function(el){
65705                                 if(el.dom.id != this.id){
65706                                         Ext.getCmp(el.dom.id).setValue(false);
65707                                 }
65708                         }, this);
65709         }
65710         return this;
65711     },
65712
65713     // private
65714     getCheckEl: function(){
65715         if(this.inGroup){
65716             return this.el.up('.x-form-radio-group');
65717         }
65718         return this.el.up('form') || Ext.getBody();
65719     }
65720 });
65721 Ext.reg('radio', Ext.form.Radio);
65722 /**
65723  * @class Ext.form.RadioGroup
65724  * @extends Ext.form.CheckboxGroup
65725  * A grouping container for {@link Ext.form.Radio} controls.
65726  * @constructor
65727  * Creates a new RadioGroup
65728  * @param {Object} config Configuration options
65729  * @xtype radiogroup
65730  */
65731 Ext.form.RadioGroup = Ext.extend(Ext.form.CheckboxGroup, {
65732     /**
65733      * @cfg {Array} items An Array of {@link Ext.form.Radio Radio}s or Radio config objects
65734      * to arrange in the group.
65735      */
65736     /**
65737      * @cfg {Boolean} allowBlank True to allow every item in the group to be blank (defaults to true).
65738      * If allowBlank = false and no items are selected at validation time, {@link @blankText} will
65739      * be used as the error text.
65740      */
65741     allowBlank : true,
65742     /**
65743      * @cfg {String} blankText Error text to display if the {@link #allowBlank} validation fails
65744      * (defaults to 'You must select one item in this group')
65745      */
65746     blankText : 'You must select one item in this group',
65747     
65748     // private
65749     defaultType : 'radio',
65750     
65751     // private
65752     groupCls : 'x-form-radio-group',
65753     
65754     /**
65755      * @event change
65756      * Fires when the state of a child radio changes.
65757      * @param {Ext.form.RadioGroup} this
65758      * @param {Ext.form.Radio} checked The checked radio
65759      */
65760     
65761     /**
65762      * Gets the selected {@link Ext.form.Radio} in the group, if it exists.
65763      * @return {Ext.form.Radio} The selected radio.
65764      */
65765     getValue : function(){
65766         var out = null;
65767         this.eachItem(function(item){
65768             if(item.checked){
65769                 out = item;
65770                 return false;
65771             }
65772         });
65773         return out;
65774     },
65775     
65776     /**
65777      * Sets the checked radio in the group.
65778      * @param {String/Ext.form.Radio} id The radio to check.
65779      * @param {Boolean} value The value to set the radio.
65780      * @return {Ext.form.RadioGroup} this
65781      */
65782     onSetValue : function(id, value){
65783         if(arguments.length > 1){
65784             var f = this.getBox(id);
65785             if(f){
65786                 f.setValue(value);
65787                 if(f.checked){
65788                     this.eachItem(function(item){
65789                         if (item !== f){
65790                             item.setValue(false);
65791                         }
65792                     });
65793                 }
65794             }
65795         }else{
65796             this.setValueForItem(id);
65797         }
65798     },
65799     
65800     setValueForItem : function(val){
65801         val = String(val).split(',')[0];
65802         this.eachItem(function(item){
65803             item.setValue(val == item.inputValue);
65804         });
65805     },
65806     
65807     // private
65808     fireChecked : function(){
65809         if(!this.checkTask){
65810             this.checkTask = new Ext.util.DelayedTask(this.bufferChecked, this);
65811         }
65812         this.checkTask.delay(10);
65813     },
65814     
65815     // private
65816     bufferChecked : function(){
65817         var out = null;
65818         this.eachItem(function(item){
65819             if(item.checked){
65820                 out = item;
65821                 return false;
65822             }
65823         });
65824         this.fireEvent('change', this, out);
65825     },
65826     
65827     onDestroy : function(){
65828         if(this.checkTask){
65829             this.checkTask.cancel();
65830             this.checkTask = null;
65831         }
65832         Ext.form.RadioGroup.superclass.onDestroy.call(this);
65833     }
65834
65835 });
65836
65837 Ext.reg('radiogroup', Ext.form.RadioGroup);
65838 /**
65839  * @class Ext.form.Hidden
65840  * @extends Ext.form.Field
65841  * A basic hidden field for storing hidden values in forms that need to be passed in the form submit.
65842  * @constructor
65843  * Create a new Hidden field.
65844  * @param {Object} config Configuration options
65845  * @xtype hidden
65846  */
65847 Ext.form.Hidden = Ext.extend(Ext.form.Field, {
65848     // private
65849     inputType : 'hidden',
65850     
65851     shouldLayout: false,
65852
65853     // private
65854     onRender : function(){
65855         Ext.form.Hidden.superclass.onRender.apply(this, arguments);
65856     },
65857
65858     // private
65859     initEvents : function(){
65860         this.originalValue = this.getValue();
65861     },
65862
65863     // These are all private overrides
65864     setSize : Ext.emptyFn,
65865     setWidth : Ext.emptyFn,
65866     setHeight : Ext.emptyFn,
65867     setPosition : Ext.emptyFn,
65868     setPagePosition : Ext.emptyFn,
65869     markInvalid : Ext.emptyFn,
65870     clearInvalid : Ext.emptyFn
65871 });
65872 Ext.reg('hidden', Ext.form.Hidden);/**
65873  * @class Ext.form.BasicForm
65874  * @extends Ext.util.Observable
65875  * <p>Encapsulates the DOM &lt;form> element at the heart of the {@link Ext.form.FormPanel FormPanel} class, and provides
65876  * input field management, validation, submission, and form loading services.</p>
65877  * <p>By default, Ext Forms are submitted through Ajax, using an instance of {@link Ext.form.Action.Submit}.
65878  * To enable normal browser submission of an Ext Form, use the {@link #standardSubmit} config option.</p>
65879  * <p><b><u>File Uploads</u></b></p>
65880  * <p>{@link #fileUpload File uploads} are not performed using Ajax submission, that
65881  * is they are <b>not</b> performed using XMLHttpRequests. Instead the form is submitted in the standard
65882  * manner with the DOM <tt>&lt;form></tt> element temporarily modified to have its
65883  * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
65884  * to a dynamically generated, hidden <tt>&lt;iframe></tt> which is inserted into the document
65885  * but removed after the return data has been gathered.</p>
65886  * <p>The server response is parsed by the browser to create the document for the IFRAME. If the
65887  * server is using JSON to send the return object, then the
65888  * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17">Content-Type</a> header
65889  * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.</p>
65890  * <p>Characters which are significant to an HTML parser must be sent as HTML entities, so encode
65891  * "&lt;" as "&amp;lt;", "&amp;" as "&amp;amp;" etc.</p>
65892  * <p>The response text is retrieved from the document, and a fake XMLHttpRequest object
65893  * is created containing a <tt>responseText</tt> property in order to conform to the
65894  * requirements of event handlers and callbacks.</p>
65895  * <p>Be aware that file upload packets are sent with the content type <a href="http://www.faqs.org/rfcs/rfc2388.html">multipart/form</a>
65896  * and some server technologies (notably JEE) may require some custom processing in order to
65897  * retrieve parameter names and parameter values from the packet content.</p>
65898  * @constructor
65899  * @param {Mixed} el The form element or its id
65900  * @param {Object} config Configuration options
65901  */
65902 Ext.form.BasicForm = Ext.extend(Ext.util.Observable, {
65903
65904     constructor: function(el, config){
65905         Ext.apply(this, config);
65906         if(Ext.isString(this.paramOrder)){
65907             this.paramOrder = this.paramOrder.split(/[\s,|]/);
65908         }
65909         /**
65910          * A {@link Ext.util.MixedCollection MixedCollection} containing all the Ext.form.Fields in this form.
65911          * @type MixedCollection
65912          * @property items
65913          */
65914         this.items = new Ext.util.MixedCollection(false, function(o){
65915             return o.getItemId();
65916         });
65917         this.addEvents(
65918             /**
65919              * @event beforeaction
65920              * Fires before any action is performed. Return false to cancel the action.
65921              * @param {Form} this
65922              * @param {Action} action The {@link Ext.form.Action} to be performed
65923              */
65924             'beforeaction',
65925             /**
65926              * @event actionfailed
65927              * Fires when an action fails.
65928              * @param {Form} this
65929              * @param {Action} action The {@link Ext.form.Action} that failed
65930              */
65931             'actionfailed',
65932             /**
65933              * @event actioncomplete
65934              * Fires when an action is completed.
65935              * @param {Form} this
65936              * @param {Action} action The {@link Ext.form.Action} that completed
65937              */
65938             'actioncomplete'
65939         );
65940
65941         if(el){
65942             this.initEl(el);
65943         }
65944         Ext.form.BasicForm.superclass.constructor.call(this);
65945     },
65946
65947     /**
65948      * @cfg {String} method
65949      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
65950      */
65951     /**
65952      * @cfg {DataReader} reader
65953      * An Ext.data.DataReader (e.g. {@link Ext.data.XmlReader}) to be used to read
65954      * data when executing 'load' actions. This is optional as there is built-in
65955      * support for processing JSON.  For additional information on using an XMLReader
65956      * see the example provided in examples/form/xml-form.html.
65957      */
65958     /**
65959      * @cfg {DataReader} errorReader
65960      * <p>An Ext.data.DataReader (e.g. {@link Ext.data.XmlReader}) to be used to
65961      * read field error messages returned from 'submit' actions. This is optional
65962      * as there is built-in support for processing JSON.</p>
65963      * <p>The Records which provide messages for the invalid Fields must use the
65964      * Field name (or id) as the Record ID, and must contain a field called 'msg'
65965      * which contains the error message.</p>
65966      * <p>The errorReader does not have to be a full-blown implementation of a
65967      * DataReader. It simply needs to implement a <tt>read(xhr)</tt> function
65968      * which returns an Array of Records in an object with the following
65969      * structure:</p><pre><code>
65970 {
65971     records: recordArray
65972 }
65973 </code></pre>
65974      */
65975     /**
65976      * @cfg {String} url
65977      * The URL to use for form actions if one isn't supplied in the
65978      * <code>{@link #doAction doAction} options</code>.
65979      */
65980     /**
65981      * @cfg {Boolean} fileUpload
65982      * Set to true if this form is a file upload.
65983      * <p>File uploads are not performed using normal 'Ajax' techniques, that is they are <b>not</b>
65984      * performed using XMLHttpRequests. Instead the form is submitted in the standard manner with the
65985      * DOM <tt>&lt;form></tt> element temporarily modified to have its
65986      * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
65987      * to a dynamically generated, hidden <tt>&lt;iframe></tt> which is inserted into the document
65988      * but removed after the return data has been gathered.</p>
65989      * <p>The server response is parsed by the browser to create the document for the IFRAME. If the
65990      * server is using JSON to send the return object, then the
65991      * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17">Content-Type</a> header
65992      * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.</p>
65993      * <p>Characters which are significant to an HTML parser must be sent as HTML entities, so encode
65994      * "&lt;" as "&amp;lt;", "&amp;" as "&amp;amp;" etc.</p>
65995      * <p>The response text is retrieved from the document, and a fake XMLHttpRequest object
65996      * is created containing a <tt>responseText</tt> property in order to conform to the
65997      * requirements of event handlers and callbacks.</p>
65998      * <p>Be aware that file upload packets are sent with the content type <a href="http://www.faqs.org/rfcs/rfc2388.html">multipart/form</a>
65999      * and some server technologies (notably JEE) may require some custom processing in order to
66000      * retrieve parameter names and parameter values from the packet content.</p>
66001      */
66002     /**
66003      * @cfg {Object} baseParams
66004      * <p>Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.</p>
66005      * <p>Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.</p>
66006      */
66007     /**
66008      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
66009      */
66010     timeout: 30,
66011
66012     /**
66013      * @cfg {Object} api (Optional) If specified load and submit actions will be handled
66014      * with {@link Ext.form.Action.DirectLoad} and {@link Ext.form.Action.DirectSubmit}.
66015      * Methods which have been imported by Ext.Direct can be specified here to load and submit
66016      * forms.
66017      * Such as the following:<pre><code>
66018 api: {
66019     load: App.ss.MyProfile.load,
66020     submit: App.ss.MyProfile.submit
66021 }
66022 </code></pre>
66023      * <p>Load actions can use <code>{@link #paramOrder}</code> or <code>{@link #paramsAsHash}</code>
66024      * to customize how the load method is invoked.
66025      * Submit actions will always use a standard form submit. The formHandler configuration must
66026      * be set on the associated server-side method which has been imported by Ext.Direct</p>
66027      */
66028
66029     /**
66030      * @cfg {Array/String} paramOrder <p>A list of params to be executed server side.
66031      * Defaults to <tt>undefined</tt>. Only used for the <code>{@link #api}</code>
66032      * <code>load</code> configuration.</p>
66033      * <br><p>Specify the params in the order in which they must be executed on the
66034      * server-side as either (1) an Array of String values, or (2) a String of params
66035      * delimited by either whitespace, comma, or pipe. For example,
66036      * any of the following would be acceptable:</p><pre><code>
66037 paramOrder: ['param1','param2','param3']
66038 paramOrder: 'param1 param2 param3'
66039 paramOrder: 'param1,param2,param3'
66040 paramOrder: 'param1|param2|param'
66041      </code></pre>
66042      */
66043     paramOrder: undefined,
66044
66045     /**
66046      * @cfg {Boolean} paramsAsHash Only used for the <code>{@link #api}</code>
66047      * <code>load</code> configuration. Send parameters as a collection of named
66048      * arguments (defaults to <tt>false</tt>). Providing a
66049      * <tt>{@link #paramOrder}</tt> nullifies this configuration.
66050      */
66051     paramsAsHash: false,
66052
66053     /**
66054      * @cfg {String} waitTitle
66055      * The default title to show for the waiting message box (defaults to <tt>'Please Wait...'</tt>)
66056      */
66057     waitTitle: 'Please Wait...',
66058
66059     // private
66060     activeAction : null,
66061
66062     /**
66063      * @cfg {Boolean} trackResetOnLoad If set to <tt>true</tt>, {@link #reset}() resets to the last loaded
66064      * or {@link #setValues}() data instead of when the form was first created.  Defaults to <tt>false</tt>.
66065      */
66066     trackResetOnLoad : false,
66067
66068     /**
66069      * @cfg {Boolean} standardSubmit
66070      * <p>If set to <tt>true</tt>, standard HTML form submits are used instead
66071      * of XHR (Ajax) style form submissions. Defaults to <tt>false</tt>.</p>
66072      * <br><p><b>Note:</b> When using <code>standardSubmit</code>, the
66073      * <code>options</code> to <code>{@link #submit}</code> are ignored because
66074      * Ext's Ajax infrastracture is bypassed. To pass extra parameters (e.g.
66075      * <code>baseParams</code> and <code>params</code>), utilize hidden fields
66076      * to submit extra data, for example:</p>
66077      * <pre><code>
66078 new Ext.FormPanel({
66079     standardSubmit: true,
66080     baseParams: {
66081         foo: 'bar'
66082     },
66083     {@link url}: 'myProcess.php',
66084     items: [{
66085         xtype: 'textfield',
66086         name: 'userName'
66087     }],
66088     buttons: [{
66089         text: 'Save',
66090         handler: function(){
66091             var fp = this.ownerCt.ownerCt,
66092                 form = fp.getForm();
66093             if (form.isValid()) {
66094                 // check if there are baseParams and if
66095                 // hiddent items have been added already
66096                 if (fp.baseParams && !fp.paramsAdded) {
66097                     // add hidden items for all baseParams
66098                     for (i in fp.baseParams) {
66099                         fp.add({
66100                             xtype: 'hidden',
66101                             name: i,
66102                             value: fp.baseParams[i]
66103                         });
66104                     }
66105                     fp.doLayout();
66106                     // set a custom flag to prevent re-adding
66107                     fp.paramsAdded = true;
66108                 }
66109                 form.{@link #submit}();
66110             }
66111         }
66112     }]
66113 });
66114      * </code></pre>
66115      */
66116     /**
66117      * By default wait messages are displayed with Ext.MessageBox.wait. You can target a specific
66118      * element by passing it or its id or mask the form itself by passing in true.
66119      * @type Mixed
66120      * @property waitMsgTarget
66121      */
66122
66123     // private
66124     initEl : function(el){
66125         this.el = Ext.get(el);
66126         this.id = this.el.id || Ext.id();
66127         if(!this.standardSubmit){
66128             this.el.on('submit', this.onSubmit, this);
66129         }
66130         this.el.addClass('x-form');
66131     },
66132
66133     /**
66134      * Get the HTML form Element
66135      * @return Ext.Element
66136      */
66137     getEl: function(){
66138         return this.el;
66139     },
66140
66141     // private
66142     onSubmit : function(e){
66143         e.stopEvent();
66144     },
66145
66146     /**
66147      * Destroys this object.
66148      * @private
66149      * @param {Boolean} bound true if the object is bound to a form panel. If this is the case
66150      * the FormPanel will take care of destroying certain things, so we're just doubling up.
66151      */
66152     destroy: function(bound){
66153         if(bound !== true){
66154             this.items.each(function(f){
66155                 Ext.destroy(f);
66156             });
66157             Ext.destroy(this.el);
66158         }
66159         this.items.clear();
66160         this.purgeListeners();
66161     },
66162
66163     /**
66164      * Returns true if client-side validation on the form is successful.
66165      * @return Boolean
66166      */
66167     isValid : function(){
66168         var valid = true;
66169         this.items.each(function(f){
66170            if(!f.validate()){
66171                valid = false;
66172            }
66173         });
66174         return valid;
66175     },
66176
66177     /**
66178      * <p>Returns true if any fields in this form have changed from their original values.</p>
66179      * <p>Note that if this BasicForm was configured with {@link #trackResetOnLoad} then the
66180      * Fields' <i>original values</i> are updated when the values are loaded by {@link #setValues}
66181      * or {@link #loadRecord}.</p>
66182      * @return Boolean
66183      */
66184     isDirty : function(){
66185         var dirty = false;
66186         this.items.each(function(f){
66187            if(f.isDirty()){
66188                dirty = true;
66189                return false;
66190            }
66191         });
66192         return dirty;
66193     },
66194
66195     /**
66196      * Performs a predefined action ({@link Ext.form.Action.Submit} or
66197      * {@link Ext.form.Action.Load}) or a custom extension of {@link Ext.form.Action}
66198      * to perform application-specific processing.
66199      * @param {String/Object} actionName The name of the predefined action type,
66200      * or instance of {@link Ext.form.Action} to perform.
66201      * @param {Object} options (optional) The options to pass to the {@link Ext.form.Action}.
66202      * All of the config options listed below are supported by both the
66203      * {@link Ext.form.Action.Submit submit} and {@link Ext.form.Action.Load load}
66204      * actions unless otherwise noted (custom actions could also accept
66205      * other config options):<ul>
66206      *
66207      * <li><b>url</b> : String<div class="sub-desc">The url for the action (defaults
66208      * to the form's {@link #url}.)</div></li>
66209      *
66210      * <li><b>method</b> : String<div class="sub-desc">The form method to use (defaults
66211      * to the form's method, or POST if not defined)</div></li>
66212      *
66213      * <li><b>params</b> : String/Object<div class="sub-desc"><p>The params to pass
66214      * (defaults to the form's baseParams, or none if not defined)</p>
66215      * <p>Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.</p></div></li>
66216      *
66217      * <li><b>headers</b> : Object<div class="sub-desc">Request headers to set for the action
66218      * (defaults to the form's default headers)</div></li>
66219      *
66220      * <li><b>success</b> : Function<div class="sub-desc">The callback that will
66221      * be invoked after a successful response (see top of
66222      * {@link Ext.form.Action.Submit submit} and {@link Ext.form.Action.Load load}
66223      * for a description of what constitutes a successful response).
66224      * The function is passed the following parameters:<ul>
66225      * <li><tt>form</tt> : Ext.form.BasicForm<div class="sub-desc">The form that requested the action</div></li>
66226      * <li><tt>action</tt> : The {@link Ext.form.Action Action} object which performed the operation.
66227      * <div class="sub-desc">The action object contains these properties of interest:<ul>
66228      * <li><tt>{@link Ext.form.Action#response response}</tt></li>
66229      * <li><tt>{@link Ext.form.Action#result result}</tt> : interrogate for custom postprocessing</li>
66230      * <li><tt>{@link Ext.form.Action#type type}</tt></li>
66231      * </ul></div></li></ul></div></li>
66232      *
66233      * <li><b>failure</b> : Function<div class="sub-desc">The callback that will be invoked after a
66234      * failed transaction attempt. The function is passed the following parameters:<ul>
66235      * <li><tt>form</tt> : The {@link Ext.form.BasicForm} that requested the action.</li>
66236      * <li><tt>action</tt> : The {@link Ext.form.Action Action} object which performed the operation.
66237      * <div class="sub-desc">The action object contains these properties of interest:<ul>
66238      * <li><tt>{@link Ext.form.Action#failureType failureType}</tt></li>
66239      * <li><tt>{@link Ext.form.Action#response response}</tt></li>
66240      * <li><tt>{@link Ext.form.Action#result result}</tt> : interrogate for custom postprocessing</li>
66241      * <li><tt>{@link Ext.form.Action#type type}</tt></li>
66242      * </ul></div></li></ul></div></li>
66243      *
66244      * <li><b>scope</b> : Object<div class="sub-desc">The scope in which to call the
66245      * callback functions (The <tt>this</tt> reference for the callback functions).</div></li>
66246      *
66247      * <li><b>clientValidation</b> : Boolean<div class="sub-desc">Submit Action only.
66248      * Determines whether a Form's fields are validated in a final call to
66249      * {@link Ext.form.BasicForm#isValid isValid} prior to submission. Set to <tt>false</tt>
66250      * to prevent this. If undefined, pre-submission field validation is performed.</div></li></ul>
66251      *
66252      * @return {BasicForm} this
66253      */
66254     doAction : function(action, options){
66255         if(Ext.isString(action)){
66256             action = new Ext.form.Action.ACTION_TYPES[action](this, options);
66257         }
66258         if(this.fireEvent('beforeaction', this, action) !== false){
66259             this.beforeAction(action);
66260             action.run.defer(100, action);
66261         }
66262         return this;
66263     },
66264
66265     /**
66266      * Shortcut to {@link #doAction do} a {@link Ext.form.Action.Submit submit action}.
66267      * @param {Object} options The options to pass to the action (see {@link #doAction} for details).<br>
66268      * <p><b>Note:</b> this is ignored when using the {@link #standardSubmit} option.</p>
66269      * <p>The following code:</p><pre><code>
66270 myFormPanel.getForm().submit({
66271     clientValidation: true,
66272     url: 'updateConsignment.php',
66273     params: {
66274         newStatus: 'delivered'
66275     },
66276     success: function(form, action) {
66277        Ext.Msg.alert('Success', action.result.msg);
66278     },
66279     failure: function(form, action) {
66280         switch (action.failureType) {
66281             case Ext.form.Action.CLIENT_INVALID:
66282                 Ext.Msg.alert('Failure', 'Form fields may not be submitted with invalid values');
66283                 break;
66284             case Ext.form.Action.CONNECT_FAILURE:
66285                 Ext.Msg.alert('Failure', 'Ajax communication failed');
66286                 break;
66287             case Ext.form.Action.SERVER_INVALID:
66288                Ext.Msg.alert('Failure', action.result.msg);
66289        }
66290     }
66291 });
66292 </code></pre>
66293      * would process the following server response for a successful submission:<pre><code>
66294 {
66295     "success":true, // note this is Boolean, not string
66296     "msg":"Consignment updated"
66297 }
66298 </code></pre>
66299      * and the following server response for a failed submission:<pre><code>
66300 {
66301     "success":false, // note this is Boolean, not string
66302     "msg":"You do not have permission to perform this operation"
66303 }
66304 </code></pre>
66305      * @return {BasicForm} this
66306      */
66307     submit : function(options){
66308         options = options || {};
66309         if(this.standardSubmit){
66310             var v = options.clientValidation === false || this.isValid();
66311             if(v){
66312                 var el = this.el.dom;
66313                 if(this.url && Ext.isEmpty(el.action)){
66314                     el.action = this.url;
66315                 }
66316                 el.submit();
66317             }
66318             return v;
66319         }
66320         var submitAction = String.format('{0}submit', this.api ? 'direct' : '');
66321         this.doAction(submitAction, options);
66322         return this;
66323     },
66324
66325     /**
66326      * Shortcut to {@link #doAction do} a {@link Ext.form.Action.Load load action}.
66327      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
66328      * @return {BasicForm} this
66329      */
66330     load : function(options){
66331         var loadAction = String.format('{0}load', this.api ? 'direct' : '');
66332         this.doAction(loadAction, options);
66333         return this;
66334     },
66335
66336     /**
66337      * Persists the values in this form into the passed {@link Ext.data.Record} object in a beginEdit/endEdit block.
66338      * @param {Record} record The record to edit
66339      * @return {BasicForm} this
66340      */
66341     updateRecord : function(record){
66342         record.beginEdit();
66343         var fs = record.fields,
66344             field,
66345             value;
66346         fs.each(function(f){
66347             field = this.findField(f.name);
66348             if(field){
66349                 value = field.getValue();
66350                 if (typeof value != undefined && value.getGroupValue) {
66351                     value = value.getGroupValue();
66352                 } else if ( field.eachItem ) {
66353                     value = [];
66354                     field.eachItem(function(item){
66355                         value.push(item.getValue());
66356                     });
66357                 }
66358                 record.set(f.name, value);
66359             }
66360         }, this);
66361         record.endEdit();
66362         return this;
66363     },
66364
66365     /**
66366      * Loads an {@link Ext.data.Record} into this form by calling {@link #setValues} with the
66367      * {@link Ext.data.Record#data record data}.
66368      * See also {@link #trackResetOnLoad}.
66369      * @param {Record} record The record to load
66370      * @return {BasicForm} this
66371      */
66372     loadRecord : function(record){
66373         this.setValues(record.data);
66374         return this;
66375     },
66376
66377     // private
66378     beforeAction : function(action){
66379         // Call HtmlEditor's syncValue before actions
66380         this.items.each(function(f){
66381             if(f.isFormField && f.syncValue){
66382                 f.syncValue();
66383             }
66384         });
66385         var o = action.options;
66386         if(o.waitMsg){
66387             if(this.waitMsgTarget === true){
66388                 this.el.mask(o.waitMsg, 'x-mask-loading');
66389             }else if(this.waitMsgTarget){
66390                 this.waitMsgTarget = Ext.get(this.waitMsgTarget);
66391                 this.waitMsgTarget.mask(o.waitMsg, 'x-mask-loading');
66392             }else{
66393                 Ext.MessageBox.wait(o.waitMsg, o.waitTitle || this.waitTitle);
66394             }
66395         }
66396     },
66397
66398     // private
66399     afterAction : function(action, success){
66400         this.activeAction = null;
66401         var o = action.options;
66402         if(o.waitMsg){
66403             if(this.waitMsgTarget === true){
66404                 this.el.unmask();
66405             }else if(this.waitMsgTarget){
66406                 this.waitMsgTarget.unmask();
66407             }else{
66408                 Ext.MessageBox.updateProgress(1);
66409                 Ext.MessageBox.hide();
66410             }
66411         }
66412         if(success){
66413             if(o.reset){
66414                 this.reset();
66415             }
66416             Ext.callback(o.success, o.scope, [this, action]);
66417             this.fireEvent('actioncomplete', this, action);
66418         }else{
66419             Ext.callback(o.failure, o.scope, [this, action]);
66420             this.fireEvent('actionfailed', this, action);
66421         }
66422     },
66423
66424     /**
66425      * Find a {@link Ext.form.Field} in this form.
66426      * @param {String} id The value to search for (specify either a {@link Ext.Component#id id},
66427      * {@link Ext.grid.Column#dataIndex dataIndex}, {@link Ext.form.Field#getName name or hiddenName}).
66428      * @return Field
66429      */
66430     findField : function(id) {
66431         var field = this.items.get(id);
66432
66433         if (!Ext.isObject(field)) {
66434             //searches for the field corresponding to the given id. Used recursively for composite fields
66435             var findMatchingField = function(f) {
66436                 if (f.isFormField) {
66437                     if (f.dataIndex == id || f.id == id || f.getName() == id) {
66438                         field = f;
66439                         return false;
66440                     } else if (f.isComposite) {
66441                         return f.items.each(findMatchingField);
66442                     } else if (f instanceof Ext.form.CheckboxGroup && f.rendered) {
66443                         return f.eachItem(findMatchingField);
66444                     }
66445                 }
66446             };
66447
66448             this.items.each(findMatchingField);
66449         }
66450         return field || null;
66451     },
66452
66453
66454     /**
66455      * Mark fields in this form invalid in bulk.
66456      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
66457      * @return {BasicForm} this
66458      */
66459     markInvalid : function(errors){
66460         if (Ext.isArray(errors)) {
66461             for(var i = 0, len = errors.length; i < len; i++){
66462                 var fieldError = errors[i];
66463                 var f = this.findField(fieldError.id);
66464                 if(f){
66465                     f.markInvalid(fieldError.msg);
66466                 }
66467             }
66468         } else {
66469             var field, id;
66470             for(id in errors){
66471                 if(!Ext.isFunction(errors[id]) && (field = this.findField(id))){
66472                     field.markInvalid(errors[id]);
66473                 }
66474             }
66475         }
66476
66477         return this;
66478     },
66479
66480     /**
66481      * Set values for fields in this form in bulk.
66482      * @param {Array/Object} values Either an array in the form:<pre><code>
66483 [{id:'clientName', value:'Fred. Olsen Lines'},
66484  {id:'portOfLoading', value:'FXT'},
66485  {id:'portOfDischarge', value:'OSL'} ]</code></pre>
66486      * or an object hash of the form:<pre><code>
66487 {
66488     clientName: 'Fred. Olsen Lines',
66489     portOfLoading: 'FXT',
66490     portOfDischarge: 'OSL'
66491 }</code></pre>
66492      * @return {BasicForm} this
66493      */
66494     setValues : function(values){
66495         if(Ext.isArray(values)){ // array of objects
66496             for(var i = 0, len = values.length; i < len; i++){
66497                 var v = values[i];
66498                 var f = this.findField(v.id);
66499                 if(f){
66500                     f.setValue(v.value);
66501                     if(this.trackResetOnLoad){
66502                         f.originalValue = f.getValue();
66503                     }
66504                 }
66505             }
66506         }else{ // object hash
66507             var field, id;
66508             for(id in values){
66509                 if(!Ext.isFunction(values[id]) && (field = this.findField(id))){
66510                     field.setValue(values[id]);
66511                     if(this.trackResetOnLoad){
66512                         field.originalValue = field.getValue();
66513                     }
66514                 }
66515             }
66516         }
66517         return this;
66518     },
66519
66520     /**
66521      * <p>Returns the fields in this form as an object with key/value pairs as they would be submitted using a standard form submit.
66522      * If multiple fields exist with the same name they are returned as an array.</p>
66523      * <p><b>Note:</b> The values are collected from all enabled HTML input elements within the form, <u>not</u> from
66524      * the Ext Field objects. This means that all returned values are Strings (or Arrays of Strings) and that the
66525      * value can potentially be the emptyText of a field.</p>
66526      * @param {Boolean} asString (optional) Pass true to return the values as a string. (defaults to false, returning an Object)
66527      * @return {String/Object}
66528      */
66529     getValues : function(asString){
66530         var fs = Ext.lib.Ajax.serializeForm(this.el.dom);
66531         if(asString === true){
66532             return fs;
66533         }
66534         return Ext.urlDecode(fs);
66535     },
66536
66537     /**
66538      * Retrieves the fields in the form as a set of key/value pairs, using the {@link Ext.form.Field#getValue getValue()} method.
66539      * If multiple fields exist with the same name they are returned as an array.
66540      * @param {Boolean} dirtyOnly (optional) True to return only fields that are dirty.
66541      * @return {Object} The values in the form
66542      */
66543     getFieldValues : function(dirtyOnly){
66544         var o = {},
66545             n,
66546             key,
66547             val;
66548         this.items.each(function(f) {
66549             if (!f.disabled && (dirtyOnly !== true || f.isDirty())) {
66550                 n = f.getName();
66551                 key = o[n];
66552                 val = f.getValue();
66553
66554                 if(Ext.isDefined(key)){
66555                     if(Ext.isArray(key)){
66556                         o[n].push(val);
66557                     }else{
66558                         o[n] = [key, val];
66559                     }
66560                 }else{
66561                     o[n] = val;
66562                 }
66563             }
66564         });
66565         return o;
66566     },
66567
66568     /**
66569      * Clears all invalid messages in this form.
66570      * @return {BasicForm} this
66571      */
66572     clearInvalid : function(){
66573         this.items.each(function(f){
66574            f.clearInvalid();
66575         });
66576         return this;
66577     },
66578
66579     /**
66580      * Resets this form.
66581      * @return {BasicForm} this
66582      */
66583     reset : function(){
66584         this.items.each(function(f){
66585             f.reset();
66586         });
66587         return this;
66588     },
66589
66590     /**
66591      * Add Ext.form Components to this form's Collection. This does not result in rendering of
66592      * the passed Component, it just enables the form to validate Fields, and distribute values to
66593      * Fields.
66594      * <p><b>You will not usually call this function. In order to be rendered, a Field must be added
66595      * to a {@link Ext.Container Container}, usually an {@link Ext.form.FormPanel FormPanel}.
66596      * The FormPanel to which the field is added takes care of adding the Field to the BasicForm's
66597      * collection.</b></p>
66598      * @param {Field} field1
66599      * @param {Field} field2 (optional)
66600      * @param {Field} etc (optional)
66601      * @return {BasicForm} this
66602      */
66603     add : function(){
66604         this.items.addAll(Array.prototype.slice.call(arguments, 0));
66605         return this;
66606     },
66607
66608     /**
66609      * Removes a field from the items collection (does NOT remove its markup).
66610      * @param {Field} field
66611      * @return {BasicForm} this
66612      */
66613     remove : function(field){
66614         this.items.remove(field);
66615         return this;
66616     },
66617
66618     /**
66619      * Removes all fields from the collection that have been destroyed.
66620      */
66621     cleanDestroyed : function() {
66622         this.items.filterBy(function(o) { return !!o.isDestroyed; }).each(this.remove, this);
66623     },
66624
66625     /**
66626      * Iterates through the {@link Ext.form.Field Field}s which have been {@link #add add}ed to this BasicForm,
66627      * checks them for an id attribute, and calls {@link Ext.form.Field#applyToMarkup} on the existing dom element with that id.
66628      * @return {BasicForm} this
66629      */
66630     render : function(){
66631         this.items.each(function(f){
66632             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
66633                 f.applyToMarkup(f.id);
66634             }
66635         });
66636         return this;
66637     },
66638
66639     /**
66640      * Calls {@link Ext#apply} for all fields in this form with the passed object.
66641      * @param {Object} values
66642      * @return {BasicForm} this
66643      */
66644     applyToFields : function(o){
66645         this.items.each(function(f){
66646            Ext.apply(f, o);
66647         });
66648         return this;
66649     },
66650
66651     /**
66652      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
66653      * @param {Object} values
66654      * @return {BasicForm} this
66655      */
66656     applyIfToFields : function(o){
66657         this.items.each(function(f){
66658            Ext.applyIf(f, o);
66659         });
66660         return this;
66661     },
66662
66663     callFieldMethod : function(fnName, args){
66664         args = args || [];
66665         this.items.each(function(f){
66666             if(Ext.isFunction(f[fnName])){
66667                 f[fnName].apply(f, args);
66668             }
66669         });
66670         return this;
66671     }
66672 });
66673
66674 // back compat
66675 Ext.BasicForm = Ext.form.BasicForm;
66676 /**
66677  * @class Ext.form.FormPanel
66678  * @extends Ext.Panel
66679  * <p>Standard form container.</p>
66680  *
66681  * <p><b><u>Layout</u></b></p>
66682  * <p>By default, FormPanel is configured with <tt>layout:'form'</tt> to use an {@link Ext.layout.FormLayout}
66683  * layout manager, which styles and renders fields and labels correctly. When nesting additional Containers
66684  * within a FormPanel, you should ensure that any descendant Containers which host input Fields use the
66685  * {@link Ext.layout.FormLayout} layout manager.</p>
66686  *
66687  * <p><b><u>BasicForm</u></b></p>
66688  * <p>Although <b>not listed</b> as configuration options of FormPanel, the FormPanel class accepts all
66689  * of the config options required to configure its internal {@link Ext.form.BasicForm} for:
66690  * <div class="mdetail-params"><ul>
66691  * <li>{@link Ext.form.BasicForm#fileUpload file uploads}</li>
66692  * <li>functionality for {@link Ext.form.BasicForm#doAction loading, validating and submitting} the form</li>
66693  * </ul></div>
66694  *
66695  * <p><b>Note</b>: If subclassing FormPanel, any configuration options for the BasicForm must be applied to
66696  * the <tt><b>initialConfig</b></tt> property of the FormPanel. Applying {@link Ext.form.BasicForm BasicForm}
66697  * configuration settings to <b><tt>this</tt></b> will <b>not</b> affect the BasicForm's configuration.</p>
66698  *
66699  * <p><b><u>Form Validation</u></b></p>
66700  * <p>For information on form validation see the following:</p>
66701  * <div class="mdetail-params"><ul>
66702  * <li>{@link Ext.form.TextField}</li>
66703  * <li>{@link Ext.form.VTypes}</li>
66704  * <li>{@link Ext.form.BasicForm#doAction BasicForm.doAction <b>clientValidation</b> notes}</li>
66705  * <li><tt>{@link Ext.form.FormPanel#monitorValid monitorValid}</tt></li>
66706  * </ul></div>
66707  *
66708  * <p><b><u>Form Submission</u></b></p>
66709  * <p>By default, Ext Forms are submitted through Ajax, using {@link Ext.form.Action}. To enable normal browser
66710  * submission of the {@link Ext.form.BasicForm BasicForm} contained in this FormPanel, see the
66711  * <tt><b>{@link Ext.form.BasicForm#standardSubmit standardSubmit}</b></tt> option.</p>
66712  *
66713  * @constructor
66714  * @param {Object} config Configuration options
66715  * @xtype form
66716  */
66717 Ext.FormPanel = Ext.extend(Ext.Panel, {
66718     /**
66719      * @cfg {String} formId (optional) The id of the FORM tag (defaults to an auto-generated id).
66720      */
66721     /**
66722      * @cfg {Boolean} hideLabels
66723      * <p><tt>true</tt> to hide field labels by default (sets <tt>display:none</tt>). Defaults to
66724      * <tt>false</tt>.</p>
66725      * <p>Also see {@link Ext.Component}.<tt>{@link Ext.Component#hideLabel hideLabel}</tt>.
66726      */
66727     /**
66728      * @cfg {Number} labelPad
66729      * The default padding in pixels for field labels (defaults to <tt>5</tt>). <tt>labelPad</tt> only
66730      * applies if <tt>{@link #labelWidth}</tt> is also specified, otherwise it will be ignored.
66731      */
66732     /**
66733      * @cfg {String} labelSeparator
66734      * See {@link Ext.Component}.<tt>{@link Ext.Component#labelSeparator labelSeparator}</tt>
66735      */
66736     /**
66737      * @cfg {Number} labelWidth The width of labels in pixels. This property cascades to child containers
66738      * and can be overridden on any child container (e.g., a fieldset can specify a different <tt>labelWidth</tt>
66739      * for its fields) (defaults to <tt>100</tt>).
66740      */
66741     /**
66742      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
66743      */
66744     /**
66745      * @cfg {Array} buttons
66746      * An array of {@link Ext.Button}s or {@link Ext.Button} configs used to add buttons to the footer of this FormPanel.<br>
66747      * <p>Buttons in the footer of a FormPanel may be configured with the option <tt>formBind: true</tt>. This causes
66748      * the form's {@link #monitorValid valid state monitor task} to enable/disable those Buttons depending on
66749      * the form's valid/invalid state.</p>
66750      */
66751
66752
66753     /**
66754      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to <tt>75</tt>).
66755      */
66756     minButtonWidth : 75,
66757
66758     /**
66759      * @cfg {String} labelAlign The label alignment value used for the <tt>text-align</tt> specification
66760      * for the <b>container</b>. Valid values are <tt>"left</tt>", <tt>"top"</tt> or <tt>"right"</tt>
66761      * (defaults to <tt>"left"</tt>). This property cascades to child <b>containers</b> and can be
66762      * overridden on any child <b>container</b> (e.g., a fieldset can specify a different <tt>labelAlign</tt>
66763      * for its fields).
66764      */
66765     labelAlign : 'left',
66766
66767     /**
66768      * @cfg {Boolean} monitorValid If <tt>true</tt>, the form monitors its valid state <b>client-side</b> and
66769      * regularly fires the {@link #clientvalidation} event passing that state.<br>
66770      * <p>When monitoring valid state, the FormPanel enables/disables any of its configured
66771      * {@link #buttons} which have been configured with <code>formBind: true</code> depending
66772      * on whether the {@link Ext.form.BasicForm#isValid form is valid} or not. Defaults to <tt>false</tt></p>
66773      */
66774     monitorValid : false,
66775
66776     /**
66777      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
66778      */
66779     monitorPoll : 200,
66780
66781     /**
66782      * @cfg {String} layout Defaults to <tt>'form'</tt>.  Normally this configuration property should not be altered.
66783      * For additional details see {@link Ext.layout.FormLayout} and {@link Ext.Container#layout Ext.Container.layout}.
66784      */
66785     layout : 'form',
66786
66787     // private
66788     initComponent : function(){
66789         this.form = this.createForm();
66790         Ext.FormPanel.superclass.initComponent.call(this);
66791
66792         this.bodyCfg = {
66793             tag: 'form',
66794             cls: this.baseCls + '-body',
66795             method : this.method || 'POST',
66796             id : this.formId || Ext.id()
66797         };
66798         if(this.fileUpload) {
66799             this.bodyCfg.enctype = 'multipart/form-data';
66800         }
66801         this.initItems();
66802
66803         this.addEvents(
66804             /**
66805              * @event clientvalidation
66806              * If the monitorValid config option is true, this event fires repetitively to notify of valid state
66807              * @param {Ext.form.FormPanel} this
66808              * @param {Boolean} valid true if the form has passed client-side validation
66809              */
66810             'clientvalidation'
66811         );
66812
66813         this.relayEvents(this.form, ['beforeaction', 'actionfailed', 'actioncomplete']);
66814     },
66815
66816     // private
66817     createForm : function(){
66818         var config = Ext.applyIf({listeners: {}}, this.initialConfig);
66819         return new Ext.form.BasicForm(null, config);
66820     },
66821
66822     // private
66823     initFields : function(){
66824         var f = this.form;
66825         var formPanel = this;
66826         var fn = function(c){
66827             if(formPanel.isField(c)){
66828                 f.add(c);
66829             }else if(c.findBy && c != formPanel){
66830                 formPanel.applySettings(c);
66831                 //each check required for check/radio groups.
66832                 if(c.items && c.items.each){
66833                     c.items.each(fn, this);
66834                 }
66835             }
66836         };
66837         this.items.each(fn, this);
66838     },
66839
66840     // private
66841     applySettings: function(c){
66842         var ct = c.ownerCt;
66843         Ext.applyIf(c, {
66844             labelAlign: ct.labelAlign,
66845             labelWidth: ct.labelWidth,
66846             itemCls: ct.itemCls
66847         });
66848     },
66849
66850     // private
66851     getLayoutTarget : function(){
66852         return this.form.el;
66853     },
66854
66855     /**
66856      * Provides access to the {@link Ext.form.BasicForm Form} which this Panel contains.
66857      * @return {Ext.form.BasicForm} The {@link Ext.form.BasicForm Form} which this Panel contains.
66858      */
66859     getForm : function(){
66860         return this.form;
66861     },
66862
66863     // private
66864     onRender : function(ct, position){
66865         this.initFields();
66866         Ext.FormPanel.superclass.onRender.call(this, ct, position);
66867         this.form.initEl(this.body);
66868     },
66869
66870     // private
66871     beforeDestroy : function(){
66872         this.stopMonitoring();
66873         this.form.destroy(true);
66874         Ext.FormPanel.superclass.beforeDestroy.call(this);
66875     },
66876
66877     // Determine if a Component is usable as a form Field.
66878     isField : function(c) {
66879         return !!c.setValue && !!c.getValue && !!c.markInvalid && !!c.clearInvalid;
66880     },
66881
66882     // private
66883     initEvents : function(){
66884         Ext.FormPanel.superclass.initEvents.call(this);
66885         // Listeners are required here to catch bubbling events from children.
66886         this.on({
66887             scope: this,
66888             add: this.onAddEvent,
66889             remove: this.onRemoveEvent
66890         });
66891         if(this.monitorValid){ // initialize after render
66892             this.startMonitoring();
66893         }
66894     },
66895
66896     // private
66897     onAdd: function(c){
66898         Ext.FormPanel.superclass.onAdd.call(this, c);
66899         this.processAdd(c);
66900     },
66901
66902     // private
66903     onAddEvent: function(ct, c){
66904         if(ct !== this){
66905             this.processAdd(c);
66906         }
66907     },
66908
66909     // private
66910     processAdd : function(c){
66911         // If a single form Field, add it
66912         if(this.isField(c)){
66913             this.form.add(c);
66914         // If a Container, add any Fields it might contain
66915         }else if(c.findBy){
66916             this.applySettings(c);
66917             this.form.add.apply(this.form, c.findBy(this.isField));
66918         }
66919     },
66920
66921     // private
66922     onRemove: function(c){
66923         Ext.FormPanel.superclass.onRemove.call(this, c);
66924         this.processRemove(c);
66925     },
66926
66927     onRemoveEvent: function(ct, c){
66928         if(ct !== this){
66929             this.processRemove(c);
66930         }
66931     },
66932
66933     // private
66934     processRemove: function(c){
66935         if(!this.destroying){
66936             // If a single form Field, remove it
66937             if(this.isField(c)){
66938                 this.form.remove(c);
66939             // If a Container, its already destroyed by the time it gets here.  Remove any references to destroyed fields.
66940             }else if (c.findBy){
66941                 Ext.each(c.findBy(this.isField), this.form.remove, this.form);
66942                 /*
66943                  * This isn't the most efficient way of getting rid of the items, however it's the most
66944                  * correct, which in this case is most important.
66945                  */
66946                 this.form.cleanDestroyed();
66947             }
66948         }
66949     },
66950
66951     /**
66952      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
66953      * option "monitorValid"
66954      */
66955     startMonitoring : function(){
66956         if(!this.validTask){
66957             this.validTask = new Ext.util.TaskRunner();
66958             this.validTask.start({
66959                 run : this.bindHandler,
66960                 interval : this.monitorPoll || 200,
66961                 scope: this
66962             });
66963         }
66964     },
66965
66966     /**
66967      * Stops monitoring of the valid state of this form
66968      */
66969     stopMonitoring : function(){
66970         if(this.validTask){
66971             this.validTask.stopAll();
66972             this.validTask = null;
66973         }
66974     },
66975
66976     /**
66977      * This is a proxy for the underlying BasicForm's {@link Ext.form.BasicForm#load} call.
66978      * @param {Object} options The options to pass to the action (see {@link Ext.form.BasicForm#doAction} for details)
66979      */
66980     load : function(){
66981         this.form.load.apply(this.form, arguments);
66982     },
66983
66984     // private
66985     onDisable : function(){
66986         Ext.FormPanel.superclass.onDisable.call(this);
66987         if(this.form){
66988             this.form.items.each(function(){
66989                  this.disable();
66990             });
66991         }
66992     },
66993
66994     // private
66995     onEnable : function(){
66996         Ext.FormPanel.superclass.onEnable.call(this);
66997         if(this.form){
66998             this.form.items.each(function(){
66999                  this.enable();
67000             });
67001         }
67002     },
67003
67004     // private
67005     bindHandler : function(){
67006         var valid = true;
67007         this.form.items.each(function(f){
67008             if(!f.isValid(true)){
67009                 valid = false;
67010                 return false;
67011             }
67012         });
67013         if(this.fbar){
67014             var fitems = this.fbar.items.items;
67015             for(var i = 0, len = fitems.length; i < len; i++){
67016                 var btn = fitems[i];
67017                 if(btn.formBind === true && btn.disabled === valid){
67018                     btn.setDisabled(!valid);
67019                 }
67020             }
67021         }
67022         this.fireEvent('clientvalidation', this, valid);
67023     }
67024 });
67025 Ext.reg('form', Ext.FormPanel);
67026
67027 Ext.form.FormPanel = Ext.FormPanel;
67028 /**
67029  * @class Ext.form.FieldSet
67030  * @extends Ext.Panel
67031  * Standard container used for grouping items within a {@link Ext.form.FormPanel form}.
67032  * <pre><code>
67033 var form = new Ext.FormPanel({
67034     title: 'Simple Form with FieldSets',
67035     labelWidth: 75, // label settings here cascade unless overridden
67036     url: 'save-form.php',
67037     frame:true,
67038     bodyStyle:'padding:5px 5px 0',
67039     width: 700,
67040     renderTo: document.body,
67041     layout:'column', // arrange items in columns
67042     defaults: {      // defaults applied to items
67043         layout: 'form',
67044         border: false,
67045         bodyStyle: 'padding:4px'
67046     },
67047     items: [{
67048         // Fieldset in Column 1
67049         xtype:'fieldset',
67050         columnWidth: 0.5,
67051         title: 'Fieldset 1',
67052         collapsible: true,
67053         autoHeight:true,
67054         defaults: {
67055             anchor: '-20' // leave room for error icon
67056         },
67057         defaultType: 'textfield',
67058         items :[{
67059                 fieldLabel: 'Field 1'
67060             }, {
67061                 fieldLabel: 'Field 2'
67062             }, {
67063                 fieldLabel: 'Field 3'
67064             }
67065         ]
67066     },{
67067         // Fieldset in Column 2 - Panel inside
67068         xtype:'fieldset',
67069         title: 'Show Panel', // title, header, or checkboxToggle creates fieldset header
67070         autoHeight:true,
67071         columnWidth: 0.5,
67072         checkboxToggle: true,
67073         collapsed: true, // fieldset initially collapsed
67074         layout:'anchor',
67075         items :[{
67076             xtype: 'panel',
67077             anchor: '100%',
67078             title: 'Panel inside a fieldset',
67079             frame: true,
67080             height: 100
67081         }]
67082     }]
67083 });
67084  * </code></pre>
67085  * @constructor
67086  * @param {Object} config Configuration options
67087  * @xtype fieldset
67088  */
67089 Ext.form.FieldSet = Ext.extend(Ext.Panel, {
67090     /**
67091      * @cfg {Mixed} checkboxToggle <tt>true</tt> to render a checkbox into the fieldset frame just
67092      * in front of the legend to expand/collapse the fieldset when the checkbox is toggled. (defaults
67093      * to <tt>false</tt>).
67094      * <p>A {@link Ext.DomHelper DomHelper} element spec may also be specified to create the checkbox.
67095      * If <tt>true</tt> is specified, the default DomHelper config object used to create the element
67096      * is:</p><pre><code>
67097      * {tag: 'input', type: 'checkbox', name: this.checkboxName || this.id+'-checkbox'}
67098      * </code></pre>
67099      */
67100     /**
67101      * @cfg {String} checkboxName The name to assign to the fieldset's checkbox if <tt>{@link #checkboxToggle} = true</tt>
67102      * (defaults to <tt>'[checkbox id]-checkbox'</tt>).
67103      */
67104     /**
67105      * @cfg {Boolean} collapsible
67106      * <tt>true</tt> to make the fieldset collapsible and have the expand/collapse toggle button automatically
67107      * rendered into the legend element, <tt>false</tt> to keep the fieldset statically sized with no collapse
67108      * button (defaults to <tt>false</tt>). Another option is to configure <tt>{@link #checkboxToggle}</tt>.
67109      */
67110     /**
67111      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
67112      */
67113     /**
67114      * @cfg {String} itemCls A css class to apply to the <tt>x-form-item</tt> of fields (see
67115      * {@link Ext.layout.FormLayout}.{@link Ext.layout.FormLayout#fieldTpl fieldTpl} for details).
67116      * This property cascades to child containers.
67117      */
67118     /**
67119      * @cfg {String} baseCls The base CSS class applied to the fieldset (defaults to <tt>'x-fieldset'</tt>).
67120      */
67121     baseCls : 'x-fieldset',
67122     /**
67123      * @cfg {String} layout The {@link Ext.Container#layout} to use inside the fieldset (defaults to <tt>'form'</tt>).
67124      */
67125     layout : 'form',
67126     /**
67127      * @cfg {Boolean} animCollapse
67128      * <tt>true</tt> to animate the transition when the panel is collapsed, <tt>false</tt> to skip the
67129      * animation (defaults to <tt>false</tt>).
67130      */
67131     animCollapse : false,
67132
67133     // private
67134     onRender : function(ct, position){
67135         if(!this.el){
67136             this.el = document.createElement('fieldset');
67137             this.el.id = this.id;
67138             if (this.title || this.header || this.checkboxToggle) {
67139                 this.el.appendChild(document.createElement('legend')).className = this.baseCls + '-header';
67140             }
67141         }
67142
67143         Ext.form.FieldSet.superclass.onRender.call(this, ct, position);
67144
67145         if(this.checkboxToggle){
67146             var o = typeof this.checkboxToggle == 'object' ?
67147                     this.checkboxToggle :
67148                     {tag: 'input', type: 'checkbox', name: this.checkboxName || this.id+'-checkbox'};
67149             this.checkbox = this.header.insertFirst(o);
67150             this.checkbox.dom.checked = !this.collapsed;
67151             this.mon(this.checkbox, 'click', this.onCheckClick, this);
67152         }
67153     },
67154
67155     // private
67156     onCollapse : function(doAnim, animArg){
67157         if(this.checkbox){
67158             this.checkbox.dom.checked = false;
67159         }
67160         Ext.form.FieldSet.superclass.onCollapse.call(this, doAnim, animArg);
67161
67162     },
67163
67164     // private
67165     onExpand : function(doAnim, animArg){
67166         if(this.checkbox){
67167             this.checkbox.dom.checked = true;
67168         }
67169         Ext.form.FieldSet.superclass.onExpand.call(this, doAnim, animArg);
67170     },
67171
67172     /**
67173      * This function is called by the fieldset's checkbox when it is toggled (only applies when
67174      * checkboxToggle = true).  This method should never be called externally, but can be
67175      * overridden to provide custom behavior when the checkbox is toggled if needed.
67176      */
67177     onCheckClick : function(){
67178         this[this.checkbox.dom.checked ? 'expand' : 'collapse']();
67179     }
67180
67181     /**
67182      * @cfg {String/Number} activeItem
67183      * @hide
67184      */
67185     /**
67186      * @cfg {Mixed} applyTo
67187      * @hide
67188      */
67189     /**
67190      * @cfg {Boolean} bodyBorder
67191      * @hide
67192      */
67193     /**
67194      * @cfg {Boolean} border
67195      * @hide
67196      */
67197     /**
67198      * @cfg {Boolean/Number} bufferResize
67199      * @hide
67200      */
67201     /**
67202      * @cfg {Boolean} collapseFirst
67203      * @hide
67204      */
67205     /**
67206      * @cfg {String} defaultType
67207      * @hide
67208      */
67209     /**
67210      * @cfg {String} disabledClass
67211      * @hide
67212      */
67213     /**
67214      * @cfg {String} elements
67215      * @hide
67216      */
67217     /**
67218      * @cfg {Boolean} floating
67219      * @hide
67220      */
67221     /**
67222      * @cfg {Boolean} footer
67223      * @hide
67224      */
67225     /**
67226      * @cfg {Boolean} frame
67227      * @hide
67228      */
67229     /**
67230      * @cfg {Boolean} header
67231      * @hide
67232      */
67233     /**
67234      * @cfg {Boolean} headerAsText
67235      * @hide
67236      */
67237     /**
67238      * @cfg {Boolean} hideCollapseTool
67239      * @hide
67240      */
67241     /**
67242      * @cfg {String} iconCls
67243      * @hide
67244      */
67245     /**
67246      * @cfg {Boolean/String} shadow
67247      * @hide
67248      */
67249     /**
67250      * @cfg {Number} shadowOffset
67251      * @hide
67252      */
67253     /**
67254      * @cfg {Boolean} shim
67255      * @hide
67256      */
67257     /**
67258      * @cfg {Object/Array} tbar
67259      * @hide
67260      */
67261     /**
67262      * @cfg {Array} tools
67263      * @hide
67264      */
67265     /**
67266      * @cfg {Ext.Template/Ext.XTemplate} toolTemplate
67267      * @hide
67268      */
67269     /**
67270      * @cfg {String} xtype
67271      * @hide
67272      */
67273     /**
67274      * @property header
67275      * @hide
67276      */
67277     /**
67278      * @property footer
67279      * @hide
67280      */
67281     /**
67282      * @method focus
67283      * @hide
67284      */
67285     /**
67286      * @method getBottomToolbar
67287      * @hide
67288      */
67289     /**
67290      * @method getTopToolbar
67291      * @hide
67292      */
67293     /**
67294      * @method setIconClass
67295      * @hide
67296      */
67297     /**
67298      * @event activate
67299      * @hide
67300      */
67301     /**
67302      * @event beforeclose
67303      * @hide
67304      */
67305     /**
67306      * @event bodyresize
67307      * @hide
67308      */
67309     /**
67310      * @event close
67311      * @hide
67312      */
67313     /**
67314      * @event deactivate
67315      * @hide
67316      */
67317 });
67318 Ext.reg('fieldset', Ext.form.FieldSet);/**
67319  * @class Ext.form.HtmlEditor
67320  * @extends Ext.form.Field
67321  * Provides a lightweight HTML Editor component. Some toolbar features are not supported by Safari and will be
67322  * automatically hidden when needed.  These are noted in the config options where appropriate.
67323  * <br><br>The editor's toolbar buttons have tooltips defined in the {@link #buttonTips} property, but they are not
67324  * enabled by default unless the global {@link Ext.QuickTips} singleton is {@link Ext.QuickTips#init initialized}.
67325  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
67326  * supported by this editor.</b>
67327  * <br><br>An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
67328  * any element that has display set to 'none' can cause problems in Safari and Firefox due to their default iframe reloading bugs.
67329  * <br><br>Example usage:
67330  * <pre><code>
67331 // Simple example rendered with default options:
67332 Ext.QuickTips.init();  // enable tooltips
67333 new Ext.form.HtmlEditor({
67334     renderTo: Ext.getBody(),
67335     width: 800,
67336     height: 300
67337 });
67338
67339 // Passed via xtype into a container and with custom options:
67340 Ext.QuickTips.init();  // enable tooltips
67341 new Ext.Panel({
67342     title: 'HTML Editor',
67343     renderTo: Ext.getBody(),
67344     width: 600,
67345     height: 300,
67346     frame: true,
67347     layout: 'fit',
67348     items: {
67349         xtype: 'htmleditor',
67350         enableColors: false,
67351         enableAlignments: false
67352     }
67353 });
67354 </code></pre>
67355  * @constructor
67356  * Create a new HtmlEditor
67357  * @param {Object} config
67358  * @xtype htmleditor
67359  */
67360
67361 Ext.form.HtmlEditor = Ext.extend(Ext.form.Field, {
67362     /**
67363      * @cfg {Boolean} enableFormat Enable the bold, italic and underline buttons (defaults to true)
67364      */
67365     enableFormat : true,
67366     /**
67367      * @cfg {Boolean} enableFontSize Enable the increase/decrease font size buttons (defaults to true)
67368      */
67369     enableFontSize : true,
67370     /**
67371      * @cfg {Boolean} enableColors Enable the fore/highlight color buttons (defaults to true)
67372      */
67373     enableColors : true,
67374     /**
67375      * @cfg {Boolean} enableAlignments Enable the left, center, right alignment buttons (defaults to true)
67376      */
67377     enableAlignments : true,
67378     /**
67379      * @cfg {Boolean} enableLists Enable the bullet and numbered list buttons. Not available in Safari. (defaults to true)
67380      */
67381     enableLists : true,
67382     /**
67383      * @cfg {Boolean} enableSourceEdit Enable the switch to source edit button. Not available in Safari. (defaults to true)
67384      */
67385     enableSourceEdit : true,
67386     /**
67387      * @cfg {Boolean} enableLinks Enable the create link button. Not available in Safari. (defaults to true)
67388      */
67389     enableLinks : true,
67390     /**
67391      * @cfg {Boolean} enableFont Enable font selection. Not available in Safari. (defaults to true)
67392      */
67393     enableFont : true,
67394     /**
67395      * @cfg {String} createLinkText The default text for the create link prompt
67396      */
67397     createLinkText : 'Please enter the URL for the link:',
67398     /**
67399      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
67400      */
67401     defaultLinkValue : 'http:/'+'/',
67402     /**
67403      * @cfg {Array} fontFamilies An array of available font families
67404      */
67405     fontFamilies : [
67406         'Arial',
67407         'Courier New',
67408         'Tahoma',
67409         'Times New Roman',
67410         'Verdana'
67411     ],
67412     defaultFont: 'tahoma',
67413     /**
67414      * @cfg {String} defaultValue A default value to be put into the editor to resolve focus issues (defaults to &#160; (Non-breaking space) in Opera and IE6, &#8203; (Zero-width space) in all other browsers).
67415      */
67416     defaultValue: (Ext.isOpera || Ext.isIE6) ? '&#160;' : '&#8203;',
67417
67418     // private properties
67419     actionMode: 'wrap',
67420     validationEvent : false,
67421     deferHeight: true,
67422     initialized : false,
67423     activated : false,
67424     sourceEditMode : false,
67425     onFocus : Ext.emptyFn,
67426     iframePad:3,
67427     hideMode:'offsets',
67428     defaultAutoCreate : {
67429         tag: "textarea",
67430         style:"width:500px;height:300px;",
67431         autocomplete: "off"
67432     },
67433
67434     // private
67435     initComponent : function(){
67436         this.addEvents(
67437             /**
67438              * @event initialize
67439              * Fires when the editor is fully initialized (including the iframe)
67440              * @param {HtmlEditor} this
67441              */
67442             'initialize',
67443             /**
67444              * @event activate
67445              * Fires when the editor is first receives the focus. Any insertion must wait
67446              * until after this event.
67447              * @param {HtmlEditor} this
67448              */
67449             'activate',
67450              /**
67451              * @event beforesync
67452              * Fires before the textarea is updated with content from the editor iframe. Return false
67453              * to cancel the sync.
67454              * @param {HtmlEditor} this
67455              * @param {String} html
67456              */
67457             'beforesync',
67458              /**
67459              * @event beforepush
67460              * Fires before the iframe editor is updated with content from the textarea. Return false
67461              * to cancel the push.
67462              * @param {HtmlEditor} this
67463              * @param {String} html
67464              */
67465             'beforepush',
67466              /**
67467              * @event sync
67468              * Fires when the textarea is updated with content from the editor iframe.
67469              * @param {HtmlEditor} this
67470              * @param {String} html
67471              */
67472             'sync',
67473              /**
67474              * @event push
67475              * Fires when the iframe editor is updated with content from the textarea.
67476              * @param {HtmlEditor} this
67477              * @param {String} html
67478              */
67479             'push',
67480              /**
67481              * @event editmodechange
67482              * Fires when the editor switches edit modes
67483              * @param {HtmlEditor} this
67484              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
67485              */
67486             'editmodechange'
67487         );
67488         Ext.form.HtmlEditor.superclass.initComponent.call(this);
67489     },
67490
67491     // private
67492     createFontOptions : function(){
67493         var buf = [], fs = this.fontFamilies, ff, lc;
67494         for(var i = 0, len = fs.length; i< len; i++){
67495             ff = fs[i];
67496             lc = ff.toLowerCase();
67497             buf.push(
67498                 '<option value="',lc,'" style="font-family:',ff,';"',
67499                     (this.defaultFont == lc ? ' selected="true">' : '>'),
67500                     ff,
67501                 '</option>'
67502             );
67503         }
67504         return buf.join('');
67505     },
67506
67507     /*
67508      * Protected method that will not generally be called directly. It
67509      * is called when the editor creates its toolbar. Override this method if you need to
67510      * add custom toolbar buttons.
67511      * @param {HtmlEditor} editor
67512      */
67513     createToolbar : function(editor){
67514         var items = [];
67515         var tipsEnabled = Ext.QuickTips && Ext.QuickTips.isEnabled();
67516
67517
67518         function btn(id, toggle, handler){
67519             return {
67520                 itemId : id,
67521                 cls : 'x-btn-icon',
67522                 iconCls: 'x-edit-'+id,
67523                 enableToggle:toggle !== false,
67524                 scope: editor,
67525                 handler:handler||editor.relayBtnCmd,
67526                 clickEvent:'mousedown',
67527                 tooltip: tipsEnabled ? editor.buttonTips[id] || undefined : undefined,
67528                 overflowText: editor.buttonTips[id].title || undefined,
67529                 tabIndex:-1
67530             };
67531         }
67532
67533
67534         if(this.enableFont && !Ext.isSafari2){
67535             var fontSelectItem = new Ext.Toolbar.Item({
67536                autoEl: {
67537                     tag:'select',
67538                     cls:'x-font-select',
67539                     html: this.createFontOptions()
67540                }
67541             });
67542
67543             items.push(
67544                 fontSelectItem,
67545                 '-'
67546             );
67547         }
67548
67549         if(this.enableFormat){
67550             items.push(
67551                 btn('bold'),
67552                 btn('italic'),
67553                 btn('underline')
67554             );
67555         }
67556
67557         if(this.enableFontSize){
67558             items.push(
67559                 '-',
67560                 btn('increasefontsize', false, this.adjustFont),
67561                 btn('decreasefontsize', false, this.adjustFont)
67562             );
67563         }
67564
67565         if(this.enableColors){
67566             items.push(
67567                 '-', {
67568                     itemId:'forecolor',
67569                     cls:'x-btn-icon',
67570                     iconCls: 'x-edit-forecolor',
67571                     clickEvent:'mousedown',
67572                     tooltip: tipsEnabled ? editor.buttonTips.forecolor || undefined : undefined,
67573                     tabIndex:-1,
67574                     menu : new Ext.menu.ColorMenu({
67575                         allowReselect: true,
67576                         focus: Ext.emptyFn,
67577                         value:'000000',
67578                         plain:true,
67579                         listeners: {
67580                             scope: this,
67581                             select: function(cp, color){
67582                                 this.execCmd('forecolor', Ext.isWebKit || Ext.isIE ? '#'+color : color);
67583                                 this.deferFocus();
67584                             }
67585                         },
67586                         clickEvent:'mousedown'
67587                     })
67588                 }, {
67589                     itemId:'backcolor',
67590                     cls:'x-btn-icon',
67591                     iconCls: 'x-edit-backcolor',
67592                     clickEvent:'mousedown',
67593                     tooltip: tipsEnabled ? editor.buttonTips.backcolor || undefined : undefined,
67594                     tabIndex:-1,
67595                     menu : new Ext.menu.ColorMenu({
67596                         focus: Ext.emptyFn,
67597                         value:'FFFFFF',
67598                         plain:true,
67599                         allowReselect: true,
67600                         listeners: {
67601                             scope: this,
67602                             select: function(cp, color){
67603                                 if(Ext.isGecko){
67604                                     this.execCmd('useCSS', false);
67605                                     this.execCmd('hilitecolor', color);
67606                                     this.execCmd('useCSS', true);
67607                                     this.deferFocus();
67608                                 }else{
67609                                     this.execCmd(Ext.isOpera ? 'hilitecolor' : 'backcolor', Ext.isWebKit || Ext.isIE ? '#'+color : color);
67610                                     this.deferFocus();
67611                                 }
67612                             }
67613                         },
67614                         clickEvent:'mousedown'
67615                     })
67616                 }
67617             );
67618         }
67619
67620         if(this.enableAlignments){
67621             items.push(
67622                 '-',
67623                 btn('justifyleft'),
67624                 btn('justifycenter'),
67625                 btn('justifyright')
67626             );
67627         }
67628
67629         if(!Ext.isSafari2){
67630             if(this.enableLinks){
67631                 items.push(
67632                     '-',
67633                     btn('createlink', false, this.createLink)
67634                 );
67635             }
67636
67637             if(this.enableLists){
67638                 items.push(
67639                     '-',
67640                     btn('insertorderedlist'),
67641                     btn('insertunorderedlist')
67642                 );
67643             }
67644             if(this.enableSourceEdit){
67645                 items.push(
67646                     '-',
67647                     btn('sourceedit', true, function(btn){
67648                         this.toggleSourceEdit(!this.sourceEditMode);
67649                     })
67650                 );
67651             }
67652         }
67653
67654         // build the toolbar
67655         var tb = new Ext.Toolbar({
67656             renderTo: this.wrap.dom.firstChild,
67657             items: items
67658         });
67659
67660         if (fontSelectItem) {
67661             this.fontSelect = fontSelectItem.el;
67662
67663             this.mon(this.fontSelect, 'change', function(){
67664                 var font = this.fontSelect.dom.value;
67665                 this.relayCmd('fontname', font);
67666                 this.deferFocus();
67667             }, this);
67668         }
67669
67670         // stop form submits
67671         this.mon(tb.el, 'click', function(e){
67672             e.preventDefault();
67673         });
67674
67675         this.tb = tb;
67676         this.tb.doLayout();
67677     },
67678
67679     onDisable: function(){
67680         this.wrap.mask();
67681         Ext.form.HtmlEditor.superclass.onDisable.call(this);
67682     },
67683
67684     onEnable: function(){
67685         this.wrap.unmask();
67686         Ext.form.HtmlEditor.superclass.onEnable.call(this);
67687     },
67688
67689     setReadOnly: function(readOnly){
67690
67691         Ext.form.HtmlEditor.superclass.setReadOnly.call(this, readOnly);
67692         if(this.initialized){
67693             if(Ext.isIE){
67694                 this.getEditorBody().contentEditable = !readOnly;
67695             }else{
67696                 this.setDesignMode(!readOnly);
67697             }
67698             var bd = this.getEditorBody();
67699             if(bd){
67700                 bd.style.cursor = this.readOnly ? 'default' : 'text';
67701             }
67702             this.disableItems(readOnly);
67703         }
67704     },
67705
67706     /**
67707      * Protected method that will not generally be called directly. It
67708      * is called when the editor initializes the iframe with HTML contents. Override this method if you
67709      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
67710      *
67711      * Note: IE8-Standards has unwanted scroller behavior, so the default meta tag forces IE7 compatibility
67712      */
67713     getDocMarkup : function(){
67714         var h = Ext.fly(this.iframe).getHeight() - this.iframePad * 2;
67715         return String.format('<html><head><style type="text/css">body{border: 0; margin: 0; padding: {0}px; height: {1}px; cursor: text}</style></head><body></body></html>', this.iframePad, h);
67716     },
67717
67718     // private
67719     getEditorBody : function(){
67720         var doc = this.getDoc();
67721         return doc.body || doc.documentElement;
67722     },
67723
67724     // private
67725     getDoc : function(){
67726         return Ext.isIE ? this.getWin().document : (this.iframe.contentDocument || this.getWin().document);
67727     },
67728
67729     // private
67730     getWin : function(){
67731         return Ext.isIE ? this.iframe.contentWindow : window.frames[this.iframe.name];
67732     },
67733
67734     // private
67735     onRender : function(ct, position){
67736         Ext.form.HtmlEditor.superclass.onRender.call(this, ct, position);
67737         this.el.dom.style.border = '0 none';
67738         this.el.dom.setAttribute('tabIndex', -1);
67739         this.el.addClass('x-hidden');
67740         if(Ext.isIE){ // fix IE 1px bogus margin
67741             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;');
67742         }
67743         this.wrap = this.el.wrap({
67744             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
67745         });
67746
67747         this.createToolbar(this);
67748
67749         this.disableItems(true);
67750
67751         this.tb.doLayout();
67752
67753         this.createIFrame();
67754
67755         if(!this.width){
67756             var sz = this.el.getSize();
67757             this.setSize(sz.width, this.height || sz.height);
67758         }
67759         this.resizeEl = this.positionEl = this.wrap;
67760     },
67761
67762     createIFrame: function(){
67763         var iframe = document.createElement('iframe');
67764         iframe.name = Ext.id();
67765         iframe.frameBorder = '0';
67766         iframe.style.overflow = 'auto';
67767         iframe.src = Ext.SSL_SECURE_URL;
67768
67769         this.wrap.dom.appendChild(iframe);
67770         this.iframe = iframe;
67771
67772         this.monitorTask = Ext.TaskMgr.start({
67773             run: this.checkDesignMode,
67774             scope: this,
67775             interval:100
67776         });
67777     },
67778
67779     initFrame : function(){
67780         Ext.TaskMgr.stop(this.monitorTask);
67781         var doc = this.getDoc();
67782         this.win = this.getWin();
67783
67784         doc.open();
67785         doc.write(this.getDocMarkup());
67786         doc.close();
67787
67788         var task = { // must defer to wait for browser to be ready
67789             run : function(){
67790                 var doc = this.getDoc();
67791                 if(doc.body || doc.readyState == 'complete'){
67792                     Ext.TaskMgr.stop(task);
67793                     this.setDesignMode(true);
67794                     this.initEditor.defer(10, this);
67795                 }
67796             },
67797             interval : 10,
67798             duration:10000,
67799             scope: this
67800         };
67801         Ext.TaskMgr.start(task);
67802     },
67803
67804
67805     checkDesignMode : function(){
67806         if(this.wrap && this.wrap.dom.offsetWidth){
67807             var doc = this.getDoc();
67808             if(!doc){
67809                 return;
67810             }
67811             if(!doc.editorInitialized || this.getDesignMode() != 'on'){
67812                 this.initFrame();
67813             }
67814         }
67815     },
67816
67817     /* private
67818      * set current design mode. To enable, mode can be true or 'on', off otherwise
67819      */
67820     setDesignMode : function(mode){
67821         var doc = this.getDoc();
67822         if (doc) {
67823             if(this.readOnly){
67824                 mode = false;
67825             }
67826             doc.designMode = (/on|true/i).test(String(mode).toLowerCase()) ?'on':'off';
67827         }
67828
67829     },
67830
67831     // private
67832     getDesignMode : function(){
67833         var doc = this.getDoc();
67834         if(!doc){ return ''; }
67835         return String(doc.designMode).toLowerCase();
67836
67837     },
67838
67839     disableItems: function(disabled){
67840         if(this.fontSelect){
67841             this.fontSelect.dom.disabled = disabled;
67842         }
67843         this.tb.items.each(function(item){
67844             if(item.getItemId() != 'sourceedit'){
67845                 item.setDisabled(disabled);
67846             }
67847         });
67848     },
67849
67850     // private
67851     onResize : function(w, h){
67852         Ext.form.HtmlEditor.superclass.onResize.apply(this, arguments);
67853         if(this.el && this.iframe){
67854             if(Ext.isNumber(w)){
67855                 var aw = w - this.wrap.getFrameWidth('lr');
67856                 this.el.setWidth(aw);
67857                 this.tb.setWidth(aw);
67858                 this.iframe.style.width = Math.max(aw, 0) + 'px';
67859             }
67860             if(Ext.isNumber(h)){
67861                 var ah = h - this.wrap.getFrameWidth('tb') - this.tb.el.getHeight();
67862                 this.el.setHeight(ah);
67863                 this.iframe.style.height = Math.max(ah, 0) + 'px';
67864                 var bd = this.getEditorBody();
67865                 if(bd){
67866                     bd.style.height = Math.max((ah - (this.iframePad*2)), 0) + 'px';
67867                 }
67868             }
67869         }
67870     },
67871
67872     /**
67873      * Toggles the editor between standard and source edit mode.
67874      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
67875      */
67876     toggleSourceEdit : function(sourceEditMode){
67877         var iframeHeight,
67878             elHeight;
67879
67880         if (sourceEditMode === undefined) {
67881             sourceEditMode = !this.sourceEditMode;
67882         }
67883         this.sourceEditMode = sourceEditMode === true;
67884         var btn = this.tb.getComponent('sourceedit');
67885
67886         if (btn.pressed !== this.sourceEditMode) {
67887             btn.toggle(this.sourceEditMode);
67888             if (!btn.xtbHidden) {
67889                 return;
67890             }
67891         }
67892         if (this.sourceEditMode) {
67893             // grab the height of the containing panel before we hide the iframe
67894             this.previousSize = this.getSize();
67895
67896             iframeHeight = Ext.get(this.iframe).getHeight();
67897
67898             this.disableItems(true);
67899             this.syncValue();
67900             this.iframe.className = 'x-hidden';
67901             this.el.removeClass('x-hidden');
67902             this.el.dom.removeAttribute('tabIndex');
67903             this.el.focus();
67904             this.el.dom.style.height = iframeHeight + 'px';
67905         }
67906         else {
67907             elHeight = parseInt(this.el.dom.style.height, 10);
67908             if (this.initialized) {
67909                 this.disableItems(this.readOnly);
67910             }
67911             this.pushValue();
67912             this.iframe.className = '';
67913             this.el.addClass('x-hidden');
67914             this.el.dom.setAttribute('tabIndex', -1);
67915             this.deferFocus();
67916
67917             this.setSize(this.previousSize);
67918             delete this.previousSize;
67919             this.iframe.style.height = elHeight + 'px';
67920         }
67921         this.fireEvent('editmodechange', this, this.sourceEditMode);
67922     },
67923
67924     // private used internally
67925     createLink : function() {
67926         var url = prompt(this.createLinkText, this.defaultLinkValue);
67927         if(url && url != 'http:/'+'/'){
67928             this.relayCmd('createlink', url);
67929         }
67930     },
67931
67932     // private
67933     initEvents : function(){
67934         this.originalValue = this.getValue();
67935     },
67936
67937     /**
67938      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
67939      * @method
67940      */
67941     markInvalid : Ext.emptyFn,
67942
67943     /**
67944      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
67945      * @method
67946      */
67947     clearInvalid : Ext.emptyFn,
67948
67949     // docs inherit from Field
67950     setValue : function(v){
67951         Ext.form.HtmlEditor.superclass.setValue.call(this, v);
67952         this.pushValue();
67953         return this;
67954     },
67955
67956     /**
67957      * Protected method that will not generally be called directly. If you need/want
67958      * custom HTML cleanup, this is the method you should override.
67959      * @param {String} html The HTML to be cleaned
67960      * @return {String} The cleaned HTML
67961      */
67962     cleanHtml: function(html) {
67963         html = String(html);
67964         if(Ext.isWebKit){ // strip safari nonsense
67965             html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
67966         }
67967
67968         /*
67969          * Neat little hack. Strips out all the non-digit characters from the default
67970          * value and compares it to the character code of the first character in the string
67971          * because it can cause encoding issues when posted to the server.
67972          */
67973         if(html.charCodeAt(0) == this.defaultValue.replace(/\D/g, '')){
67974             html = html.substring(1);
67975         }
67976         return html;
67977     },
67978
67979     /**
67980      * Protected method that will not generally be called directly. Syncs the contents
67981      * of the editor iframe with the textarea.
67982      */
67983     syncValue : function(){
67984         if(this.initialized){
67985             var bd = this.getEditorBody();
67986             var html = bd.innerHTML;
67987             if(Ext.isWebKit){
67988                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
67989                 var m = bs.match(/text-align:(.*?);/i);
67990                 if(m && m[1]){
67991                     html = '<div style="'+m[0]+'">' + html + '</div>';
67992                 }
67993             }
67994             html = this.cleanHtml(html);
67995             if(this.fireEvent('beforesync', this, html) !== false){
67996                 this.el.dom.value = html;
67997                 this.fireEvent('sync', this, html);
67998             }
67999         }
68000     },
68001
68002     //docs inherit from Field
68003     getValue : function() {
68004         this[this.sourceEditMode ? 'pushValue' : 'syncValue']();
68005         return Ext.form.HtmlEditor.superclass.getValue.call(this);
68006     },
68007
68008     /**
68009      * Protected method that will not generally be called directly. Pushes the value of the textarea
68010      * into the iframe editor.
68011      */
68012     pushValue : function(){
68013         if(this.initialized){
68014             var v = this.el.dom.value;
68015             if(!this.activated && v.length < 1){
68016                 v = this.defaultValue;
68017             }
68018             if(this.fireEvent('beforepush', this, v) !== false){
68019                 this.getEditorBody().innerHTML = v;
68020                 if(Ext.isGecko){
68021                     // Gecko hack, see: https://bugzilla.mozilla.org/show_bug.cgi?id=232791#c8
68022                     this.setDesignMode(false);  //toggle off first
68023                     this.setDesignMode(true);
68024                 }
68025                 this.fireEvent('push', this, v);
68026             }
68027
68028         }
68029     },
68030
68031     // private
68032     deferFocus : function(){
68033         this.focus.defer(10, this);
68034     },
68035
68036     // docs inherit from Field
68037     focus : function(){
68038         if(this.win && !this.sourceEditMode){
68039             this.win.focus();
68040         }else{
68041             this.el.focus();
68042         }
68043     },
68044
68045     // private
68046     initEditor : function(){
68047         //Destroying the component during/before initEditor can cause issues.
68048         try{
68049             var dbody = this.getEditorBody(),
68050                 ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat', 'background-color', 'color'),
68051                 doc,
68052                 fn;
68053
68054             ss['background-attachment'] = 'fixed'; // w3c
68055             dbody.bgProperties = 'fixed'; // ie
68056
68057             Ext.DomHelper.applyStyles(dbody, ss);
68058
68059             doc = this.getDoc();
68060
68061             if(doc){
68062                 try{
68063                     Ext.EventManager.removeAll(doc);
68064                 }catch(e){}
68065             }
68066
68067             /*
68068              * We need to use createDelegate here, because when using buffer, the delayed task is added
68069              * as a property to the function. When the listener is removed, the task is deleted from the function.
68070              * Since onEditorEvent is shared on the prototype, if we have multiple html editors, the first time one of the editors
68071              * is destroyed, it causes the fn to be deleted from the prototype, which causes errors. Essentially, we're just anonymizing the function.
68072              */
68073             fn = this.onEditorEvent.createDelegate(this);
68074             Ext.EventManager.on(doc, {
68075                 mousedown: fn,
68076                 dblclick: fn,
68077                 click: fn,
68078                 keyup: fn,
68079                 buffer:100
68080             });
68081
68082             if(Ext.isGecko){
68083                 Ext.EventManager.on(doc, 'keypress', this.applyCommand, this);
68084             }
68085             if(Ext.isIE || Ext.isWebKit || Ext.isOpera){
68086                 Ext.EventManager.on(doc, 'keydown', this.fixKeys, this);
68087             }
68088             doc.editorInitialized = true;
68089             this.initialized = true;
68090             this.pushValue();
68091             this.setReadOnly(this.readOnly);
68092             this.fireEvent('initialize', this);
68093         }catch(e){}
68094     },
68095
68096     // private
68097     beforeDestroy : function(){
68098         if(this.monitorTask){
68099             Ext.TaskMgr.stop(this.monitorTask);
68100         }
68101         if(this.rendered){
68102             Ext.destroy(this.tb);
68103             var doc = this.getDoc();
68104             if(doc){
68105                 try{
68106                     Ext.EventManager.removeAll(doc);
68107                     for (var prop in doc){
68108                         delete doc[prop];
68109                     }
68110                 }catch(e){}
68111             }
68112             if(this.wrap){
68113                 this.wrap.dom.innerHTML = '';
68114                 this.wrap.remove();
68115             }
68116         }
68117         Ext.form.HtmlEditor.superclass.beforeDestroy.call(this);
68118     },
68119
68120     // private
68121     onFirstFocus : function(){
68122         this.activated = true;
68123         this.disableItems(this.readOnly);
68124         if(Ext.isGecko){ // prevent silly gecko errors
68125             this.win.focus();
68126             var s = this.win.getSelection();
68127             if(!s.focusNode || s.focusNode.nodeType != 3){
68128                 var r = s.getRangeAt(0);
68129                 r.selectNodeContents(this.getEditorBody());
68130                 r.collapse(true);
68131                 this.deferFocus();
68132             }
68133             try{
68134                 this.execCmd('useCSS', true);
68135                 this.execCmd('styleWithCSS', false);
68136             }catch(e){}
68137         }
68138         this.fireEvent('activate', this);
68139     },
68140
68141     // private
68142     adjustFont: function(btn){
68143         var adjust = btn.getItemId() == 'increasefontsize' ? 1 : -1,
68144             doc = this.getDoc(),
68145             v = parseInt(doc.queryCommandValue('FontSize') || 2, 10);
68146         if((Ext.isSafari && !Ext.isSafari2) || Ext.isChrome || Ext.isAir){
68147             // Safari 3 values
68148             // 1 = 10px, 2 = 13px, 3 = 16px, 4 = 18px, 5 = 24px, 6 = 32px
68149             if(v <= 10){
68150                 v = 1 + adjust;
68151             }else if(v <= 13){
68152                 v = 2 + adjust;
68153             }else if(v <= 16){
68154                 v = 3 + adjust;
68155             }else if(v <= 18){
68156                 v = 4 + adjust;
68157             }else if(v <= 24){
68158                 v = 5 + adjust;
68159             }else {
68160                 v = 6 + adjust;
68161             }
68162             v = v.constrain(1, 6);
68163         }else{
68164             if(Ext.isSafari){ // safari
68165                 adjust *= 2;
68166             }
68167             v = Math.max(1, v+adjust) + (Ext.isSafari ? 'px' : 0);
68168         }
68169         this.execCmd('FontSize', v);
68170     },
68171
68172     // private
68173     onEditorEvent : function(e){
68174         this.updateToolbar();
68175     },
68176
68177
68178     /**
68179      * Protected method that will not generally be called directly. It triggers
68180      * a toolbar update by reading the markup state of the current selection in the editor.
68181      */
68182     updateToolbar: function(){
68183
68184         if(this.readOnly){
68185             return;
68186         }
68187
68188         if(!this.activated){
68189             this.onFirstFocus();
68190             return;
68191         }
68192
68193         var btns = this.tb.items.map,
68194             doc = this.getDoc();
68195
68196         if(this.enableFont && !Ext.isSafari2){
68197             var name = (doc.queryCommandValue('FontName')||this.defaultFont).toLowerCase();
68198             if(name != this.fontSelect.dom.value){
68199                 this.fontSelect.dom.value = name;
68200             }
68201         }
68202         if(this.enableFormat){
68203             btns.bold.toggle(doc.queryCommandState('bold'));
68204             btns.italic.toggle(doc.queryCommandState('italic'));
68205             btns.underline.toggle(doc.queryCommandState('underline'));
68206         }
68207         if(this.enableAlignments){
68208             btns.justifyleft.toggle(doc.queryCommandState('justifyleft'));
68209             btns.justifycenter.toggle(doc.queryCommandState('justifycenter'));
68210             btns.justifyright.toggle(doc.queryCommandState('justifyright'));
68211         }
68212         if(!Ext.isSafari2 && this.enableLists){
68213             btns.insertorderedlist.toggle(doc.queryCommandState('insertorderedlist'));
68214             btns.insertunorderedlist.toggle(doc.queryCommandState('insertunorderedlist'));
68215         }
68216
68217         Ext.menu.MenuMgr.hideAll();
68218
68219         this.syncValue();
68220     },
68221
68222     // private
68223     relayBtnCmd : function(btn){
68224         this.relayCmd(btn.getItemId());
68225     },
68226
68227     /**
68228      * Executes a Midas editor command on the editor document and performs necessary focus and
68229      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
68230      * @param {String} cmd The Midas command
68231      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
68232      */
68233     relayCmd : function(cmd, value){
68234         (function(){
68235             this.focus();
68236             this.execCmd(cmd, value);
68237             this.updateToolbar();
68238         }).defer(10, this);
68239     },
68240
68241     /**
68242      * Executes a Midas editor command directly on the editor document.
68243      * For visual commands, you should use {@link #relayCmd} instead.
68244      * <b>This should only be called after the editor is initialized.</b>
68245      * @param {String} cmd The Midas command
68246      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
68247      */
68248     execCmd : function(cmd, value){
68249         var doc = this.getDoc();
68250         doc.execCommand(cmd, false, value === undefined ? null : value);
68251         this.syncValue();
68252     },
68253
68254     // private
68255     applyCommand : function(e){
68256         if(e.ctrlKey){
68257             var c = e.getCharCode(), cmd;
68258             if(c > 0){
68259                 c = String.fromCharCode(c);
68260                 switch(c){
68261                     case 'b':
68262                         cmd = 'bold';
68263                     break;
68264                     case 'i':
68265                         cmd = 'italic';
68266                     break;
68267                     case 'u':
68268                         cmd = 'underline';
68269                     break;
68270                 }
68271                 if(cmd){
68272                     this.win.focus();
68273                     this.execCmd(cmd);
68274                     this.deferFocus();
68275                     e.preventDefault();
68276                 }
68277             }
68278         }
68279     },
68280
68281     /**
68282      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
68283      * to insert text.
68284      * @param {String} text
68285      */
68286     insertAtCursor : function(text){
68287         if(!this.activated){
68288             return;
68289         }
68290         if(Ext.isIE){
68291             this.win.focus();
68292             var doc = this.getDoc(),
68293                 r = doc.selection.createRange();
68294             if(r){
68295                 r.pasteHTML(text);
68296                 this.syncValue();
68297                 this.deferFocus();
68298             }
68299         }else{
68300             this.win.focus();
68301             this.execCmd('InsertHTML', text);
68302             this.deferFocus();
68303         }
68304     },
68305
68306     // private
68307     fixKeys : function(){ // load time branching for fastest keydown performance
68308         if(Ext.isIE){
68309             return function(e){
68310                 var k = e.getKey(),
68311                     doc = this.getDoc(),
68312                         r;
68313                 if(k == e.TAB){
68314                     e.stopEvent();
68315                     r = doc.selection.createRange();
68316                     if(r){
68317                         r.collapse(true);
68318                         r.pasteHTML('&nbsp;&nbsp;&nbsp;&nbsp;');
68319                         this.deferFocus();
68320                     }
68321                 }else if(k == e.ENTER){
68322                     r = doc.selection.createRange();
68323                     if(r){
68324                         var target = r.parentElement();
68325                         if(!target || target.tagName.toLowerCase() != 'li'){
68326                             e.stopEvent();
68327                             r.pasteHTML('<br />');
68328                             r.collapse(false);
68329                             r.select();
68330                         }
68331                     }
68332                 }
68333             };
68334         }else if(Ext.isOpera){
68335             return function(e){
68336                 var k = e.getKey();
68337                 if(k == e.TAB){
68338                     e.stopEvent();
68339                     this.win.focus();
68340                     this.execCmd('InsertHTML','&nbsp;&nbsp;&nbsp;&nbsp;');
68341                     this.deferFocus();
68342                 }
68343             };
68344         }else if(Ext.isWebKit){
68345             return function(e){
68346                 var k = e.getKey();
68347                 if(k == e.TAB){
68348                     e.stopEvent();
68349                     this.execCmd('InsertText','\t');
68350                     this.deferFocus();
68351                 }else if(k == e.ENTER){
68352                     e.stopEvent();
68353                     this.execCmd('InsertHtml','<br /><br />');
68354                     this.deferFocus();
68355                 }
68356              };
68357         }
68358     }(),
68359
68360     /**
68361      * Returns the editor's toolbar. <b>This is only available after the editor has been rendered.</b>
68362      * @return {Ext.Toolbar}
68363      */
68364     getToolbar : function(){
68365         return this.tb;
68366     },
68367
68368     /**
68369      * Object collection of toolbar tooltips for the buttons in the editor. The key
68370      * is the command id associated with that button and the value is a valid QuickTips object.
68371      * For example:
68372 <pre><code>
68373 {
68374     bold : {
68375         title: 'Bold (Ctrl+B)',
68376         text: 'Make the selected text bold.',
68377         cls: 'x-html-editor-tip'
68378     },
68379     italic : {
68380         title: 'Italic (Ctrl+I)',
68381         text: 'Make the selected text italic.',
68382         cls: 'x-html-editor-tip'
68383     },
68384     ...
68385 </code></pre>
68386     * @type Object
68387      */
68388     buttonTips : {
68389         bold : {
68390             title: 'Bold (Ctrl+B)',
68391             text: 'Make the selected text bold.',
68392             cls: 'x-html-editor-tip'
68393         },
68394         italic : {
68395             title: 'Italic (Ctrl+I)',
68396             text: 'Make the selected text italic.',
68397             cls: 'x-html-editor-tip'
68398         },
68399         underline : {
68400             title: 'Underline (Ctrl+U)',
68401             text: 'Underline the selected text.',
68402             cls: 'x-html-editor-tip'
68403         },
68404         increasefontsize : {
68405             title: 'Grow Text',
68406             text: 'Increase the font size.',
68407             cls: 'x-html-editor-tip'
68408         },
68409         decreasefontsize : {
68410             title: 'Shrink Text',
68411             text: 'Decrease the font size.',
68412             cls: 'x-html-editor-tip'
68413         },
68414         backcolor : {
68415             title: 'Text Highlight Color',
68416             text: 'Change the background color of the selected text.',
68417             cls: 'x-html-editor-tip'
68418         },
68419         forecolor : {
68420             title: 'Font Color',
68421             text: 'Change the color of the selected text.',
68422             cls: 'x-html-editor-tip'
68423         },
68424         justifyleft : {
68425             title: 'Align Text Left',
68426             text: 'Align text to the left.',
68427             cls: 'x-html-editor-tip'
68428         },
68429         justifycenter : {
68430             title: 'Center Text',
68431             text: 'Center text in the editor.',
68432             cls: 'x-html-editor-tip'
68433         },
68434         justifyright : {
68435             title: 'Align Text Right',
68436             text: 'Align text to the right.',
68437             cls: 'x-html-editor-tip'
68438         },
68439         insertunorderedlist : {
68440             title: 'Bullet List',
68441             text: 'Start a bulleted list.',
68442             cls: 'x-html-editor-tip'
68443         },
68444         insertorderedlist : {
68445             title: 'Numbered List',
68446             text: 'Start a numbered list.',
68447             cls: 'x-html-editor-tip'
68448         },
68449         createlink : {
68450             title: 'Hyperlink',
68451             text: 'Make the selected text a hyperlink.',
68452             cls: 'x-html-editor-tip'
68453         },
68454         sourceedit : {
68455             title: 'Source Edit',
68456             text: 'Switch to source editing mode.',
68457             cls: 'x-html-editor-tip'
68458         }
68459     }
68460
68461     // hide stuff that is not compatible
68462     /**
68463      * @event blur
68464      * @hide
68465      */
68466     /**
68467      * @event change
68468      * @hide
68469      */
68470     /**
68471      * @event focus
68472      * @hide
68473      */
68474     /**
68475      * @event specialkey
68476      * @hide
68477      */
68478     /**
68479      * @cfg {String} fieldClass @hide
68480      */
68481     /**
68482      * @cfg {String} focusClass @hide
68483      */
68484     /**
68485      * @cfg {String} autoCreate @hide
68486      */
68487     /**
68488      * @cfg {String} inputType @hide
68489      */
68490     /**
68491      * @cfg {String} invalidClass @hide
68492      */
68493     /**
68494      * @cfg {String} invalidText @hide
68495      */
68496     /**
68497      * @cfg {String} msgFx @hide
68498      */
68499     /**
68500      * @cfg {String} validateOnBlur @hide
68501      */
68502     /**
68503      * @cfg {Boolean} allowDomMove  @hide
68504      */
68505     /**
68506      * @cfg {String} applyTo @hide
68507      */
68508     /**
68509      * @cfg {String} autoHeight  @hide
68510      */
68511     /**
68512      * @cfg {String} autoWidth  @hide
68513      */
68514     /**
68515      * @cfg {String} cls  @hide
68516      */
68517     /**
68518      * @cfg {String} disabled  @hide
68519      */
68520     /**
68521      * @cfg {String} disabledClass  @hide
68522      */
68523     /**
68524      * @cfg {String} msgTarget  @hide
68525      */
68526     /**
68527      * @cfg {String} readOnly  @hide
68528      */
68529     /**
68530      * @cfg {String} style  @hide
68531      */
68532     /**
68533      * @cfg {String} validationDelay  @hide
68534      */
68535     /**
68536      * @cfg {String} validationEvent  @hide
68537      */
68538     /**
68539      * @cfg {String} tabIndex  @hide
68540      */
68541     /**
68542      * @property disabled
68543      * @hide
68544      */
68545     /**
68546      * @method applyToMarkup
68547      * @hide
68548      */
68549     /**
68550      * @method disable
68551      * @hide
68552      */
68553     /**
68554      * @method enable
68555      * @hide
68556      */
68557     /**
68558      * @method validate
68559      * @hide
68560      */
68561     /**
68562      * @event valid
68563      * @hide
68564      */
68565     /**
68566      * @method setDisabled
68567      * @hide
68568      */
68569     /**
68570      * @cfg keys
68571      * @hide
68572      */
68573 });
68574 Ext.reg('htmleditor', Ext.form.HtmlEditor);
68575 /**
68576  * @class Ext.form.TimeField
68577  * @extends Ext.form.ComboBox
68578  * Provides a time input field with a time dropdown and automatic time validation.  Example usage:
68579  * <pre><code>
68580 new Ext.form.TimeField({
68581     minValue: '9:00 AM',
68582     maxValue: '6:00 PM',
68583     increment: 30
68584 });
68585 </code></pre>
68586  * @constructor
68587  * Create a new TimeField
68588  * @param {Object} config
68589  * @xtype timefield
68590  */
68591 Ext.form.TimeField = Ext.extend(Ext.form.ComboBox, {
68592     /**
68593      * @cfg {Date/String} minValue
68594      * The minimum allowed time. Can be either a Javascript date object with a valid time value or a string
68595      * time in a valid format -- see {@link #format} and {@link #altFormats} (defaults to undefined).
68596      */
68597     minValue : undefined,
68598     /**
68599      * @cfg {Date/String} maxValue
68600      * The maximum allowed time. Can be either a Javascript date object with a valid time value or a string
68601      * time in a valid format -- see {@link #format} and {@link #altFormats} (defaults to undefined).
68602      */
68603     maxValue : undefined,
68604     /**
68605      * @cfg {String} minText
68606      * The error text to display when the date in the cell is before minValue (defaults to
68607      * 'The time in this field must be equal to or after {0}').
68608      */
68609     minText : "The time in this field must be equal to or after {0}",
68610     /**
68611      * @cfg {String} maxText
68612      * The error text to display when the time is after maxValue (defaults to
68613      * 'The time in this field must be equal to or before {0}').
68614      */
68615     maxText : "The time in this field must be equal to or before {0}",
68616     /**
68617      * @cfg {String} invalidText
68618      * The error text to display when the time in the field is invalid (defaults to
68619      * '{value} is not a valid time').
68620      */
68621     invalidText : "{0} is not a valid time",
68622     /**
68623      * @cfg {String} format
68624      * The default time format string which can be overriden for localization support.  The format must be
68625      * valid according to {@link Date#parseDate} (defaults to 'g:i A', e.g., '3:15 PM').  For 24-hour time
68626      * format try 'H:i' instead.
68627      */
68628     format : "g:i A",
68629     /**
68630      * @cfg {String} altFormats
68631      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
68632      * format (defaults to 'g:ia|g:iA|g:i a|g:i A|h:i|g:i|H:i|ga|ha|gA|h a|g a|g A|gi|hi|gia|hia|g|H|gi a|hi a|giA|hiA|gi A|hi A').
68633      */
68634     altFormats : "g:ia|g:iA|g:i a|g:i A|h:i|g:i|H:i|ga|ha|gA|h a|g a|g A|gi|hi|gia|hia|g|H|gi a|hi a|giA|hiA|gi A|hi A",
68635     /**
68636      * @cfg {Number} increment
68637      * The number of minutes between each time value in the list (defaults to 15).
68638      */
68639     increment: 15,
68640
68641     // private override
68642     mode: 'local',
68643     // private override
68644     triggerAction: 'all',
68645     // private override
68646     typeAhead: false,
68647
68648     // private - This is the date to use when generating time values in the absence of either minValue
68649     // or maxValue.  Using the current date causes DST issues on DST boundary dates, so this is an
68650     // arbitrary "safe" date that can be any date aside from DST boundary dates.
68651     initDate: '1/1/2008',
68652
68653     initDateFormat: 'j/n/Y',
68654
68655     // private
68656     initComponent : function(){
68657         if(Ext.isDefined(this.minValue)){
68658             this.setMinValue(this.minValue, true);
68659         }
68660         if(Ext.isDefined(this.maxValue)){
68661             this.setMaxValue(this.maxValue, true);
68662         }
68663         if(!this.store){
68664             this.generateStore(true);
68665         }
68666         Ext.form.TimeField.superclass.initComponent.call(this);
68667     },
68668
68669     /**
68670      * Replaces any existing {@link #minValue} with the new time and refreshes the store.
68671      * @param {Date/String} value The minimum time that can be selected
68672      */
68673     setMinValue: function(value, /* private */ initial){
68674         this.setLimit(value, true, initial);
68675         return this;
68676     },
68677
68678     /**
68679      * Replaces any existing {@link #maxValue} with the new time and refreshes the store.
68680      * @param {Date/String} value The maximum time that can be selected
68681      */
68682     setMaxValue: function(value, /* private */ initial){
68683         this.setLimit(value, false, initial);
68684         return this;
68685     },
68686
68687     // private
68688     generateStore: function(initial){
68689         var min = this.minValue || new Date(this.initDate).clearTime(),
68690             max = this.maxValue || new Date(this.initDate).clearTime().add('mi', (24 * 60) - 1),
68691             times = [];
68692
68693         while(min <= max){
68694             times.push(min.dateFormat(this.format));
68695             min = min.add('mi', this.increment);
68696         }
68697         this.bindStore(times, initial);
68698     },
68699
68700     // private
68701     setLimit: function(value, isMin, initial){
68702         var d;
68703         if(Ext.isString(value)){
68704             d = this.parseDate(value);
68705         }else if(Ext.isDate(value)){
68706             d = value;
68707         }
68708         if(d){
68709             var val = new Date(this.initDate).clearTime();
68710             val.setHours(d.getHours(), d.getMinutes(), d.getSeconds(), d.getMilliseconds());
68711             this[isMin ? 'minValue' : 'maxValue'] = val;
68712             if(!initial){
68713                 this.generateStore();
68714             }
68715         }
68716     },
68717
68718     // inherited docs
68719     getValue : function(){
68720         var v = Ext.form.TimeField.superclass.getValue.call(this);
68721         return this.formatDate(this.parseDate(v)) || '';
68722     },
68723
68724     // inherited docs
68725     setValue : function(value){
68726         return Ext.form.TimeField.superclass.setValue.call(this, this.formatDate(this.parseDate(value)));
68727     },
68728
68729     // private overrides
68730     validateValue : Ext.form.DateField.prototype.validateValue,
68731
68732     formatDate : Ext.form.DateField.prototype.formatDate,
68733
68734     parseDate: function(value) {
68735         if (!value || Ext.isDate(value)) {
68736             return value;
68737         }
68738
68739         var id = this.initDate + ' ',
68740             idf = this.initDateFormat + ' ',
68741             v = Date.parseDate(id + value, idf + this.format), // *** handle DST. note: this.format is a TIME-only format
68742             af = this.altFormats;
68743
68744         if (!v && af) {
68745             if (!this.altFormatsArray) {
68746                 this.altFormatsArray = af.split("|");
68747             }
68748             for (var i = 0, afa = this.altFormatsArray, len = afa.length; i < len && !v; i++) {
68749                 v = Date.parseDate(id + value, idf + afa[i]);
68750             }
68751         }
68752
68753         return v;
68754     }
68755 });
68756 Ext.reg('timefield', Ext.form.TimeField);/**
68757  * @class Ext.form.SliderField
68758  * @extends Ext.form.Field
68759  * Wraps a {@link Ext.slider.MultiSlider Slider} so it can be used as a form field.
68760  * @constructor
68761  * Creates a new SliderField
68762  * @param {Object} config Configuration options. Note that you can pass in any slider configuration options, as well as
68763  * as any field configuration options.
68764  * @xtype sliderfield
68765  */
68766 Ext.form.SliderField = Ext.extend(Ext.form.Field, {
68767     
68768     /**
68769      * @cfg {Boolean} useTips
68770      * True to use an Ext.slider.Tip to display tips for the value. Defaults to <tt>true</tt>.
68771      */
68772     useTips : true,
68773     
68774     /**
68775      * @cfg {Function} tipText
68776      * A function used to display custom text for the slider tip. Defaults to <tt>null</tt>, which will
68777      * use the default on the plugin.
68778      */
68779     tipText : null,
68780     
68781     // private override
68782     actionMode: 'wrap',
68783     
68784     /**
68785      * Initialize the component.
68786      * @private
68787      */
68788     initComponent : function() {
68789         var cfg = Ext.copyTo({
68790             id: this.id + '-slider'
68791         }, this.initialConfig, ['vertical', 'minValue', 'maxValue', 'decimalPrecision', 'keyIncrement', 'increment', 'clickToChange', 'animate']);
68792         
68793         // only can use it if it exists.
68794         if (this.useTips) {
68795             var plug = this.tipText ? {getText: this.tipText} : {};
68796             cfg.plugins = [new Ext.slider.Tip(plug)];
68797         }
68798         this.slider = new Ext.Slider(cfg);
68799         Ext.form.SliderField.superclass.initComponent.call(this);
68800     },    
68801     
68802     /**
68803      * Set up the hidden field
68804      * @param {Object} ct The container to render to.
68805      * @param {Object} position The position in the container to render to.
68806      * @private
68807      */
68808     onRender : function(ct, position){
68809         this.autoCreate = {
68810             id: this.id,
68811             name: this.name,
68812             type: 'hidden',
68813             tag: 'input'    
68814         };
68815         Ext.form.SliderField.superclass.onRender.call(this, ct, position);
68816         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
68817         this.resizeEl = this.positionEl = this.wrap;
68818         this.slider.render(this.wrap);
68819     },
68820     
68821     /**
68822      * Ensure that the slider size is set automatically when the field resizes.
68823      * @param {Object} w The width
68824      * @param {Object} h The height
68825      * @param {Object} aw The adjusted width
68826      * @param {Object} ah The adjusted height
68827      * @private
68828      */
68829     onResize : function(w, h, aw, ah){
68830         Ext.form.SliderField.superclass.onResize.call(this, w, h, aw, ah);
68831         this.slider.setSize(w, h);    
68832     },
68833     
68834     /**
68835      * Initialize any events for this class.
68836      * @private
68837      */
68838     initEvents : function(){
68839         Ext.form.SliderField.superclass.initEvents.call(this);
68840         this.slider.on('change', this.onChange, this);   
68841     },
68842     
68843     /**
68844      * Utility method to set the value of the field when the slider changes.
68845      * @param {Object} slider The slider object.
68846      * @param {Object} v The new value.
68847      * @private
68848      */
68849     onChange : function(slider, v){
68850         this.setValue(v, undefined, true);
68851     },
68852     
68853     /**
68854      * Enable the slider when the field is enabled.
68855      * @private
68856      */
68857     onEnable : function(){
68858         Ext.form.SliderField.superclass.onEnable.call(this);
68859         this.slider.enable();
68860     },
68861     
68862     /**
68863      * Disable the slider when the field is disabled.
68864      * @private
68865      */
68866     onDisable : function(){
68867         Ext.form.SliderField.superclass.onDisable.call(this);
68868         this.slider.disable();    
68869     },
68870     
68871     /**
68872      * Ensure the slider is destroyed when the field is destroyed.
68873      * @private
68874      */
68875     beforeDestroy : function(){
68876         Ext.destroy(this.slider);
68877         Ext.form.SliderField.superclass.beforeDestroy.call(this);
68878     },
68879     
68880     /**
68881      * If a side icon is shown, do alignment to the slider
68882      * @private
68883      */
68884     alignErrorIcon : function(){
68885         this.errorIcon.alignTo(this.slider.el, 'tl-tr', [2, 0]);
68886     },
68887     
68888     /**
68889      * Sets the minimum field value.
68890      * @param {Number} v The new minimum value.
68891      * @return {Ext.form.SliderField} this
68892      */
68893     setMinValue : function(v){
68894         this.slider.setMinValue(v);
68895         return this;    
68896     },
68897     
68898     /**
68899      * Sets the maximum field value.
68900      * @param {Number} v The new maximum value.
68901      * @return {Ext.form.SliderField} this
68902      */
68903     setMaxValue : function(v){
68904         this.slider.setMaxValue(v);
68905         return this;    
68906     },
68907     
68908     /**
68909      * Sets the value for this field.
68910      * @param {Number} v The new value.
68911      * @param {Boolean} animate (optional) Whether to animate the transition. If not specified, it will default to the animate config.
68912      * @return {Ext.form.SliderField} this
68913      */
68914     setValue : function(v, animate, /* private */ silent){
68915         // silent is used if the setValue method is invoked by the slider
68916         // which means we don't need to set the value on the slider.
68917         if(!silent){
68918             this.slider.setValue(v, animate);
68919         }
68920         return Ext.form.SliderField.superclass.setValue.call(this, this.slider.getValue());
68921     },
68922     
68923     /**
68924      * Gets the current value for this field.
68925      * @return {Number} The current value.
68926      */
68927     getValue : function(){
68928         return this.slider.getValue();    
68929     }
68930 });
68931
68932 Ext.reg('sliderfield', Ext.form.SliderField);/**
68933  * @class Ext.form.Label
68934  * @extends Ext.BoxComponent
68935  * Basic Label field.
68936  * @constructor
68937  * Creates a new Label
68938  * @param {Ext.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
68939  * element and its id used as the component id.  If a string is passed, it is assumed to be the id of an existing element
68940  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
68941  * @xtype label
68942  */
68943 Ext.form.Label = Ext.extend(Ext.BoxComponent, {
68944     /**
68945      * @cfg {String} text The plain text to display within the label (defaults to ''). If you need to include HTML
68946      * tags within the label's innerHTML, use the {@link #html} config instead.
68947      */
68948     /**
68949      * @cfg {String} forId The id of the input element to which this label will be bound via the standard HTML 'for'
68950      * attribute. If not specified, the attribute will not be added to the label.
68951      */
68952     /**
68953      * @cfg {String} html An HTML fragment that will be used as the label's innerHTML (defaults to '').
68954      * Note that if {@link #text} is specified it will take precedence and this value will be ignored.
68955      */
68956
68957     // private
68958     onRender : function(ct, position){
68959         if(!this.el){
68960             this.el = document.createElement('label');
68961             this.el.id = this.getId();
68962             this.el.innerHTML = this.text ? Ext.util.Format.htmlEncode(this.text) : (this.html || '');
68963             if(this.forId){
68964                 this.el.setAttribute('for', this.forId);
68965             }
68966         }
68967         Ext.form.Label.superclass.onRender.call(this, ct, position);
68968     },
68969
68970     /**
68971      * Updates the label's innerHTML with the specified string.
68972      * @param {String} text The new label text
68973      * @param {Boolean} encode (optional) False to skip HTML-encoding the text when rendering it
68974      * to the label (defaults to true which encodes the value). This might be useful if you want to include
68975      * tags in the label's innerHTML rather than rendering them as string literals per the default logic.
68976      * @return {Label} this
68977      */
68978     setText : function(t, encode){
68979         var e = encode === false;
68980         this[!e ? 'text' : 'html'] = t;
68981         delete this[e ? 'text' : 'html'];
68982         if(this.rendered){
68983             this.el.dom.innerHTML = encode !== false ? Ext.util.Format.htmlEncode(t) : t;
68984         }
68985         return this;
68986     }
68987 });
68988
68989 Ext.reg('label', Ext.form.Label);/**
68990  * @class Ext.form.Action
68991  * <p>The subclasses of this class provide actions to perform upon {@link Ext.form.BasicForm Form}s.</p>
68992  * <p>Instances of this class are only created by a {@link Ext.form.BasicForm Form} when
68993  * the Form needs to perform an action such as submit or load. The Configuration options
68994  * listed for this class are set through the Form's action methods: {@link Ext.form.BasicForm#submit submit},
68995  * {@link Ext.form.BasicForm#load load} and {@link Ext.form.BasicForm#doAction doAction}</p>
68996  * <p>The instance of Action which performed the action is passed to the success
68997  * and failure callbacks of the Form's action methods ({@link Ext.form.BasicForm#submit submit},
68998  * {@link Ext.form.BasicForm#load load} and {@link Ext.form.BasicForm#doAction doAction}),
68999  * and to the {@link Ext.form.BasicForm#actioncomplete actioncomplete} and
69000  * {@link Ext.form.BasicForm#actionfailed actionfailed} event handlers.</p>
69001  */
69002 Ext.form.Action = function(form, options){
69003     this.form = form;
69004     this.options = options || {};
69005 };
69006
69007 /**
69008  * Failure type returned when client side validation of the Form fails
69009  * thus aborting a submit action. Client side validation is performed unless
69010  * {@link #clientValidation} is explicitly set to <tt>false</tt>.
69011  * @type {String}
69012  * @static
69013  */
69014 Ext.form.Action.CLIENT_INVALID = 'client';
69015 /**
69016  * <p>Failure type returned when server side processing fails and the {@link #result}'s
69017  * <tt style="font-weight:bold">success</tt> property is set to <tt>false</tt>.</p>
69018  * <p>In the case of a form submission, field-specific error messages may be returned in the
69019  * {@link #result}'s <tt style="font-weight:bold">errors</tt> property.</p>
69020  * @type {String}
69021  * @static
69022  */
69023 Ext.form.Action.SERVER_INVALID = 'server';
69024 /**
69025  * Failure type returned when a communication error happens when attempting
69026  * to send a request to the remote server. The {@link #response} may be examined to
69027  * provide further information.
69028  * @type {String}
69029  * @static
69030  */
69031 Ext.form.Action.CONNECT_FAILURE = 'connect';
69032 /**
69033  * Failure type returned when the response's <tt style="font-weight:bold">success</tt>
69034  * property is set to <tt>false</tt>, or no field values are returned in the response's
69035  * <tt style="font-weight:bold">data</tt> property.
69036  * @type {String}
69037  * @static
69038  */
69039 Ext.form.Action.LOAD_FAILURE = 'load';
69040
69041 Ext.form.Action.prototype = {
69042 /**
69043  * @cfg {String} url The URL that the Action is to invoke.
69044  */
69045 /**
69046  * @cfg {Boolean} reset When set to <tt><b>true</b></tt>, causes the Form to be
69047  * {@link Ext.form.BasicForm.reset reset} on Action success. If specified, this happens
69048  * <b>before</b> the {@link #success} callback is called and before the Form's
69049  * {@link Ext.form.BasicForm.actioncomplete actioncomplete} event fires.
69050  */
69051 /**
69052  * @cfg {String} method The HTTP method to use to access the requested URL. Defaults to the
69053  * {@link Ext.form.BasicForm}'s method, or if that is not specified, the underlying DOM form's method.
69054  */
69055 /**
69056  * @cfg {Mixed} params <p>Extra parameter values to pass. These are added to the Form's
69057  * {@link Ext.form.BasicForm#baseParams} and passed to the specified URL along with the Form's
69058  * input fields.</p>
69059  * <p>Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.</p>
69060  */
69061 /**
69062  * @cfg {Number} timeout The number of seconds to wait for a server response before
69063  * failing with the {@link #failureType} as {@link #Action.CONNECT_FAILURE}. If not specified,
69064  * defaults to the configured <tt>{@link Ext.form.BasicForm#timeout timeout}</tt> of the
69065  * {@link Ext.form.BasicForm form}.
69066  */
69067 /**
69068  * @cfg {Function} success The function to call when a valid success return packet is recieved.
69069  * The function is passed the following parameters:<ul class="mdetail-params">
69070  * <li><b>form</b> : Ext.form.BasicForm<div class="sub-desc">The form that requested the action</div></li>
69071  * <li><b>action</b> : Ext.form.Action<div class="sub-desc">The Action class. The {@link #result}
69072  * property of this object may be examined to perform custom postprocessing.</div></li>
69073  * </ul>
69074  */
69075 /**
69076  * @cfg {Function} failure The function to call when a failure packet was recieved, or when an
69077  * error ocurred in the Ajax communication.
69078  * The function is passed the following parameters:<ul class="mdetail-params">
69079  * <li><b>form</b> : Ext.form.BasicForm<div class="sub-desc">The form that requested the action</div></li>
69080  * <li><b>action</b> : Ext.form.Action<div class="sub-desc">The Action class. If an Ajax
69081  * error ocurred, the failure type will be in {@link #failureType}. The {@link #result}
69082  * property of this object may be examined to perform custom postprocessing.</div></li>
69083  * </ul>
69084  */
69085 /**
69086  * @cfg {Object} scope The scope in which to call the callback functions (The <tt>this</tt> reference
69087  * for the callback functions).
69088  */
69089 /**
69090  * @cfg {String} waitMsg The message to be displayed by a call to {@link Ext.MessageBox#wait}
69091  * during the time the action is being processed.
69092  */
69093 /**
69094  * @cfg {String} waitTitle The title to be displayed by a call to {@link Ext.MessageBox#wait}
69095  * during the time the action is being processed.
69096  */
69097
69098 /**
69099  * @cfg {Boolean} submitEmptyText If set to <tt>true</tt>, the emptyText value will be sent with the form
69100  * when it is submitted.  Defaults to <tt>true</tt>.
69101  */
69102
69103 /**
69104  * The type of action this Action instance performs.
69105  * Currently only "submit" and "load" are supported.
69106  * @type {String}
69107  */
69108     type : 'default',
69109 /**
69110  * The type of failure detected will be one of these: {@link #CLIENT_INVALID},
69111  * {@link #SERVER_INVALID}, {@link #CONNECT_FAILURE}, or {@link #LOAD_FAILURE}.  Usage:
69112  * <pre><code>
69113 var fp = new Ext.form.FormPanel({
69114 ...
69115 buttons: [{
69116     text: 'Save',
69117     formBind: true,
69118     handler: function(){
69119         if(fp.getForm().isValid()){
69120             fp.getForm().submit({
69121                 url: 'form-submit.php',
69122                 waitMsg: 'Submitting your data...',
69123                 success: function(form, action){
69124                     // server responded with success = true
69125                     var result = action.{@link #result};
69126                 },
69127                 failure: function(form, action){
69128                     if (action.{@link #failureType} === Ext.form.Action.{@link #CONNECT_FAILURE}) {
69129                         Ext.Msg.alert('Error',
69130                             'Status:'+action.{@link #response}.status+': '+
69131                             action.{@link #response}.statusText);
69132                     }
69133                     if (action.failureType === Ext.form.Action.{@link #SERVER_INVALID}){
69134                         // server responded with success = false
69135                         Ext.Msg.alert('Invalid', action.{@link #result}.errormsg);
69136                     }
69137                 }
69138             });
69139         }
69140     }
69141 },{
69142     text: 'Reset',
69143     handler: function(){
69144         fp.getForm().reset();
69145     }
69146 }]
69147  * </code></pre>
69148  * @property failureType
69149  * @type {String}
69150  */
69151  /**
69152  * The XMLHttpRequest object used to perform the action.
69153  * @property response
69154  * @type {Object}
69155  */
69156  /**
69157  * The decoded response object containing a boolean <tt style="font-weight:bold">success</tt> property and
69158  * other, action-specific properties.
69159  * @property result
69160  * @type {Object}
69161  */
69162
69163     // interface method
69164     run : function(options){
69165
69166     },
69167
69168     // interface method
69169     success : function(response){
69170
69171     },
69172
69173     // interface method
69174     handleResponse : function(response){
69175
69176     },
69177
69178     // default connection failure
69179     failure : function(response){
69180         this.response = response;
69181         this.failureType = Ext.form.Action.CONNECT_FAILURE;
69182         this.form.afterAction(this, false);
69183     },
69184
69185     // private
69186     // shared code among all Actions to validate that there was a response
69187     // with either responseText or responseXml
69188     processResponse : function(response){
69189         this.response = response;
69190         if(!response.responseText && !response.responseXML){
69191             return true;
69192         }
69193         this.result = this.handleResponse(response);
69194         return this.result;
69195     },
69196
69197     // utility functions used internally
69198     getUrl : function(appendParams){
69199         var url = this.options.url || this.form.url || this.form.el.dom.action;
69200         if(appendParams){
69201             var p = this.getParams();
69202             if(p){
69203                 url = Ext.urlAppend(url, p);
69204             }
69205         }
69206         return url;
69207     },
69208
69209     // private
69210     getMethod : function(){
69211         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
69212     },
69213
69214     // private
69215     getParams : function(){
69216         var bp = this.form.baseParams;
69217         var p = this.options.params;
69218         if(p){
69219             if(typeof p == "object"){
69220                 p = Ext.urlEncode(Ext.applyIf(p, bp));
69221             }else if(typeof p == 'string' && bp){
69222                 p += '&' + Ext.urlEncode(bp);
69223             }
69224         }else if(bp){
69225             p = Ext.urlEncode(bp);
69226         }
69227         return p;
69228     },
69229
69230     // private
69231     createCallback : function(opts){
69232         var opts = opts || {};
69233         return {
69234             success: this.success,
69235             failure: this.failure,
69236             scope: this,
69237             timeout: (opts.timeout*1000) || (this.form.timeout*1000),
69238             upload: this.form.fileUpload ? this.success : undefined
69239         };
69240     }
69241 };
69242
69243 /**
69244  * @class Ext.form.Action.Submit
69245  * @extends Ext.form.Action
69246  * <p>A class which handles submission of data from {@link Ext.form.BasicForm Form}s
69247  * and processes the returned response.</p>
69248  * <p>Instances of this class are only created by a {@link Ext.form.BasicForm Form} when
69249  * {@link Ext.form.BasicForm#submit submit}ting.</p>
69250  * <p><u><b>Response Packet Criteria</b></u></p>
69251  * <p>A response packet may contain:
69252  * <div class="mdetail-params"><ul>
69253  * <li><b><code>success</code></b> property : Boolean
69254  * <div class="sub-desc">The <code>success</code> property is required.</div></li>
69255  * <li><b><code>errors</code></b> property : Object
69256  * <div class="sub-desc"><div class="sub-desc">The <code>errors</code> property,
69257  * which is optional, contains error messages for invalid fields.</div></li>
69258  * </ul></div>
69259  * <p><u><b>JSON Packets</b></u></p>
69260  * <p>By default, response packets are assumed to be JSON, so a typical response
69261  * packet may look like this:</p><pre><code>
69262 {
69263     success: false,
69264     errors: {
69265         clientCode: "Client not found",
69266         portOfLoading: "This field must not be null"
69267     }
69268 }</code></pre>
69269  * <p>Other data may be placed into the response for processing by the {@link Ext.form.BasicForm}'s callback
69270  * or event handler methods. The object decoded from this JSON is available in the
69271  * {@link Ext.form.Action#result result} property.</p>
69272  * <p>Alternatively, if an {@link #errorReader} is specified as an {@link Ext.data.XmlReader XmlReader}:</p><pre><code>
69273     errorReader: new Ext.data.XmlReader({
69274             record : 'field',
69275             success: '@success'
69276         }, [
69277             'id', 'msg'
69278         ]
69279     )
69280 </code></pre>
69281  * <p>then the results may be sent back in XML format:</p><pre><code>
69282 &lt;?xml version="1.0" encoding="UTF-8"?&gt;
69283 &lt;message success="false"&gt;
69284 &lt;errors&gt;
69285     &lt;field&gt;
69286         &lt;id&gt;clientCode&lt;/id&gt;
69287         &lt;msg&gt;&lt;![CDATA[Code not found. &lt;br /&gt;&lt;i&gt;This is a test validation message from the server &lt;/i&gt;]]&gt;&lt;/msg&gt;
69288     &lt;/field&gt;
69289     &lt;field&gt;
69290         &lt;id&gt;portOfLoading&lt;/id&gt;
69291         &lt;msg&gt;&lt;![CDATA[Port not found. &lt;br /&gt;&lt;i&gt;This is a test validation message from the server &lt;/i&gt;]]&gt;&lt;/msg&gt;
69292     &lt;/field&gt;
69293 &lt;/errors&gt;
69294 &lt;/message&gt;
69295 </code></pre>
69296  * <p>Other elements may be placed into the response XML for processing by the {@link Ext.form.BasicForm}'s callback
69297  * or event handler methods. The XML document is available in the {@link #errorReader}'s {@link Ext.data.XmlReader#xmlData xmlData} property.</p>
69298  */
69299 Ext.form.Action.Submit = function(form, options){
69300     Ext.form.Action.Submit.superclass.constructor.call(this, form, options);
69301 };
69302
69303 Ext.extend(Ext.form.Action.Submit, Ext.form.Action, {
69304     /**
69305      * @cfg {Ext.data.DataReader} errorReader <p><b>Optional. JSON is interpreted with
69306      * no need for an errorReader.</b></p>
69307      * <p>A Reader which reads a single record from the returned data. The DataReader's
69308      * <b>success</b> property specifies how submission success is determined. The Record's
69309      * data provides the error messages to apply to any invalid form Fields.</p>
69310      */
69311     /**
69312      * @cfg {boolean} clientValidation Determines whether a Form's fields are validated
69313      * in a final call to {@link Ext.form.BasicForm#isValid isValid} prior to submission.
69314      * Pass <tt>false</tt> in the Form's submit options to prevent this. If not defined, pre-submission field validation
69315      * is performed.
69316      */
69317     type : 'submit',
69318
69319     // private
69320     run : function(){
69321         var o = this.options,
69322             method = this.getMethod(),
69323             isGet = method == 'GET';
69324         if(o.clientValidation === false || this.form.isValid()){
69325             if (o.submitEmptyText === false) {
69326                 var fields = this.form.items,
69327                     emptyFields = [],
69328                     setupEmptyFields = function(f){
69329                         if (f.el.getValue() == f.emptyText) {
69330                             emptyFields.push(f);
69331                             f.el.dom.value = "";
69332                         }
69333                         if(f.isComposite && f.rendered){
69334                             f.items.each(setupEmptyFields);
69335                         }
69336                     };
69337                     
69338                 fields.each(setupEmptyFields);
69339             }
69340             Ext.Ajax.request(Ext.apply(this.createCallback(o), {
69341                 form:this.form.el.dom,
69342                 url:this.getUrl(isGet),
69343                 method: method,
69344                 headers: o.headers,
69345                 params:!isGet ? this.getParams() : null,
69346                 isUpload: this.form.fileUpload
69347             }));
69348             if (o.submitEmptyText === false) {
69349                 Ext.each(emptyFields, function(f) {
69350                     if (f.applyEmptyText) {
69351                         f.applyEmptyText();
69352                     }
69353                 });
69354             }
69355         }else if (o.clientValidation !== false){ // client validation failed
69356             this.failureType = Ext.form.Action.CLIENT_INVALID;
69357             this.form.afterAction(this, false);
69358         }
69359     },
69360
69361     // private
69362     success : function(response){
69363         var result = this.processResponse(response);
69364         if(result === true || result.success){
69365             this.form.afterAction(this, true);
69366             return;
69367         }
69368         if(result.errors){
69369             this.form.markInvalid(result.errors);
69370         }
69371         this.failureType = Ext.form.Action.SERVER_INVALID;
69372         this.form.afterAction(this, false);
69373     },
69374
69375     // private
69376     handleResponse : function(response){
69377         if(this.form.errorReader){
69378             var rs = this.form.errorReader.read(response);
69379             var errors = [];
69380             if(rs.records){
69381                 for(var i = 0, len = rs.records.length; i < len; i++) {
69382                     var r = rs.records[i];
69383                     errors[i] = r.data;
69384                 }
69385             }
69386             if(errors.length < 1){
69387                 errors = null;
69388             }
69389             return {
69390                 success : rs.success,
69391                 errors : errors
69392             };
69393         }
69394         return Ext.decode(response.responseText);
69395     }
69396 });
69397
69398
69399 /**
69400  * @class Ext.form.Action.Load
69401  * @extends Ext.form.Action
69402  * <p>A class which handles loading of data from a server into the Fields of an {@link Ext.form.BasicForm}.</p>
69403  * <p>Instances of this class are only created by a {@link Ext.form.BasicForm Form} when
69404  * {@link Ext.form.BasicForm#load load}ing.</p>
69405  * <p><u><b>Response Packet Criteria</b></u></p>
69406  * <p>A response packet <b>must</b> contain:
69407  * <div class="mdetail-params"><ul>
69408  * <li><b><code>success</code></b> property : Boolean</li>
69409  * <li><b><code>data</code></b> property : Object</li>
69410  * <div class="sub-desc">The <code>data</code> property contains the values of Fields to load.
69411  * The individual value object for each Field is passed to the Field's
69412  * {@link Ext.form.Field#setValue setValue} method.</div></li>
69413  * </ul></div>
69414  * <p><u><b>JSON Packets</b></u></p>
69415  * <p>By default, response packets are assumed to be JSON, so for the following form load call:<pre><code>
69416 var myFormPanel = new Ext.form.FormPanel({
69417     title: 'Client and routing info',
69418     items: [{
69419         fieldLabel: 'Client',
69420         name: 'clientName'
69421     }, {
69422         fieldLabel: 'Port of loading',
69423         name: 'portOfLoading'
69424     }, {
69425         fieldLabel: 'Port of discharge',
69426         name: 'portOfDischarge'
69427     }]
69428 });
69429 myFormPanel.{@link Ext.form.FormPanel#getForm getForm}().{@link Ext.form.BasicForm#load load}({
69430     url: '/getRoutingInfo.php',
69431     params: {
69432         consignmentRef: myConsignmentRef
69433     },
69434     failure: function(form, action) {
69435         Ext.Msg.alert("Load failed", action.result.errorMessage);
69436     }
69437 });
69438 </code></pre>
69439  * a <b>success response</b> packet may look like this:</p><pre><code>
69440 {
69441     success: true,
69442     data: {
69443         clientName: "Fred. Olsen Lines",
69444         portOfLoading: "FXT",
69445         portOfDischarge: "OSL"
69446     }
69447 }</code></pre>
69448  * while a <b>failure response</b> packet may look like this:</p><pre><code>
69449 {
69450     success: false,
69451     errorMessage: "Consignment reference not found"
69452 }</code></pre>
69453  * <p>Other data may be placed into the response for processing the {@link Ext.form.BasicForm Form}'s
69454  * callback or event handler methods. The object decoded from this JSON is available in the
69455  * {@link Ext.form.Action#result result} property.</p>
69456  */
69457 Ext.form.Action.Load = function(form, options){
69458     Ext.form.Action.Load.superclass.constructor.call(this, form, options);
69459     this.reader = this.form.reader;
69460 };
69461
69462 Ext.extend(Ext.form.Action.Load, Ext.form.Action, {
69463     // private
69464     type : 'load',
69465
69466     // private
69467     run : function(){
69468         Ext.Ajax.request(Ext.apply(
69469                 this.createCallback(this.options), {
69470                     method:this.getMethod(),
69471                     url:this.getUrl(false),
69472                     headers: this.options.headers,
69473                     params:this.getParams()
69474         }));
69475     },
69476
69477     // private
69478     success : function(response){
69479         var result = this.processResponse(response);
69480         if(result === true || !result.success || !result.data){
69481             this.failureType = Ext.form.Action.LOAD_FAILURE;
69482             this.form.afterAction(this, false);
69483             return;
69484         }
69485         this.form.clearInvalid();
69486         this.form.setValues(result.data);
69487         this.form.afterAction(this, true);
69488     },
69489
69490     // private
69491     handleResponse : function(response){
69492         if(this.form.reader){
69493             var rs = this.form.reader.read(response);
69494             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
69495             return {
69496                 success : rs.success,
69497                 data : data
69498             };
69499         }
69500         return Ext.decode(response.responseText);
69501     }
69502 });
69503
69504
69505
69506 /**
69507  * @class Ext.form.Action.DirectLoad
69508  * @extends Ext.form.Action.Load
69509  * <p>Provides Ext.direct support for loading form data.</p>
69510  * <p>This example illustrates usage of Ext.Direct to <b>load</b> a form through Ext.Direct.</p>
69511  * <pre><code>
69512 var myFormPanel = new Ext.form.FormPanel({
69513     // configs for FormPanel
69514     title: 'Basic Information',
69515     renderTo: document.body,
69516     width: 300, height: 160,
69517     padding: 10,
69518
69519     // configs apply to child items
69520     defaults: {anchor: '100%'},
69521     defaultType: 'textfield',
69522     items: [{
69523         fieldLabel: 'Name',
69524         name: 'name'
69525     },{
69526         fieldLabel: 'Email',
69527         name: 'email'
69528     },{
69529         fieldLabel: 'Company',
69530         name: 'company'
69531     }],
69532
69533     // configs for BasicForm
69534     api: {
69535         // The server-side method to call for load() requests
69536         load: Profile.getBasicInfo,
69537         // The server-side must mark the submit handler as a 'formHandler'
69538         submit: Profile.updateBasicInfo
69539     },
69540     // specify the order for the passed params
69541     paramOrder: ['uid', 'foo']
69542 });
69543
69544 // load the form
69545 myFormPanel.getForm().load({
69546     // pass 2 arguments to server side getBasicInfo method (len=2)
69547     params: {
69548         foo: 'bar',
69549         uid: 34
69550     }
69551 });
69552  * </code></pre>
69553  * The data packet sent to the server will resemble something like:
69554  * <pre><code>
69555 [
69556     {
69557         "action":"Profile","method":"getBasicInfo","type":"rpc","tid":2,
69558         "data":[34,"bar"] // note the order of the params
69559     }
69560 ]
69561  * </code></pre>
69562  * The form will process a data packet returned by the server that is similar
69563  * to the following format:
69564  * <pre><code>
69565 [
69566     {
69567         "action":"Profile","method":"getBasicInfo","type":"rpc","tid":2,
69568         "result":{
69569             "success":true,
69570             "data":{
69571                 "name":"Fred Flintstone",
69572                 "company":"Slate Rock and Gravel",
69573                 "email":"fred.flintstone@slaterg.com"
69574             }
69575         }
69576     }
69577 ]
69578  * </code></pre>
69579  */
69580 Ext.form.Action.DirectLoad = Ext.extend(Ext.form.Action.Load, {
69581     constructor: function(form, opts) {
69582         Ext.form.Action.DirectLoad.superclass.constructor.call(this, form, opts);
69583     },
69584     type : 'directload',
69585
69586     run : function(){
69587         var args = this.getParams();
69588         args.push(this.success, this);
69589         this.form.api.load.apply(window, args);
69590     },
69591
69592     getParams : function() {
69593         var buf = [], o = {};
69594         var bp = this.form.baseParams;
69595         var p = this.options.params;
69596         Ext.apply(o, p, bp);
69597         var paramOrder = this.form.paramOrder;
69598         if(paramOrder){
69599             for(var i = 0, len = paramOrder.length; i < len; i++){
69600                 buf.push(o[paramOrder[i]]);
69601             }
69602         }else if(this.form.paramsAsHash){
69603             buf.push(o);
69604         }
69605         return buf;
69606     },
69607     // Direct actions have already been processed and therefore
69608     // we can directly set the result; Direct Actions do not have
69609     // a this.response property.
69610     processResponse : function(result) {
69611         this.result = result;
69612         return result;
69613     },
69614
69615     success : function(response, trans){
69616         if(trans.type == Ext.Direct.exceptions.SERVER){
69617             response = {};
69618         }
69619         Ext.form.Action.DirectLoad.superclass.success.call(this, response);
69620     }
69621 });
69622
69623 /**
69624  * @class Ext.form.Action.DirectSubmit
69625  * @extends Ext.form.Action.Submit
69626  * <p>Provides Ext.direct support for submitting form data.</p>
69627  * <p>This example illustrates usage of Ext.Direct to <b>submit</b> a form through Ext.Direct.</p>
69628  * <pre><code>
69629 var myFormPanel = new Ext.form.FormPanel({
69630     // configs for FormPanel
69631     title: 'Basic Information',
69632     renderTo: document.body,
69633     width: 300, height: 160,
69634     padding: 10,
69635     buttons:[{
69636         text: 'Submit',
69637         handler: function(){
69638             myFormPanel.getForm().submit({
69639                 params: {
69640                     foo: 'bar',
69641                     uid: 34
69642                 }
69643             });
69644         }
69645     }],
69646
69647     // configs apply to child items
69648     defaults: {anchor: '100%'},
69649     defaultType: 'textfield',
69650     items: [{
69651         fieldLabel: 'Name',
69652         name: 'name'
69653     },{
69654         fieldLabel: 'Email',
69655         name: 'email'
69656     },{
69657         fieldLabel: 'Company',
69658         name: 'company'
69659     }],
69660
69661     // configs for BasicForm
69662     api: {
69663         // The server-side method to call for load() requests
69664         load: Profile.getBasicInfo,
69665         // The server-side must mark the submit handler as a 'formHandler'
69666         submit: Profile.updateBasicInfo
69667     },
69668     // specify the order for the passed params
69669     paramOrder: ['uid', 'foo']
69670 });
69671  * </code></pre>
69672  * The data packet sent to the server will resemble something like:
69673  * <pre><code>
69674 {
69675     "action":"Profile","method":"updateBasicInfo","type":"rpc","tid":"6",
69676     "result":{
69677         "success":true,
69678         "id":{
69679             "extAction":"Profile","extMethod":"updateBasicInfo",
69680             "extType":"rpc","extTID":"6","extUpload":"false",
69681             "name":"Aaron Conran","email":"aaron@extjs.com","company":"Ext JS, LLC"
69682         }
69683     }
69684 }
69685  * </code></pre>
69686  * The form will process a data packet returned by the server that is similar
69687  * to the following:
69688  * <pre><code>
69689 // sample success packet (batched requests)
69690 [
69691     {
69692         "action":"Profile","method":"updateBasicInfo","type":"rpc","tid":3,
69693         "result":{
69694             "success":true
69695         }
69696     }
69697 ]
69698
69699 // sample failure packet (one request)
69700 {
69701         "action":"Profile","method":"updateBasicInfo","type":"rpc","tid":"6",
69702         "result":{
69703             "errors":{
69704                 "email":"already taken"
69705             },
69706             "success":false,
69707             "foo":"bar"
69708         }
69709 }
69710  * </code></pre>
69711  * Also see the discussion in {@link Ext.form.Action.DirectLoad}.
69712  */
69713 Ext.form.Action.DirectSubmit = Ext.extend(Ext.form.Action.Submit, {
69714     constructor : function(form, opts) {
69715         Ext.form.Action.DirectSubmit.superclass.constructor.call(this, form, opts);
69716     },
69717     type : 'directsubmit',
69718     // override of Submit
69719     run : function(){
69720         var o = this.options;
69721         if(o.clientValidation === false || this.form.isValid()){
69722             // tag on any additional params to be posted in the
69723             // form scope
69724             this.success.params = this.getParams();
69725             this.form.api.submit(this.form.el.dom, this.success, this);
69726         }else if (o.clientValidation !== false){ // client validation failed
69727             this.failureType = Ext.form.Action.CLIENT_INVALID;
69728             this.form.afterAction(this, false);
69729         }
69730     },
69731
69732     getParams : function() {
69733         var o = {};
69734         var bp = this.form.baseParams;
69735         var p = this.options.params;
69736         Ext.apply(o, p, bp);
69737         return o;
69738     },
69739     // Direct actions have already been processed and therefore
69740     // we can directly set the result; Direct Actions do not have
69741     // a this.response property.
69742     processResponse : function(result) {
69743         this.result = result;
69744         return result;
69745     },
69746
69747     success : function(response, trans){
69748         if(trans.type == Ext.Direct.exceptions.SERVER){
69749             response = {};
69750         }
69751         Ext.form.Action.DirectSubmit.superclass.success.call(this, response);
69752     }
69753 });
69754
69755 Ext.form.Action.ACTION_TYPES = {
69756     'load' : Ext.form.Action.Load,
69757     'submit' : Ext.form.Action.Submit,
69758     'directload' : Ext.form.Action.DirectLoad,
69759     'directsubmit' : Ext.form.Action.DirectSubmit
69760 };
69761 /**
69762  * @class Ext.form.VTypes
69763  * <p>This is a singleton object which contains a set of commonly used field validation functions.
69764  * The validations provided are basic and intended to be easily customizable and extended.</p>
69765  * <p>To add custom VTypes specify the <code>{@link Ext.form.TextField#vtype vtype}</code> validation
69766  * test function, and optionally specify any corresponding error text to display and any keystroke
69767  * filtering mask to apply. For example:</p>
69768  * <pre><code>
69769 // custom Vtype for vtype:'time'
69770 var timeTest = /^([1-9]|1[0-9]):([0-5][0-9])(\s[a|p]m)$/i;
69771 Ext.apply(Ext.form.VTypes, {
69772     //  vtype validation function
69773     time: function(val, field) {
69774         return timeTest.test(val);
69775     },
69776     // vtype Text property: The error text to display when the validation function returns false
69777     timeText: 'Not a valid time.  Must be in the format "12:34 PM".',
69778     // vtype Mask property: The keystroke filter mask
69779     timeMask: /[\d\s:amp]/i
69780 });
69781  * </code></pre>
69782  * Another example:
69783  * <pre><code>
69784 // custom Vtype for vtype:'IPAddress'
69785 Ext.apply(Ext.form.VTypes, {
69786     IPAddress:  function(v) {
69787         return /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(v);
69788     },
69789     IPAddressText: 'Must be a numeric IP address',
69790     IPAddressMask: /[\d\.]/i
69791 });
69792  * </code></pre>
69793  * @singleton
69794  */
69795 Ext.form.VTypes = function(){
69796     // closure these in so they are only created once.
69797     var alpha = /^[a-zA-Z_]+$/,
69798         alphanum = /^[a-zA-Z0-9_]+$/,
69799         email = /^(\w+)([\-+.][\w]+)*@(\w[\-\w]*\.){1,5}([A-Za-z]){2,6}$/,
69800         url = /(((^https?)|(^ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
69801
69802     // All these messages and functions are configurable
69803     return {
69804         /**
69805          * The function used to validate email addresses.  Note that this is a very basic validation -- complete
69806          * validation per the email RFC specifications is very complex and beyond the scope of this class, although
69807          * this function can be overridden if a more comprehensive validation scheme is desired.  See the validation
69808          * section of the <a href="http://en.wikipedia.org/wiki/E-mail_address">Wikipedia article on email addresses</a>
69809          * for additional information.  This implementation is intended to validate the following emails:<tt>
69810          * 'barney@example.de', 'barney.rubble@example.com', 'barney-rubble@example.coop', 'barney+rubble@example.com'
69811          * </tt>.
69812          * @param {String} value The email address
69813          * @return {Boolean} true if the RegExp test passed, and false if not.
69814          */
69815         'email' : function(v){
69816             return email.test(v);
69817         },
69818         /**
69819          * The error text to display when the email validation function returns false.  Defaults to:
69820          * <tt>'This field should be an e-mail address in the format "user@example.com"'</tt>
69821          * @type String
69822          */
69823         'emailText' : 'This field should be an e-mail address in the format "user@example.com"',
69824         /**
69825          * The keystroke filter mask to be applied on email input.  See the {@link #email} method for
69826          * information about more complex email validation. Defaults to:
69827          * <tt>/[a-z0-9_\.\-@]/i</tt>
69828          * @type RegExp
69829          */
69830         'emailMask' : /[a-z0-9_\.\-@\+]/i,
69831
69832         /**
69833          * The function used to validate URLs
69834          * @param {String} value The URL
69835          * @return {Boolean} true if the RegExp test passed, and false if not.
69836          */
69837         'url' : function(v){
69838             return url.test(v);
69839         },
69840         /**
69841          * The error text to display when the url validation function returns false.  Defaults to:
69842          * <tt>'This field should be a URL in the format "http:/'+'/www.example.com"'</tt>
69843          * @type String
69844          */
69845         'urlText' : 'This field should be a URL in the format "http:/'+'/www.example.com"',
69846
69847         /**
69848          * The function used to validate alpha values
69849          * @param {String} value The value
69850          * @return {Boolean} true if the RegExp test passed, and false if not.
69851          */
69852         'alpha' : function(v){
69853             return alpha.test(v);
69854         },
69855         /**
69856          * The error text to display when the alpha validation function returns false.  Defaults to:
69857          * <tt>'This field should only contain letters and _'</tt>
69858          * @type String
69859          */
69860         'alphaText' : 'This field should only contain letters and _',
69861         /**
69862          * The keystroke filter mask to be applied on alpha input.  Defaults to:
69863          * <tt>/[a-z_]/i</tt>
69864          * @type RegExp
69865          */
69866         'alphaMask' : /[a-z_]/i,
69867
69868         /**
69869          * The function used to validate alphanumeric values
69870          * @param {String} value The value
69871          * @return {Boolean} true if the RegExp test passed, and false if not.
69872          */
69873         'alphanum' : function(v){
69874             return alphanum.test(v);
69875         },
69876         /**
69877          * The error text to display when the alphanumeric validation function returns false.  Defaults to:
69878          * <tt>'This field should only contain letters, numbers and _'</tt>
69879          * @type String
69880          */
69881         'alphanumText' : 'This field should only contain letters, numbers and _',
69882         /**
69883          * The keystroke filter mask to be applied on alphanumeric input.  Defaults to:
69884          * <tt>/[a-z0-9_]/i</tt>
69885          * @type RegExp
69886          */
69887         'alphanumMask' : /[a-z0-9_]/i
69888     };
69889 }();
69890 /**
69891  * @class Ext.grid.GridPanel
69892  * @extends Ext.Panel
69893  * <p>This class represents the primary interface of a component based grid control to represent data
69894  * in a tabular format of rows and columns. The GridPanel is composed of the following:</p>
69895  * <div class="mdetail-params"><ul>
69896  * <li><b>{@link Ext.data.Store Store}</b> : The Model holding the data records (rows)
69897  * <div class="sub-desc"></div></li>
69898  * <li><b>{@link Ext.grid.ColumnModel Column model}</b> : Column makeup
69899  * <div class="sub-desc"></div></li>
69900  * <li><b>{@link Ext.grid.GridView View}</b> : Encapsulates the user interface
69901  * <div class="sub-desc"></div></li>
69902  * <li><b>{@link Ext.grid.AbstractSelectionModel selection model}</b> : Selection behavior
69903  * <div class="sub-desc"></div></li>
69904  * </ul></div>
69905  * <p>Example usage:</p>
69906  * <pre><code>
69907 var grid = new Ext.grid.GridPanel({
69908     {@link #store}: new {@link Ext.data.Store}({
69909         {@link Ext.data.Store#autoDestroy autoDestroy}: true,
69910         {@link Ext.data.Store#reader reader}: reader,
69911         {@link Ext.data.Store#data data}: xg.dummyData
69912     }),
69913     {@link #colModel}: new {@link Ext.grid.ColumnModel}({
69914         {@link Ext.grid.ColumnModel#defaults defaults}: {
69915             width: 120,
69916             sortable: true
69917         },
69918         {@link Ext.grid.ColumnModel#columns columns}: [
69919             {id: 'company', header: 'Company', width: 200, sortable: true, dataIndex: 'company'},
69920             {header: 'Price', renderer: Ext.util.Format.usMoney, dataIndex: 'price'},
69921             {header: 'Change', dataIndex: 'change'},
69922             {header: '% Change', dataIndex: 'pctChange'},
69923             // instead of specifying renderer: Ext.util.Format.dateRenderer('m/d/Y') use xtype
69924             {
69925                 header: 'Last Updated', width: 135, dataIndex: 'lastChange',
69926                 xtype: 'datecolumn', format: 'M d, Y'
69927             }
69928         ],
69929     }),
69930     {@link #viewConfig}: {
69931         {@link Ext.grid.GridView#forceFit forceFit}: true,
69932
69933 //      Return CSS class to apply to rows depending upon data values
69934         {@link Ext.grid.GridView#getRowClass getRowClass}: function(record, index) {
69935             var c = record.{@link Ext.data.Record#get get}('change');
69936             if (c < 0) {
69937                 return 'price-fall';
69938             } else if (c > 0) {
69939                 return 'price-rise';
69940             }
69941         }
69942     },
69943     {@link #sm}: new Ext.grid.RowSelectionModel({singleSelect:true}),
69944     width: 600,
69945     height: 300,
69946     frame: true,
69947     title: 'Framed with Row Selection and Horizontal Scrolling',
69948     iconCls: 'icon-grid'
69949 });
69950  * </code></pre>
69951  * <p><b><u>Notes:</u></b></p>
69952  * <div class="mdetail-params"><ul>
69953  * <li>Although this class inherits many configuration options from base classes, some of them
69954  * (such as autoScroll, autoWidth, layout, items, etc) are not used by this class, and will
69955  * have no effect.</li>
69956  * <li>A grid <b>requires</b> a width in which to scroll its columns, and a height in which to
69957  * scroll its rows. These dimensions can either be set explicitly through the
69958  * <tt>{@link Ext.BoxComponent#height height}</tt> and <tt>{@link Ext.BoxComponent#width width}</tt>
69959  * configuration options or implicitly set by using the grid as a child item of a
69960  * {@link Ext.Container Container} which will have a {@link Ext.Container#layout layout manager}
69961  * provide the sizing of its child items (for example the Container of the Grid may specify
69962  * <tt>{@link Ext.Container#layout layout}:'fit'</tt>).</li>
69963  * <li>To access the data in a Grid, it is necessary to use the data model encapsulated
69964  * by the {@link #store Store}. See the {@link #cellclick} event for more details.</li>
69965  * </ul></div>
69966  * @constructor
69967  * @param {Object} config The config object
69968  * @xtype grid
69969  */
69970 Ext.grid.GridPanel = Ext.extend(Ext.Panel, {
69971     /**
69972      * @cfg {String} autoExpandColumn
69973      * <p>The <tt>{@link Ext.grid.Column#id id}</tt> of a {@link Ext.grid.Column column} in
69974      * this grid that should expand to fill unused space. This value specified here can not
69975      * be <tt>0</tt>.</p>
69976      * <br><p><b>Note</b>: If the Grid's {@link Ext.grid.GridView view} is configured with
69977      * <tt>{@link Ext.grid.GridView#forceFit forceFit}=true</tt> the <tt>autoExpandColumn</tt>
69978      * is ignored. See {@link Ext.grid.Column}.<tt>{@link Ext.grid.Column#width width}</tt>
69979      * for additional details.</p>
69980      * <p>See <tt>{@link #autoExpandMax}</tt> and <tt>{@link #autoExpandMin}</tt> also.</p>
69981      */
69982     autoExpandColumn : false,
69983     
69984     /**
69985      * @cfg {Number} autoExpandMax The maximum width the <tt>{@link #autoExpandColumn}</tt>
69986      * can have (if enabled). Defaults to <tt>1000</tt>.
69987      */
69988     autoExpandMax : 1000,
69989     
69990     /**
69991      * @cfg {Number} autoExpandMin The minimum width the <tt>{@link #autoExpandColumn}</tt>
69992      * can have (if enabled). Defaults to <tt>50</tt>.
69993      */
69994     autoExpandMin : 50,
69995     
69996     /**
69997      * @cfg {Boolean} columnLines <tt>true</tt> to add css for column separation lines.
69998      * Default is <tt>false</tt>.
69999      */
70000     columnLines : false,
70001     
70002     /**
70003      * @cfg {Object} cm Shorthand for <tt>{@link #colModel}</tt>.
70004      */
70005     /**
70006      * @cfg {Object} colModel The {@link Ext.grid.ColumnModel} to use when rendering the grid (required).
70007      */
70008     /**
70009      * @cfg {Array} columns An array of {@link Ext.grid.Column columns} to auto create a
70010      * {@link Ext.grid.ColumnModel}.  The ColumnModel may be explicitly created via the
70011      * <tt>{@link #colModel}</tt> configuration property.
70012      */
70013     /**
70014      * @cfg {String} ddGroup The DD group this GridPanel belongs to. Defaults to <tt>'GridDD'</tt> if not specified.
70015      */
70016     /**
70017      * @cfg {String} ddText
70018      * Configures the text in the drag proxy.  Defaults to:
70019      * <pre><code>
70020      * ddText : '{0} selected row{1}'
70021      * </code></pre>
70022      * <tt>{0}</tt> is replaced with the number of selected rows.
70023      */
70024     ddText : '{0} selected row{1}',
70025     
70026     /**
70027      * @cfg {Boolean} deferRowRender <P>Defaults to <tt>true</tt> to enable deferred row rendering.</p>
70028      * <p>This allows the GridPanel to be initially rendered empty, with the expensive update of the row
70029      * structure deferred so that layouts with GridPanels appear more quickly.</p>
70030      */
70031     deferRowRender : true,
70032     
70033     /**
70034      * @cfg {Boolean} disableSelection <p><tt>true</tt> to disable selections in the grid. Defaults to <tt>false</tt>.</p>
70035      * <p>Ignored if a {@link #selModel SelectionModel} is specified.</p>
70036      */
70037     /**
70038      * @cfg {Boolean} enableColumnResize <tt>false</tt> to turn off column resizing for the whole grid. Defaults to <tt>true</tt>.
70039      */
70040     /**
70041      * @cfg {Boolean} enableColumnHide
70042      * Defaults to <tt>true</tt> to enable {@link Ext.grid.Column#hidden hiding of columns}
70043      * with the {@link #enableHdMenu header menu}.
70044      */
70045     enableColumnHide : true,
70046     
70047     /**
70048      * @cfg {Boolean} enableColumnMove Defaults to <tt>true</tt> to enable drag and drop reorder of columns. <tt>false</tt>
70049      * to turn off column reordering via drag drop.
70050      */
70051     enableColumnMove : true,
70052     
70053     /**
70054      * @cfg {Boolean} enableDragDrop <p>Enables dragging of the selected rows of the GridPanel. Defaults to <tt>false</tt>.</p>
70055      * <p>Setting this to <b><tt>true</tt></b> causes this GridPanel's {@link #getView GridView} to
70056      * create an instance of {@link Ext.grid.GridDragZone}. <b>Note</b>: this is available only <b>after</b>
70057      * the Grid has been rendered as the GridView's <tt>{@link Ext.grid.GridView#dragZone dragZone}</tt>
70058      * property.</p>
70059      * <p>A cooperating {@link Ext.dd.DropZone DropZone} must be created who's implementations of
70060      * {@link Ext.dd.DropZone#onNodeEnter onNodeEnter}, {@link Ext.dd.DropZone#onNodeOver onNodeOver},
70061      * {@link Ext.dd.DropZone#onNodeOut onNodeOut} and {@link Ext.dd.DropZone#onNodeDrop onNodeDrop} are able
70062      * to process the {@link Ext.grid.GridDragZone#getDragData data} which is provided.</p>
70063      */
70064     enableDragDrop : false,
70065     
70066     /**
70067      * @cfg {Boolean} enableHdMenu Defaults to <tt>true</tt> to enable the drop down button for menu in the headers.
70068      */
70069     enableHdMenu : true,
70070     
70071     /**
70072      * @cfg {Boolean} hideHeaders True to hide the grid's header. Defaults to <code>false</code>.
70073      */
70074     /**
70075      * @cfg {Object} loadMask An {@link Ext.LoadMask} config or true to mask the grid while
70076      * loading. Defaults to <code>false</code>.
70077      */
70078     loadMask : false,
70079     
70080     /**
70081      * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if <tt>autoHeight</tt> is not on.
70082      */
70083     /**
70084      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Defaults to <tt>25</tt>.
70085      */
70086     minColumnWidth : 25,
70087     
70088     /**
70089      * @cfg {Object} sm Shorthand for <tt>{@link #selModel}</tt>.
70090      */
70091     /**
70092      * @cfg {Object} selModel Any subclass of {@link Ext.grid.AbstractSelectionModel} that will provide
70093      * the selection model for the grid (defaults to {@link Ext.grid.RowSelectionModel} if not specified).
70094      */
70095     /**
70096      * @cfg {Ext.data.Store} store The {@link Ext.data.Store} the grid should use as its data source (required).
70097      */
70098     /**
70099      * @cfg {Boolean} stripeRows <tt>true</tt> to stripe the rows. Default is <tt>false</tt>.
70100      * <p>This causes the CSS class <tt><b>x-grid3-row-alt</b></tt> to be added to alternate rows of
70101      * the grid. A default CSS rule is provided which sets a background colour, but you can override this
70102      * with a rule which either overrides the <b>background-color</b> style using the '!important'
70103      * modifier, or which uses a CSS selector of higher specificity.</p>
70104      */
70105     stripeRows : false,
70106     
70107     /**
70108      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is <tt>true</tt>
70109      * for GridPanel, but <tt>false</tt> for EditorGridPanel.
70110      */
70111     trackMouseOver : true,
70112     
70113     /**
70114      * @cfg {Array} stateEvents
70115      * An array of events that, when fired, should trigger this component to save its state.
70116      * Defaults to:<pre><code>
70117      * stateEvents: ['columnmove', 'columnresize', 'sortchange', 'groupchange']
70118      * </code></pre>
70119      * <p>These can be any types of events supported by this component, including browser or
70120      * custom events (e.g., <tt>['click', 'customerchange']</tt>).</p>
70121      * <p>See {@link Ext.Component#stateful} for an explanation of saving and restoring
70122      * Component state.</p>
70123      */
70124     stateEvents : ['columnmove', 'columnresize', 'sortchange', 'groupchange'],
70125     
70126     /**
70127      * @cfg {Object} view The {@link Ext.grid.GridView} used by the grid. This can be set
70128      * before a call to {@link Ext.Component#render render()}.
70129      */
70130     view : null,
70131
70132     /**
70133      * @cfg {Array} bubbleEvents
70134      * <p>An array of events that, when fired, should be bubbled to any parent container.
70135      * See {@link Ext.util.Observable#enableBubble}.
70136      * Defaults to <tt>[]</tt>.
70137      */
70138     bubbleEvents: [],
70139
70140     /**
70141      * @cfg {Object} viewConfig A config object that will be applied to the grid's UI view.  Any of
70142      * the config options available for {@link Ext.grid.GridView} can be specified here. This option
70143      * is ignored if <tt>{@link #view}</tt> is specified.
70144      */
70145
70146     // private
70147     rendered : false,
70148     
70149     // private
70150     viewReady : false,
70151
70152     // private
70153     initComponent : function() {
70154         Ext.grid.GridPanel.superclass.initComponent.call(this);
70155
70156         if (this.columnLines) {
70157             this.cls = (this.cls || '') + ' x-grid-with-col-lines';
70158         }
70159         // override any provided value since it isn't valid
70160         // and is causing too many bug reports ;)
70161         this.autoScroll = false;
70162         this.autoWidth = false;
70163
70164         if(Ext.isArray(this.columns)){
70165             this.colModel = new Ext.grid.ColumnModel(this.columns);
70166             delete this.columns;
70167         }
70168
70169         // check and correct shorthanded configs
70170         if(this.ds){
70171             this.store = this.ds;
70172             delete this.ds;
70173         }
70174         if(this.cm){
70175             this.colModel = this.cm;
70176             delete this.cm;
70177         }
70178         if(this.sm){
70179             this.selModel = this.sm;
70180             delete this.sm;
70181         }
70182         this.store = Ext.StoreMgr.lookup(this.store);
70183
70184         this.addEvents(
70185             // raw events
70186             /**
70187              * @event click
70188              * The raw click event for the entire grid.
70189              * @param {Ext.EventObject} e
70190              */
70191             'click',
70192             /**
70193              * @event dblclick
70194              * The raw dblclick event for the entire grid.
70195              * @param {Ext.EventObject} e
70196              */
70197             'dblclick',
70198             /**
70199              * @event contextmenu
70200              * The raw contextmenu event for the entire grid.
70201              * @param {Ext.EventObject} e
70202              */
70203             'contextmenu',
70204             /**
70205              * @event mousedown
70206              * The raw mousedown event for the entire grid.
70207              * @param {Ext.EventObject} e
70208              */
70209             'mousedown',
70210             /**
70211              * @event mouseup
70212              * The raw mouseup event for the entire grid.
70213              * @param {Ext.EventObject} e
70214              */
70215             'mouseup',
70216             /**
70217              * @event mouseover
70218              * The raw mouseover event for the entire grid.
70219              * @param {Ext.EventObject} e
70220              */
70221             'mouseover',
70222             /**
70223              * @event mouseout
70224              * The raw mouseout event for the entire grid.
70225              * @param {Ext.EventObject} e
70226              */
70227             'mouseout',
70228             /**
70229              * @event keypress
70230              * The raw keypress event for the entire grid.
70231              * @param {Ext.EventObject} e
70232              */
70233             'keypress',
70234             /**
70235              * @event keydown
70236              * The raw keydown event for the entire grid.
70237              * @param {Ext.EventObject} e
70238              */
70239             'keydown',
70240
70241             // custom events
70242             /**
70243              * @event cellmousedown
70244              * Fires before a cell is clicked
70245              * @param {Grid} this
70246              * @param {Number} rowIndex
70247              * @param {Number} columnIndex
70248              * @param {Ext.EventObject} e
70249              */
70250             'cellmousedown',
70251             /**
70252              * @event rowmousedown
70253              * Fires before a row is clicked
70254              * @param {Grid} this
70255              * @param {Number} rowIndex
70256              * @param {Ext.EventObject} e
70257              */
70258             'rowmousedown',
70259             /**
70260              * @event headermousedown
70261              * Fires before a header is clicked
70262              * @param {Grid} this
70263              * @param {Number} columnIndex
70264              * @param {Ext.EventObject} e
70265              */
70266             'headermousedown',
70267
70268             /**
70269              * @event groupmousedown
70270              * Fires before a group header is clicked. <b>Only applies for grids with a {@link Ext.grid.GroupingView GroupingView}</b>.
70271              * @param {Grid} this
70272              * @param {String} groupField
70273              * @param {String} groupValue
70274              * @param {Ext.EventObject} e
70275              */
70276             'groupmousedown',
70277
70278             /**
70279              * @event rowbodymousedown
70280              * Fires before the row body is clicked. <b>Only applies for grids with {@link Ext.grid.GridView#enableRowBody enableRowBody} configured.</b>
70281              * @param {Grid} this
70282              * @param {Number} rowIndex
70283              * @param {Ext.EventObject} e
70284              */
70285             'rowbodymousedown',
70286
70287             /**
70288              * @event containermousedown
70289              * Fires before the container is clicked. The container consists of any part of the grid body that is not covered by a row.
70290              * @param {Grid} this
70291              * @param {Ext.EventObject} e
70292              */
70293             'containermousedown',
70294
70295             /**
70296              * @event cellclick
70297              * Fires when a cell is clicked.
70298              * The data for the cell is drawn from the {@link Ext.data.Record Record}
70299              * for this row. To access the data in the listener function use the
70300              * following technique:
70301              * <pre><code>
70302 function(grid, rowIndex, columnIndex, e) {
70303     var record = grid.getStore().getAt(rowIndex);  // Get the Record
70304     var fieldName = grid.getColumnModel().getDataIndex(columnIndex); // Get field name
70305     var data = record.get(fieldName);
70306 }
70307 </code></pre>
70308              * @param {Grid} this
70309              * @param {Number} rowIndex
70310              * @param {Number} columnIndex
70311              * @param {Ext.EventObject} e
70312              */
70313             'cellclick',
70314             /**
70315              * @event celldblclick
70316              * Fires when a cell is double clicked
70317              * @param {Grid} this
70318              * @param {Number} rowIndex
70319              * @param {Number} columnIndex
70320              * @param {Ext.EventObject} e
70321              */
70322             'celldblclick',
70323             /**
70324              * @event rowclick
70325              * Fires when a row is clicked
70326              * @param {Grid} this
70327              * @param {Number} rowIndex
70328              * @param {Ext.EventObject} e
70329              */
70330             'rowclick',
70331             /**
70332              * @event rowdblclick
70333              * Fires when a row is double clicked
70334              * @param {Grid} this
70335              * @param {Number} rowIndex
70336              * @param {Ext.EventObject} e
70337              */
70338             'rowdblclick',
70339             /**
70340              * @event headerclick
70341              * Fires when a header is clicked
70342              * @param {Grid} this
70343              * @param {Number} columnIndex
70344              * @param {Ext.EventObject} e
70345              */
70346             'headerclick',
70347             /**
70348              * @event headerdblclick
70349              * Fires when a header cell is double clicked
70350              * @param {Grid} this
70351              * @param {Number} columnIndex
70352              * @param {Ext.EventObject} e
70353              */
70354             'headerdblclick',
70355             /**
70356              * @event groupclick
70357              * Fires when group header is clicked. <b>Only applies for grids with a {@link Ext.grid.GroupingView GroupingView}</b>.
70358              * @param {Grid} this
70359              * @param {String} groupField
70360              * @param {String} groupValue
70361              * @param {Ext.EventObject} e
70362              */
70363             'groupclick',
70364             /**
70365              * @event groupdblclick
70366              * Fires when group header is double clicked. <b>Only applies for grids with a {@link Ext.grid.GroupingView GroupingView}</b>.
70367              * @param {Grid} this
70368              * @param {String} groupField
70369              * @param {String} groupValue
70370              * @param {Ext.EventObject} e
70371              */
70372             'groupdblclick',
70373             /**
70374              * @event containerclick
70375              * Fires when the container is clicked. The container consists of any part of the grid body that is not covered by a row.
70376              * @param {Grid} this
70377              * @param {Ext.EventObject} e
70378              */
70379             'containerclick',
70380             /**
70381              * @event containerdblclick
70382              * Fires when the container is double clicked. The container consists of any part of the grid body that is not covered by a row.
70383              * @param {Grid} this
70384              * @param {Ext.EventObject} e
70385              */
70386             'containerdblclick',
70387
70388             /**
70389              * @event rowbodyclick
70390              * Fires when the row body is clicked. <b>Only applies for grids with {@link Ext.grid.GridView#enableRowBody enableRowBody} configured.</b>
70391              * @param {Grid} this
70392              * @param {Number} rowIndex
70393              * @param {Ext.EventObject} e
70394              */
70395             'rowbodyclick',
70396             /**
70397              * @event rowbodydblclick
70398              * Fires when the row body is double clicked. <b>Only applies for grids with {@link Ext.grid.GridView#enableRowBody enableRowBody} configured.</b>
70399              * @param {Grid} this
70400              * @param {Number} rowIndex
70401              * @param {Ext.EventObject} e
70402              */
70403             'rowbodydblclick',
70404
70405             /**
70406              * @event rowcontextmenu
70407              * Fires when a row is right clicked
70408              * @param {Grid} this
70409              * @param {Number} rowIndex
70410              * @param {Ext.EventObject} e
70411              */
70412             'rowcontextmenu',
70413             /**
70414              * @event cellcontextmenu
70415              * Fires when a cell is right clicked
70416              * @param {Grid} this
70417              * @param {Number} rowIndex
70418              * @param {Number} cellIndex
70419              * @param {Ext.EventObject} e
70420              */
70421             'cellcontextmenu',
70422             /**
70423              * @event headercontextmenu
70424              * Fires when a header is right clicked
70425              * @param {Grid} this
70426              * @param {Number} columnIndex
70427              * @param {Ext.EventObject} e
70428              */
70429             'headercontextmenu',
70430             /**
70431              * @event groupcontextmenu
70432              * Fires when group header is right clicked. <b>Only applies for grids with a {@link Ext.grid.GroupingView GroupingView}</b>.
70433              * @param {Grid} this
70434              * @param {String} groupField
70435              * @param {String} groupValue
70436              * @param {Ext.EventObject} e
70437              */
70438             'groupcontextmenu',
70439             /**
70440              * @event containercontextmenu
70441              * Fires when the container is right clicked. The container consists of any part of the grid body that is not covered by a row.
70442              * @param {Grid} this
70443              * @param {Ext.EventObject} e
70444              */
70445             'containercontextmenu',
70446             /**
70447              * @event rowbodycontextmenu
70448              * Fires when the row body is right clicked. <b>Only applies for grids with {@link Ext.grid.GridView#enableRowBody enableRowBody} configured.</b>
70449              * @param {Grid} this
70450              * @param {Number} rowIndex
70451              * @param {Ext.EventObject} e
70452              */
70453             'rowbodycontextmenu',
70454             /**
70455              * @event bodyscroll
70456              * Fires when the body element is scrolled
70457              * @param {Number} scrollLeft
70458              * @param {Number} scrollTop
70459              */
70460             'bodyscroll',
70461             /**
70462              * @event columnresize
70463              * Fires when the user resizes a column
70464              * @param {Number} columnIndex
70465              * @param {Number} newSize
70466              */
70467             'columnresize',
70468             /**
70469              * @event columnmove
70470              * Fires when the user moves a column
70471              * @param {Number} oldIndex
70472              * @param {Number} newIndex
70473              */
70474             'columnmove',
70475             /**
70476              * @event sortchange
70477              * Fires when the grid's store sort changes
70478              * @param {Grid} this
70479              * @param {Object} sortInfo An object with the keys field and direction
70480              */
70481             'sortchange',
70482             /**
70483              * @event groupchange
70484              * Fires when the grid's grouping changes (only applies for grids with a {@link Ext.grid.GroupingView GroupingView})
70485              * @param {Grid} this
70486              * @param {String} groupField A string with the grouping field, null if the store is not grouped.
70487              */
70488             'groupchange',
70489             /**
70490              * @event reconfigure
70491              * Fires when the grid is reconfigured with a new store and/or column model.
70492              * @param {Grid} this
70493              * @param {Ext.data.Store} store The new store
70494              * @param {Ext.grid.ColumnModel} colModel The new column model
70495              */
70496             'reconfigure',
70497             /**
70498              * @event viewready
70499              * Fires when the grid view is available (use this for selecting a default row).
70500              * @param {Grid} this
70501              */
70502             'viewready'
70503         );
70504     },
70505
70506     // private
70507     onRender : function(ct, position){
70508         Ext.grid.GridPanel.superclass.onRender.apply(this, arguments);
70509
70510         var c = this.getGridEl();
70511
70512         this.el.addClass('x-grid-panel');
70513
70514         this.mon(c, {
70515             scope: this,
70516             mousedown: this.onMouseDown,
70517             click: this.onClick,
70518             dblclick: this.onDblClick,
70519             contextmenu: this.onContextMenu
70520         });
70521
70522         this.relayEvents(c, ['mousedown','mouseup','mouseover','mouseout','keypress', 'keydown']);
70523
70524         var view = this.getView();
70525         view.init(this);
70526         view.render();
70527         this.getSelectionModel().init(this);
70528     },
70529
70530     // private
70531     initEvents : function(){
70532         Ext.grid.GridPanel.superclass.initEvents.call(this);
70533
70534         if(this.loadMask){
70535             this.loadMask = new Ext.LoadMask(this.bwrap,
70536                     Ext.apply({store:this.store}, this.loadMask));
70537         }
70538     },
70539
70540     initStateEvents : function(){
70541         Ext.grid.GridPanel.superclass.initStateEvents.call(this);
70542         this.mon(this.colModel, 'hiddenchange', this.saveState, this, {delay: 100});
70543     },
70544
70545     applyState : function(state){
70546         var cm = this.colModel,
70547             cs = state.columns,
70548             store = this.store,
70549             s,
70550             c,
70551             colIndex;
70552
70553         if(cs){
70554             for(var i = 0, len = cs.length; i < len; i++){
70555                 s = cs[i];
70556                 c = cm.getColumnById(s.id);
70557                 if(c){
70558                     colIndex = cm.getIndexById(s.id);
70559                     cm.setState(colIndex, {
70560                         hidden: s.hidden,
70561                         width: s.width,
70562                         sortable: s.sortable
70563                     });
70564                     if(colIndex != i){
70565                         cm.moveColumn(colIndex, i);
70566                     }
70567                 }
70568             }
70569         }
70570         if(store){
70571             s = state.sort;
70572             if(s){
70573                 store[store.remoteSort ? 'setDefaultSort' : 'sort'](s.field, s.direction);
70574             }
70575             s = state.group;
70576             if(store.groupBy){
70577                 if(s){
70578                     store.groupBy(s);
70579                 }else{
70580                     store.clearGrouping();
70581                 }
70582             }
70583
70584         }
70585         var o = Ext.apply({}, state);
70586         delete o.columns;
70587         delete o.sort;
70588         Ext.grid.GridPanel.superclass.applyState.call(this, o);
70589     },
70590
70591     getState : function(){
70592         var o = {columns: []},
70593             store = this.store,
70594             ss,
70595             gs;
70596
70597         for(var i = 0, c; (c = this.colModel.config[i]); i++){
70598             o.columns[i] = {
70599                 id: c.id,
70600                 width: c.width
70601             };
70602             if(c.hidden){
70603                 o.columns[i].hidden = true;
70604             }
70605             if(c.sortable){
70606                 o.columns[i].sortable = true;
70607             }
70608         }
70609         if(store){
70610             ss = store.getSortState();
70611             if(ss){
70612                 o.sort = ss;
70613             }
70614             if(store.getGroupState){
70615                 gs = store.getGroupState();
70616                 if(gs){
70617                     o.group = gs;
70618                 }
70619             }
70620         }
70621         return o;
70622     },
70623
70624     // private
70625     afterRender : function(){
70626         Ext.grid.GridPanel.superclass.afterRender.call(this);
70627         var v = this.view;
70628         this.on('bodyresize', v.layout, v);
70629         v.layout(true);
70630         if(this.deferRowRender){
70631             if (!this.deferRowRenderTask){
70632                 this.deferRowRenderTask = new Ext.util.DelayedTask(v.afterRender, this.view);
70633             }
70634             this.deferRowRenderTask.delay(10);
70635         }else{
70636             v.afterRender();
70637         }
70638         this.viewReady = true;
70639     },
70640
70641     /**
70642      * <p>Reconfigures the grid to use a different Store and Column Model
70643      * and fires the 'reconfigure' event. The View will be bound to the new
70644      * objects and refreshed.</p>
70645      * <p>Be aware that upon reconfiguring a GridPanel, certain existing settings <i>may</i> become
70646      * invalidated. For example the configured {@link #autoExpandColumn} may no longer exist in the
70647      * new ColumnModel. Also, an existing {@link Ext.PagingToolbar PagingToolbar} will still be bound
70648      * to the old Store, and will need rebinding. Any {@link #plugins} might also need reconfiguring
70649      * with the new data.</p>
70650      * @param {Ext.data.Store} store The new {@link Ext.data.Store} object
70651      * @param {Ext.grid.ColumnModel} colModel The new {@link Ext.grid.ColumnModel} object
70652      */
70653     reconfigure : function(store, colModel){
70654         var rendered = this.rendered;
70655         if(rendered){
70656             if(this.loadMask){
70657                 this.loadMask.destroy();
70658                 this.loadMask = new Ext.LoadMask(this.bwrap,
70659                         Ext.apply({}, {store:store}, this.initialConfig.loadMask));
70660             }
70661         }
70662         if(this.view){
70663             this.view.initData(store, colModel);
70664         }
70665         this.store = store;
70666         this.colModel = colModel;
70667         if(rendered){
70668             this.view.refresh(true);
70669         }
70670         this.fireEvent('reconfigure', this, store, colModel);
70671     },
70672
70673     // private
70674     onDestroy : function(){
70675         if (this.deferRowRenderTask && this.deferRowRenderTask.cancel){
70676             this.deferRowRenderTask.cancel();
70677         }
70678         if(this.rendered){
70679             Ext.destroy(this.view, this.loadMask);
70680         }else if(this.store && this.store.autoDestroy){
70681             this.store.destroy();
70682         }
70683         Ext.destroy(this.colModel, this.selModel);
70684         this.store = this.selModel = this.colModel = this.view = this.loadMask = null;
70685         Ext.grid.GridPanel.superclass.onDestroy.call(this);
70686     },
70687
70688     // private
70689     processEvent : function(name, e){
70690         this.view.processEvent(name, e);
70691     },
70692
70693     // private
70694     onClick : function(e){
70695         this.processEvent('click', e);
70696     },
70697
70698     // private
70699     onMouseDown : function(e){
70700         this.processEvent('mousedown', e);
70701     },
70702
70703     // private
70704     onContextMenu : function(e, t){
70705         this.processEvent('contextmenu', e);
70706     },
70707
70708     // private
70709     onDblClick : function(e){
70710         this.processEvent('dblclick', e);
70711     },
70712
70713     // private
70714     walkCells : function(row, col, step, fn, scope){
70715         var cm    = this.colModel,
70716             clen  = cm.getColumnCount(),
70717             ds    = this.store,
70718             rlen  = ds.getCount(),
70719             first = true;
70720
70721         if(step < 0){
70722             if(col < 0){
70723                 row--;
70724                 first = false;
70725             }
70726             while(row >= 0){
70727                 if(!first){
70728                     col = clen-1;
70729                 }
70730                 first = false;
70731                 while(col >= 0){
70732                     if(fn.call(scope || this, row, col, cm) === true){
70733                         return [row, col];
70734                     }
70735                     col--;
70736                 }
70737                 row--;
70738             }
70739         } else {
70740             if(col >= clen){
70741                 row++;
70742                 first = false;
70743             }
70744             while(row < rlen){
70745                 if(!first){
70746                     col = 0;
70747                 }
70748                 first = false;
70749                 while(col < clen){
70750                     if(fn.call(scope || this, row, col, cm) === true){
70751                         return [row, col];
70752                     }
70753                     col++;
70754                 }
70755                 row++;
70756             }
70757         }
70758         return null;
70759     },
70760
70761     /**
70762      * Returns the grid's underlying element.
70763      * @return {Element} The element
70764      */
70765     getGridEl : function(){
70766         return this.body;
70767     },
70768
70769     // private for compatibility, overridden by editor grid
70770     stopEditing : Ext.emptyFn,
70771
70772     /**
70773      * Returns the grid's selection model configured by the <code>{@link #selModel}</code>
70774      * configuration option. If no selection model was configured, this will create
70775      * and return a {@link Ext.grid.RowSelectionModel RowSelectionModel}.
70776      * @return {SelectionModel}
70777      */
70778     getSelectionModel : function(){
70779         if(!this.selModel){
70780             this.selModel = new Ext.grid.RowSelectionModel(
70781                     this.disableSelection ? {selectRow: Ext.emptyFn} : null);
70782         }
70783         return this.selModel;
70784     },
70785
70786     /**
70787      * Returns the grid's data store.
70788      * @return {Ext.data.Store} The store
70789      */
70790     getStore : function(){
70791         return this.store;
70792     },
70793
70794     /**
70795      * Returns the grid's ColumnModel.
70796      * @return {Ext.grid.ColumnModel} The column model
70797      */
70798     getColumnModel : function(){
70799         return this.colModel;
70800     },
70801
70802     /**
70803      * Returns the grid's GridView object.
70804      * @return {Ext.grid.GridView} The grid view
70805      */
70806     getView : function() {
70807         if (!this.view) {
70808             this.view = new Ext.grid.GridView(this.viewConfig);
70809         }
70810         
70811         return this.view;
70812     },
70813     /**
70814      * Called to get grid's drag proxy text, by default returns this.ddText.
70815      * @return {String} The text
70816      */
70817     getDragDropText : function(){
70818         var count = this.selModel.getCount();
70819         return String.format(this.ddText, count, count == 1 ? '' : 's');
70820     }
70821
70822     /**
70823      * @cfg {String/Number} activeItem
70824      * @hide
70825      */
70826     /**
70827      * @cfg {Boolean} autoDestroy
70828      * @hide
70829      */
70830     /**
70831      * @cfg {Object/String/Function} autoLoad
70832      * @hide
70833      */
70834     /**
70835      * @cfg {Boolean} autoWidth
70836      * @hide
70837      */
70838     /**
70839      * @cfg {Boolean/Number} bufferResize
70840      * @hide
70841      */
70842     /**
70843      * @cfg {String} defaultType
70844      * @hide
70845      */
70846     /**
70847      * @cfg {Object} defaults
70848      * @hide
70849      */
70850     /**
70851      * @cfg {Boolean} hideBorders
70852      * @hide
70853      */
70854     /**
70855      * @cfg {Mixed} items
70856      * @hide
70857      */
70858     /**
70859      * @cfg {String} layout
70860      * @hide
70861      */
70862     /**
70863      * @cfg {Object} layoutConfig
70864      * @hide
70865      */
70866     /**
70867      * @cfg {Boolean} monitorResize
70868      * @hide
70869      */
70870     /**
70871      * @property items
70872      * @hide
70873      */
70874     /**
70875      * @method add
70876      * @hide
70877      */
70878     /**
70879      * @method cascade
70880      * @hide
70881      */
70882     /**
70883      * @method doLayout
70884      * @hide
70885      */
70886     /**
70887      * @method find
70888      * @hide
70889      */
70890     /**
70891      * @method findBy
70892      * @hide
70893      */
70894     /**
70895      * @method findById
70896      * @hide
70897      */
70898     /**
70899      * @method findByType
70900      * @hide
70901      */
70902     /**
70903      * @method getComponent
70904      * @hide
70905      */
70906     /**
70907      * @method getLayout
70908      * @hide
70909      */
70910     /**
70911      * @method getUpdater
70912      * @hide
70913      */
70914     /**
70915      * @method insert
70916      * @hide
70917      */
70918     /**
70919      * @method load
70920      * @hide
70921      */
70922     /**
70923      * @method remove
70924      * @hide
70925      */
70926     /**
70927      * @event add
70928      * @hide
70929      */
70930     /**
70931      * @event afterlayout
70932      * @hide
70933      */
70934     /**
70935      * @event beforeadd
70936      * @hide
70937      */
70938     /**
70939      * @event beforeremove
70940      * @hide
70941      */
70942     /**
70943      * @event remove
70944      * @hide
70945      */
70946
70947
70948
70949     /**
70950      * @cfg {String} allowDomMove  @hide
70951      */
70952     /**
70953      * @cfg {String} autoEl @hide
70954      */
70955     /**
70956      * @cfg {String} applyTo  @hide
70957      */
70958     /**
70959      * @cfg {String} autoScroll  @hide
70960      */
70961     /**
70962      * @cfg {String} bodyBorder  @hide
70963      */
70964     /**
70965      * @cfg {String} bodyStyle  @hide
70966      */
70967     /**
70968      * @cfg {String} contentEl  @hide
70969      */
70970     /**
70971      * @cfg {String} disabledClass  @hide
70972      */
70973     /**
70974      * @cfg {String} elements  @hide
70975      */
70976     /**
70977      * @cfg {String} html  @hide
70978      */
70979     /**
70980      * @cfg {Boolean} preventBodyReset
70981      * @hide
70982      */
70983     /**
70984      * @property disabled
70985      * @hide
70986      */
70987     /**
70988      * @method applyToMarkup
70989      * @hide
70990      */
70991     /**
70992      * @method enable
70993      * @hide
70994      */
70995     /**
70996      * @method disable
70997      * @hide
70998      */
70999     /**
71000      * @method setDisabled
71001      * @hide
71002      */
71003 });
71004 Ext.reg('grid', Ext.grid.GridPanel);/**
71005  * @class Ext.grid.PivotGrid
71006  * @extends Ext.grid.GridPanel
71007  * <p>The PivotGrid component enables rapid summarization of large data sets. It provides a way to reduce a large set of
71008  * data down into a format where trends and insights become more apparent. A classic example is in sales data; a company
71009  * will often have a record of all sales it makes for a given period - this will often encompass thousands of rows of
71010  * data. The PivotGrid allows you to see how well each salesperson performed, which cities generate the most revenue, 
71011  * how products perform between cities and so on.</p>
71012  * <p>A PivotGrid is composed of two axes (left and top), one {@link #measure} and one {@link #aggregator aggregation}
71013  * function. Each axis can contain one or more {@link #dimension}, which are ordered into a hierarchy. Dimensions on the 
71014  * left axis can also specify a width. Each dimension in each axis can specify its sort ordering, defaulting to "ASC", 
71015  * and must specify one of the fields in the {@link Ext.data.Record Record} used by the PivotGrid's 
71016  * {@link Ext.data.Store Store}.</p>
71017 <pre><code>
71018 // This is the record representing a single sale
71019 var SaleRecord = Ext.data.Record.create([
71020     {name: 'person',   type: 'string'},
71021     {name: 'product',  type: 'string'},
71022     {name: 'city',     type: 'string'},
71023     {name: 'state',    type: 'string'},
71024     {name: 'year',     type: 'int'},
71025     {name: 'value',    type: 'int'}
71026 ]);
71027
71028 // A simple store that loads SaleRecord data from a url
71029 var myStore = new Ext.data.Store({
71030     url: 'data.json',
71031     autoLoad: true,
71032     reader: new Ext.data.JsonReader({
71033         root: 'rows',
71034         idProperty: 'id'
71035     }, SaleRecord)
71036 });
71037
71038 // Create the PivotGrid itself, referencing the store
71039 var pivot = new Ext.grid.PivotGrid({
71040     store     : myStore,
71041     aggregator: 'sum',
71042     measure   : 'value',
71043
71044     leftAxis: [
71045         {
71046             width: 60,
71047             dataIndex: 'product'
71048         },
71049         {
71050             width: 120,
71051             dataIndex: 'person',
71052             direction: 'DESC'
71053         }
71054     ],
71055
71056     topAxis: [
71057         {
71058             dataIndex: 'year'
71059         }
71060     ]
71061 });
71062 </code></pre>
71063  * <p>The specified {@link #measure} is the field from SaleRecord that is extracted from each combination
71064  * of product and person (on the left axis) and year on the top axis. There may be several SaleRecords in the 
71065  * data set that share this combination, so an array of measure fields is produced. This array is then 
71066  * aggregated using the {@link #aggregator} function.</p>
71067  * <p>The default aggregator function is sum, which simply adds up all of the extracted measure values. Other
71068  * built-in aggregator functions are count, avg, min and max. In addition, you can specify your own function.
71069  * In this example we show the code used to sum the measures, but you can return any value you like. See
71070  * {@link #aggregator} for more details.</p>
71071 <pre><code>
71072 new Ext.grid.PivotGrid({
71073     aggregator: function(records, measure) {
71074         var length = records.length,
71075             total  = 0,
71076             i;
71077
71078         for (i = 0; i < length; i++) {
71079             total += records[i].get(measure);
71080         }
71081
71082         return total;
71083     },
71084     
71085     renderer: function(value) {
71086         return Math.round(value);
71087     },
71088     
71089     //your normal config here
71090 });
71091 </code></pre>
71092  * <p><u>Renderers</u></p>
71093  * <p>PivotGrid optionally accepts a {@link #renderer} function which can modify the data in each cell before it
71094  * is rendered. The renderer is passed the value that would usually be placed in the cell and is expected to return
71095  * the new value. For example let's imagine we had height data expressed as a decimal - here's how we might use a
71096  * renderer to display the data in feet and inches notation:</p>
71097 <pre><code>
71098 new Ext.grid.PivotGrid({
71099     //in each case the value is a decimal number of feet
71100     renderer  : function(value) {
71101         var feet   = Math.floor(value),
71102             inches = Math.round((value - feet) * 12);
71103
71104         return String.format("{0}' {1}\"", feet, inches);
71105     },
71106     //normal config here
71107 });
71108 </code></pre>
71109  * <p><u>Reconfiguring</u></p>
71110  * <p>All aspects PivotGrid's configuration can be updated at runtime. It is easy to change the {@link #setMeasure measure}, 
71111  * {@link #setAggregator aggregation function}, {@link #setLeftAxis left} and {@link #setTopAxis top} axes and refresh the grid.</p>
71112  * <p>In this case we reconfigure the PivotGrid to have city and year as the top axis dimensions, rendering the average sale
71113  * value into the cells:</p>
71114 <pre><code>
71115 //the left axis can also be changed
71116 pivot.topAxis.setDimensions([
71117     {dataIndex: 'city', direction: 'DESC'},
71118     {dataIndex: 'year', direction: 'ASC'}
71119 ]);
71120
71121 pivot.setMeasure('value');
71122 pivot.setAggregator('avg');
71123
71124 pivot.view.refresh(true);
71125 </code></pre>
71126  * <p>See the {@link Ext.grid.PivotAxis PivotAxis} documentation for further detail on reconfiguring axes.</p>
71127  */
71128 Ext.grid.PivotGrid = Ext.extend(Ext.grid.GridPanel, {
71129     
71130     /**
71131      * @cfg {String|Function} aggregator The aggregation function to use to combine the measures extracted
71132      * for each dimension combination. Can be any of the built-in aggregators (sum, count, avg, min, max).
71133      * Can also be a function which accepts two arguments (an array of Records to aggregate, and the measure 
71134      * to aggregate them on) and should return a String.
71135      */
71136     aggregator: 'sum',
71137     
71138     /**
71139      * @cfg {Function} renderer Optional renderer to pass values through before they are rendered to the dom. This
71140      * gives an opportunity to modify cell contents after the value has been computed.
71141      */
71142     renderer: undefined,
71143     
71144     /**
71145      * @cfg {String} measure The field to extract from each Record when pivoting around the two axes. See the class
71146      * introduction docs for usage
71147      */
71148     
71149     /**
71150      * @cfg {Array|Ext.grid.PivotAxis} leftAxis Either and array of {@link #dimension} to use on the left axis, or
71151      * a {@link Ext.grid.PivotAxis} instance. If an array is passed, it is turned into a PivotAxis internally.
71152      */
71153     
71154     /**
71155      * @cfg {Array|Ext.grid.PivotAxis} topAxis Either and array of {@link #dimension} to use on the top axis, or
71156      * a {@link Ext.grid.PivotAxis} instance. If an array is passed, it is turned into a PivotAxis internally.
71157      */
71158     
71159     //inherit docs
71160     initComponent: function() {
71161         Ext.grid.PivotGrid.superclass.initComponent.apply(this, arguments);
71162         
71163         this.initAxes();
71164         
71165         //no resizing of columns is allowed yet in PivotGrid
71166         this.enableColumnResize = false;
71167         
71168         this.viewConfig = Ext.apply(this.viewConfig || {}, {
71169             forceFit: true
71170         });
71171         
71172         //TODO: dummy col model that is never used - GridView is too tightly integrated with ColumnModel
71173         //in 3.x to remove this altogether.
71174         this.colModel = new Ext.grid.ColumnModel({});
71175     },
71176     
71177     /**
71178      * Returns the function currently used to aggregate the records in each Pivot cell
71179      * @return {Function} The current aggregator function
71180      */
71181     getAggregator: function() {
71182         if (typeof this.aggregator == 'string') {
71183             return Ext.grid.PivotAggregatorMgr.types[this.aggregator];
71184         } else {
71185             return this.aggregator;
71186         }
71187     },
71188     
71189     /**
71190      * Sets the function to use when aggregating data for each cell.
71191      * @param {String|Function} aggregator The new aggregator function or named function string
71192      */
71193     setAggregator: function(aggregator) {
71194         this.aggregator = aggregator;
71195     },
71196     
71197     /**
71198      * Sets the field name to use as the Measure in this Pivot Grid
71199      * @param {String} measure The field to make the measure
71200      */
71201     setMeasure: function(measure) {
71202         this.measure = measure;
71203     },
71204     
71205     /**
71206      * Sets the left axis of this pivot grid. Optionally refreshes the grid afterwards.
71207      * @param {Ext.grid.PivotAxis} axis The pivot axis
71208      * @param {Boolean} refresh True to immediately refresh the grid and its axes (defaults to false)
71209      */
71210     setLeftAxis: function(axis, refresh) {
71211         /**
71212          * The configured {@link Ext.grid.PivotAxis} used as the left Axis for this Pivot Grid
71213          * @property leftAxis
71214          * @type Ext.grid.PivotAxis
71215          */
71216         this.leftAxis = axis;
71217         
71218         if (refresh) {
71219             this.view.refresh();
71220         }
71221     },
71222     
71223     /**
71224      * Sets the top axis of this pivot grid. Optionally refreshes the grid afterwards.
71225      * @param {Ext.grid.PivotAxis} axis The pivot axis
71226      * @param {Boolean} refresh True to immediately refresh the grid and its axes (defaults to false)
71227      */
71228     setTopAxis: function(axis, refresh) {
71229         /**
71230          * The configured {@link Ext.grid.PivotAxis} used as the top Axis for this Pivot Grid
71231          * @property topAxis
71232          * @type Ext.grid.PivotAxis
71233          */
71234         this.topAxis = axis;
71235         
71236         if (refresh) {
71237             this.view.refresh();
71238         }
71239     },
71240     
71241     /**
71242      * @private
71243      * Creates the top and left axes. Should usually only need to be called once from initComponent
71244      */
71245     initAxes: function() {
71246         var PivotAxis = Ext.grid.PivotAxis;
71247         
71248         if (!(this.leftAxis instanceof PivotAxis)) {
71249             this.setLeftAxis(new PivotAxis({
71250                 orientation: 'vertical',
71251                 dimensions : this.leftAxis || [],
71252                 store      : this.store
71253             }));
71254         };
71255         
71256         if (!(this.topAxis instanceof PivotAxis)) {
71257             this.setTopAxis(new PivotAxis({
71258                 orientation: 'horizontal',
71259                 dimensions : this.topAxis || [],
71260                 store      : this.store
71261             }));
71262         };
71263     },
71264     
71265     /**
71266      * @private
71267      * @return {Array} 2-dimensional array of cell data
71268      */
71269     extractData: function() {
71270         var records  = this.store.data.items,
71271             recCount = records.length,
71272             cells    = [],
71273             record, i, j, k;
71274         
71275         if (recCount == 0) {
71276             return [];
71277         }
71278         
71279         var leftTuples = this.leftAxis.getTuples(),
71280             leftCount  = leftTuples.length,
71281             topTuples  = this.topAxis.getTuples(),
71282             topCount   = topTuples.length,
71283             aggregator = this.getAggregator();
71284         
71285         for (i = 0; i < recCount; i++) {
71286             record = records[i];
71287             
71288             for (j = 0; j < leftCount; j++) {
71289                 cells[j] = cells[j] || [];
71290                 
71291                 if (leftTuples[j].matcher(record) === true) {
71292                     for (k = 0; k < topCount; k++) {
71293                         cells[j][k] = cells[j][k] || [];
71294                         
71295                         if (topTuples[k].matcher(record)) {
71296                             cells[j][k].push(record);
71297                         }
71298                     }
71299                 }
71300             }
71301         }
71302         
71303         var rowCount = cells.length,
71304             colCount, row;
71305         
71306         for (i = 0; i < rowCount; i++) {
71307             row = cells[i];
71308             colCount = row.length;
71309             
71310             for (j = 0; j < colCount; j++) {
71311                 cells[i][j] = aggregator(cells[i][j], this.measure);
71312             }
71313         }
71314         
71315         return cells;
71316     },
71317     
71318     /**
71319      * Returns the grid's GridView object.
71320      * @return {Ext.grid.PivotGridView} The grid view
71321      */
71322     getView: function() {
71323         if (!this.view) {
71324             this.view = new Ext.grid.PivotGridView(this.viewConfig);
71325         }
71326         
71327         return this.view;
71328     }
71329 });
71330
71331 Ext.reg('pivotgrid', Ext.grid.PivotGrid);
71332
71333
71334 Ext.grid.PivotAggregatorMgr = new Ext.AbstractManager();
71335
71336 Ext.grid.PivotAggregatorMgr.registerType('sum', function(records, measure) {
71337     var length = records.length,
71338         total  = 0,
71339         i;
71340     
71341     for (i = 0; i < length; i++) {
71342         total += records[i].get(measure);
71343     }
71344     
71345     return total;
71346 });
71347
71348 Ext.grid.PivotAggregatorMgr.registerType('avg', function(records, measure) {
71349     var length = records.length,
71350         total  = 0,
71351         i;
71352     
71353     for (i = 0; i < length; i++) {
71354         total += records[i].get(measure);
71355     }
71356     
71357     return (total / length) || 'n/a';
71358 });
71359
71360 Ext.grid.PivotAggregatorMgr.registerType('min', function(records, measure) {
71361     var data   = [],
71362         length = records.length,
71363         i;
71364     
71365     for (i = 0; i < length; i++) {
71366         data.push(records[i].get(measure));
71367     }
71368     
71369     return Math.min.apply(this, data) || 'n/a';
71370 });
71371
71372 Ext.grid.PivotAggregatorMgr.registerType('max', function(records, measure) {
71373     var data   = [],
71374         length = records.length,
71375         i;
71376     
71377     for (i = 0; i < length; i++) {
71378         data.push(records[i].get(measure));
71379     }
71380     
71381     return Math.max.apply(this, data) || 'n/a';
71382 });
71383
71384 Ext.grid.PivotAggregatorMgr.registerType('count', function(records, measure) {
71385     return records.length;
71386 });/**
71387  * @class Ext.grid.GridView
71388  * @extends Ext.util.Observable
71389  * <p>This class encapsulates the user interface of an {@link Ext.grid.GridPanel}.
71390  * Methods of this class may be used to access user interface elements to enable
71391  * special display effects. Do not change the DOM structure of the user interface.</p>
71392  * <p>This class does not provide ways to manipulate the underlying data. The data
71393  * model of a Grid is held in an {@link Ext.data.Store}.</p>
71394  * @constructor
71395  * @param {Object} config
71396  */
71397 Ext.grid.GridView = Ext.extend(Ext.util.Observable, {
71398     /**
71399      * Override this function to apply custom CSS classes to rows during rendering.  You can also supply custom
71400      * parameters to the row template for the current row to customize how it is rendered using the <b>rowParams</b>
71401      * parameter.  This function should return the CSS class name (or empty string '' for none) that will be added
71402      * to the row's wrapping div.  To apply multiple class names, simply return them space-delimited within the string
71403      * (e.g., 'my-class another-class'). Example usage:
71404     <pre><code>
71405 viewConfig: {
71406     forceFit: true,
71407     showPreview: true, // custom property
71408     enableRowBody: true, // required to create a second, full-width row to show expanded Record data
71409     getRowClass: function(record, rowIndex, rp, ds){ // rp = rowParams
71410         if(this.showPreview){
71411             rp.body = '&lt;p>'+record.data.excerpt+'&lt;/p>';
71412             return 'x-grid3-row-expanded';
71413         }
71414         return 'x-grid3-row-collapsed';
71415     }
71416 },
71417     </code></pre>
71418      * @param {Record} record The {@link Ext.data.Record} corresponding to the current row.
71419      * @param {Number} index The row index.
71420      * @param {Object} rowParams A config object that is passed to the row template during rendering that allows
71421      * customization of various aspects of a grid row.
71422      * <p>If {@link #enableRowBody} is configured <b><tt></tt>true</b>, then the following properties may be set
71423      * by this function, and will be used to render a full-width expansion row below each grid row:</p>
71424      * <ul>
71425      * <li><code>body</code> : String <div class="sub-desc">An HTML fragment to be used as the expansion row's body content (defaults to '').</div></li>
71426      * <li><code>bodyStyle</code> : String <div class="sub-desc">A CSS style specification that will be applied to the expansion row's &lt;tr> element. (defaults to '').</div></li>
71427      * </ul>
71428      * The following property will be passed in, and may be appended to:
71429      * <ul>
71430      * <li><code>tstyle</code> : String <div class="sub-desc">A CSS style specification that willl be applied to the &lt;table> element which encapsulates
71431      * both the standard grid row, and any expansion row.</div></li>
71432      * </ul>
71433      * @param {Store} store The {@link Ext.data.Store} this grid is bound to
71434      * @method getRowClass
71435      * @return {String} a CSS class name to add to the row.
71436      */
71437
71438     /**
71439      * @cfg {Boolean} enableRowBody True to add a second TR element per row that can be used to provide a row body
71440      * that spans beneath the data row.  Use the {@link #getRowClass} method's rowParams config to customize the row body.
71441      */
71442
71443     /**
71444      * @cfg {String} emptyText Default text (html tags are accepted) to display in the grid body when no rows
71445      * are available (defaults to ''). This value will be used to update the <tt>{@link #mainBody}</tt>:
71446     <pre><code>
71447     this.mainBody.update('&lt;div class="x-grid-empty">' + this.emptyText + '&lt;/div>');
71448     </code></pre>
71449      */
71450
71451     /**
71452      * @cfg {Boolean} headersDisabled True to disable the grid column headers (defaults to <tt>false</tt>).
71453      * Use the {@link Ext.grid.ColumnModel ColumnModel} <tt>{@link Ext.grid.ColumnModel#menuDisabled menuDisabled}</tt>
71454      * config to disable the <i>menu</i> for individual columns.  While this config is true the
71455      * following will be disabled:<div class="mdetail-params"><ul>
71456      * <li>clicking on header to sort</li>
71457      * <li>the trigger to reveal the menu.</li>
71458      * </ul></div>
71459      */
71460
71461     /**
71462      * <p>A customized implementation of a {@link Ext.dd.DragZone DragZone} which provides default implementations
71463      * of the template methods of DragZone to enable dragging of the selected rows of a GridPanel.
71464      * See {@link Ext.grid.GridDragZone} for details.</p>
71465      * <p>This will <b>only</b> be present:<div class="mdetail-params"><ul>
71466      * <li><i>if</i> the owning GridPanel was configured with {@link Ext.grid.GridPanel#enableDragDrop enableDragDrop}: <tt>true</tt>.</li>
71467      * <li><i>after</i> the owning GridPanel has been rendered.</li>
71468      * </ul></div>
71469      * @property dragZone
71470      * @type {Ext.grid.GridDragZone}
71471      */
71472
71473     /**
71474      * @cfg {Boolean} deferEmptyText True to defer <tt>{@link #emptyText}</tt> being applied until the store's
71475      * first load (defaults to <tt>true</tt>).
71476      */
71477     deferEmptyText : true,
71478
71479     /**
71480      * @cfg {Number} scrollOffset The amount of space to reserve for the vertical scrollbar
71481      * (defaults to <tt>undefined</tt>). If an explicit value isn't specified, this will be automatically
71482      * calculated.
71483      */
71484     scrollOffset : undefined,
71485
71486     /**
71487      * @cfg {Boolean} autoFill
71488      * Defaults to <tt>false</tt>.  Specify <tt>true</tt> to have the column widths re-proportioned
71489      * when the grid is <b>initially rendered</b>.  The
71490      * {@link Ext.grid.Column#width initially configured width}</tt> of each column will be adjusted
71491      * to fit the grid width and prevent horizontal scrolling. If columns are later resized (manually
71492      * or programmatically), the other columns in the grid will <b>not</b> be resized to fit the grid width.
71493      * See <tt>{@link #forceFit}</tt> also.
71494      */
71495     autoFill : false,
71496
71497     /**
71498      * @cfg {Boolean} forceFit
71499      * <p>Defaults to <tt>false</tt>.  Specify <tt>true</tt> to have the column widths re-proportioned
71500      * at <b>all times</b>.</p>
71501      * <p>The {@link Ext.grid.Column#width initially configured width}</tt> of each
71502      * column will be adjusted to fit the grid width and prevent horizontal scrolling. If columns are
71503      * later resized (manually or programmatically), the other columns in the grid <b>will</b> be resized
71504      * to fit the grid width.</p>
71505      * <p>Columns which are configured with <code>fixed: true</code> are omitted from being resized.</p>
71506      * <p>See <tt>{@link #autoFill}</tt>.</p>
71507      */
71508     forceFit : false,
71509
71510     /**
71511      * @cfg {Array} sortClasses The CSS classes applied to a header when it is sorted. (defaults to <tt>['sort-asc', 'sort-desc']</tt>)
71512      */
71513     sortClasses : ['sort-asc', 'sort-desc'],
71514
71515     /**
71516      * @cfg {String} sortAscText The text displayed in the 'Sort Ascending' menu item (defaults to <tt>'Sort Ascending'</tt>)
71517      */
71518     sortAscText : 'Sort Ascending',
71519
71520     /**
71521      * @cfg {String} sortDescText The text displayed in the 'Sort Descending' menu item (defaults to <tt>'Sort Descending'</tt>)
71522      */
71523     sortDescText : 'Sort Descending',
71524
71525     /**
71526      * @cfg {String} columnsText The text displayed in the 'Columns' menu item (defaults to <tt>'Columns'</tt>)
71527      */
71528     columnsText : 'Columns',
71529
71530     /**
71531      * @cfg {String} selectedRowClass The CSS class applied to a selected row (defaults to <tt>'x-grid3-row-selected'</tt>). An
71532      * example overriding the default styling:
71533     <pre><code>
71534     .x-grid3-row-selected {background-color: yellow;}
71535     </code></pre>
71536      * Note that this only controls the row, and will not do anything for the text inside it.  To style inner
71537      * facets (like text) use something like:
71538     <pre><code>
71539     .x-grid3-row-selected .x-grid3-cell-inner {
71540         color: #FFCC00;
71541     }
71542     </code></pre>
71543      * @type String
71544      */
71545     selectedRowClass : 'x-grid3-row-selected',
71546
71547     // private
71548     borderWidth : 2,
71549     tdClass : 'x-grid3-cell',
71550     hdCls : 'x-grid3-hd',
71551     
71552     
71553     /**
71554      * @cfg {Boolean} markDirty True to show the dirty cell indicator when a cell has been modified. Defaults to <tt>true</tt>.
71555      */
71556     markDirty : true,
71557
71558     /**
71559      * @cfg {Number} cellSelectorDepth The number of levels to search for cells in event delegation (defaults to <tt>4</tt>)
71560      */
71561     cellSelectorDepth : 4,
71562     
71563     /**
71564      * @cfg {Number} rowSelectorDepth The number of levels to search for rows in event delegation (defaults to <tt>10</tt>)
71565      */
71566     rowSelectorDepth : 10,
71567
71568     /**
71569      * @cfg {Number} rowBodySelectorDepth The number of levels to search for row bodies in event delegation (defaults to <tt>10</tt>)
71570      */
71571     rowBodySelectorDepth : 10,
71572
71573     /**
71574      * @cfg {String} cellSelector The selector used to find cells internally (defaults to <tt>'td.x-grid3-cell'</tt>)
71575      */
71576     cellSelector : 'td.x-grid3-cell',
71577     
71578     /**
71579      * @cfg {String} rowSelector The selector used to find rows internally (defaults to <tt>'div.x-grid3-row'</tt>)
71580      */
71581     rowSelector : 'div.x-grid3-row',
71582
71583     /**
71584      * @cfg {String} rowBodySelector The selector used to find row bodies internally (defaults to <tt>'div.x-grid3-row'</tt>)
71585      */
71586     rowBodySelector : 'div.x-grid3-row-body',
71587
71588     // private
71589     firstRowCls: 'x-grid3-row-first',
71590     lastRowCls: 'x-grid3-row-last',
71591     rowClsRe: /(?:^|\s+)x-grid3-row-(first|last|alt)(?:\s+|$)/g,
71592     
71593     /**
71594      * @cfg {String} headerMenuOpenCls The CSS class to add to the header cell when its menu is visible. Defaults to 'x-grid3-hd-menu-open'
71595      */
71596     headerMenuOpenCls: 'x-grid3-hd-menu-open',
71597     
71598     /**
71599      * @cfg {String} rowOverCls The CSS class added to each row when it is hovered over. Defaults to 'x-grid3-row-over'
71600      */
71601     rowOverCls: 'x-grid3-row-over',
71602
71603     constructor : function(config) {
71604         Ext.apply(this, config);
71605         
71606         // These events are only used internally by the grid components
71607         this.addEvents(
71608             /**
71609              * @event beforerowremoved
71610              * Internal UI Event. Fired before a row is removed.
71611              * @param {Ext.grid.GridView} view
71612              * @param {Number} rowIndex The index of the row to be removed.
71613              * @param {Ext.data.Record} record The Record to be removed
71614              */
71615             'beforerowremoved',
71616             
71617             /**
71618              * @event beforerowsinserted
71619              * Internal UI Event. Fired before rows are inserted.
71620              * @param {Ext.grid.GridView} view
71621              * @param {Number} firstRow The index of the first row to be inserted.
71622              * @param {Number} lastRow The index of the last row to be inserted.
71623              */
71624             'beforerowsinserted',
71625             
71626             /**
71627              * @event beforerefresh
71628              * Internal UI Event. Fired before the view is refreshed.
71629              * @param {Ext.grid.GridView} view
71630              */
71631             'beforerefresh',
71632             
71633             /**
71634              * @event rowremoved
71635              * Internal UI Event. Fired after a row is removed.
71636              * @param {Ext.grid.GridView} view
71637              * @param {Number} rowIndex The index of the row that was removed.
71638              * @param {Ext.data.Record} record The Record that was removed
71639              */
71640             'rowremoved',
71641             
71642             /**
71643              * @event rowsinserted
71644              * Internal UI Event. Fired after rows are inserted.
71645              * @param {Ext.grid.GridView} view
71646              * @param {Number} firstRow The index of the first inserted.
71647              * @param {Number} lastRow The index of the last row inserted.
71648              */
71649             'rowsinserted',
71650             
71651             /**
71652              * @event rowupdated
71653              * Internal UI Event. Fired after a row has been updated.
71654              * @param {Ext.grid.GridView} view
71655              * @param {Number} firstRow The index of the row updated.
71656              * @param {Ext.data.record} record The Record backing the row updated.
71657              */
71658             'rowupdated',
71659             
71660             /**
71661              * @event refresh
71662              * Internal UI Event. Fired after the GridView's body has been refreshed.
71663              * @param {Ext.grid.GridView} view
71664              */
71665             'refresh'
71666         );
71667         
71668         Ext.grid.GridView.superclass.constructor.call(this);
71669     },
71670
71671     /* -------------------------------- UI Specific ----------------------------- */
71672     
71673     /**
71674      * The master template to use when rendering the GridView. Has a default template
71675      * @property Ext.Template
71676      * @type masterTpl
71677      */
71678     masterTpl: new Ext.Template(
71679         '<div class="x-grid3" hidefocus="true">',
71680             '<div class="x-grid3-viewport">',
71681                 '<div class="x-grid3-header">',
71682                     '<div class="x-grid3-header-inner">',
71683                         '<div class="x-grid3-header-offset" style="{ostyle}">{header}</div>',
71684                     '</div>',
71685                     '<div class="x-clear"></div>',
71686                 '</div>',
71687                 '<div class="x-grid3-scroller">',
71688                     '<div class="x-grid3-body" style="{bstyle}">{body}</div>',
71689                     '<a href="#" class="x-grid3-focus" tabIndex="-1"></a>',
71690                 '</div>',
71691             '</div>',
71692             '<div class="x-grid3-resize-marker">&#160;</div>',
71693             '<div class="x-grid3-resize-proxy">&#160;</div>',
71694         '</div>'
71695     ),
71696     
71697     /**
71698      * The template to use when rendering headers. Has a default template
71699      * @property headerTpl
71700      * @type Ext.Template
71701      */
71702     headerTpl: new Ext.Template(
71703         '<table border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',
71704             '<thead>',
71705                 '<tr class="x-grid3-hd-row">{cells}</tr>',
71706             '</thead>',
71707         '</table>'
71708     ),
71709     
71710     /**
71711      * The template to use when rendering the body. Has a default template
71712      * @property bodyTpl
71713      * @type Ext.Template
71714      */
71715     bodyTpl: new Ext.Template('{rows}'),
71716     
71717     /**
71718      * The template to use to render each cell. Has a default template
71719      * @property cellTpl
71720      * @type Ext.Template
71721      */
71722     cellTpl: new Ext.Template(
71723         '<td class="x-grid3-col x-grid3-cell x-grid3-td-{id} {css}" style="{style}" tabIndex="0" {cellAttr}>',
71724             '<div class="x-grid3-cell-inner x-grid3-col-{id}" unselectable="on" {attr}>{value}</div>',
71725         '</td>'
71726     ),
71727     
71728     /**
71729      * @private
71730      * Provides default templates if they are not given for this particular instance. Most of the templates are defined on
71731      * the prototype, the ones defined inside this function are done so because they are based on Grid or GridView configuration
71732      */
71733     initTemplates : function() {
71734         var templates = this.templates || {},
71735             template, name,
71736             
71737             headerCellTpl = new Ext.Template(
71738                 '<td class="x-grid3-hd x-grid3-cell x-grid3-td-{id} {css}" style="{style}">',
71739                     '<div {tooltip} {attr} class="x-grid3-hd-inner x-grid3-hd-{id}" unselectable="on" style="{istyle}">', 
71740                         this.grid.enableHdMenu ? '<a class="x-grid3-hd-btn" href="#"></a>' : '',
71741                         '{value}',
71742                         '<img alt="" class="x-grid3-sort-icon" src="', Ext.BLANK_IMAGE_URL, '" />',
71743                     '</div>',
71744                 '</td>'
71745             ),
71746         
71747             rowBodyText = [
71748                 '<tr class="x-grid3-row-body-tr" style="{bodyStyle}">',
71749                     '<td colspan="{cols}" class="x-grid3-body-cell" tabIndex="0" hidefocus="on">',
71750                         '<div class="x-grid3-row-body">{body}</div>',
71751                     '</td>',
71752                 '</tr>'
71753             ].join(""),
71754         
71755             innerText = [
71756                 '<table class="x-grid3-row-table" border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',
71757                      '<tbody>',
71758                         '<tr>{cells}</tr>',
71759                         this.enableRowBody ? rowBodyText : '',
71760                      '</tbody>',
71761                 '</table>'
71762             ].join("");
71763         
71764         Ext.applyIf(templates, {
71765             hcell   : headerCellTpl,
71766             cell    : this.cellTpl,
71767             body    : this.bodyTpl,
71768             header  : this.headerTpl,
71769             master  : this.masterTpl,
71770             row     : new Ext.Template('<div class="x-grid3-row {alt}" style="{tstyle}">' + innerText + '</div>'),
71771             rowInner: new Ext.Template(innerText)
71772         });
71773
71774         for (name in templates) {
71775             template = templates[name];
71776             
71777             if (template && Ext.isFunction(template.compile) && !template.compiled) {
71778                 template.disableFormats = true;
71779                 template.compile();
71780             }
71781         }
71782
71783         this.templates = templates;
71784         this.colRe = new RegExp('x-grid3-td-([^\\s]+)', '');
71785     },
71786
71787     /**
71788      * @private
71789      * Each GridView has its own private flyweight, accessed through this method
71790      */
71791     fly : function(el) {
71792         if (!this._flyweight) {
71793             this._flyweight = new Ext.Element.Flyweight(document.body);
71794         }
71795         this._flyweight.dom = el;
71796         return this._flyweight;
71797     },
71798
71799     // private
71800     getEditorParent : function() {
71801         return this.scroller.dom;
71802     },
71803
71804     /**
71805      * @private
71806      * Finds and stores references to important elements
71807      */
71808     initElements : function() {
71809         var Element  = Ext.Element,
71810             el       = Ext.get(this.grid.getGridEl().dom.firstChild),
71811             mainWrap = new Element(el.child('div.x-grid3-viewport')),
71812             mainHd   = new Element(mainWrap.child('div.x-grid3-header')),
71813             scroller = new Element(mainWrap.child('div.x-grid3-scroller'));
71814         
71815         if (this.grid.hideHeaders) {
71816             mainHd.setDisplayed(false);
71817         }
71818         
71819         if (this.forceFit) {
71820             scroller.setStyle('overflow-x', 'hidden');
71821         }
71822         
71823         /**
71824          * <i>Read-only</i>. The GridView's body Element which encapsulates all rows in the Grid.
71825          * This {@link Ext.Element Element} is only available after the GridPanel has been rendered.
71826          * @type Ext.Element
71827          * @property mainBody
71828          */
71829         
71830         Ext.apply(this, {
71831             el      : el,
71832             mainWrap: mainWrap,
71833             scroller: scroller,
71834             mainHd  : mainHd,
71835             innerHd : mainHd.child('div.x-grid3-header-inner').dom,
71836             mainBody: new Element(Element.fly(scroller).child('div.x-grid3-body')),
71837             focusEl : new Element(Element.fly(scroller).child('a')),
71838             
71839             resizeMarker: new Element(el.child('div.x-grid3-resize-marker')),
71840             resizeProxy : new Element(el.child('div.x-grid3-resize-proxy'))
71841         });
71842         
71843         this.focusEl.swallowEvent('click', true);
71844     },
71845
71846     // private
71847     getRows : function() {
71848         return this.hasRows() ? this.mainBody.dom.childNodes : [];
71849     },
71850
71851     // finder methods, used with delegation
71852
71853     // private
71854     findCell : function(el) {
71855         if (!el) {
71856             return false;
71857         }
71858         return this.fly(el).findParent(this.cellSelector, this.cellSelectorDepth);
71859     },
71860
71861     /**
71862      * <p>Return the index of the grid column which contains the passed HTMLElement.</p>
71863      * See also {@link #findRowIndex}
71864      * @param {HTMLElement} el The target element
71865      * @return {Number} The column index, or <b>false</b> if the target element is not within a row of this GridView.
71866      */
71867     findCellIndex : function(el, requiredCls) {
71868         var cell = this.findCell(el),
71869             hasCls;
71870         
71871         if (cell) {
71872             hasCls = this.fly(cell).hasClass(requiredCls);
71873             if (!requiredCls || hasCls) {
71874                 return this.getCellIndex(cell);
71875             }
71876         }
71877         return false;
71878     },
71879
71880     // private
71881     getCellIndex : function(el) {
71882         if (el) {
71883             var match = el.className.match(this.colRe);
71884             
71885             if (match && match[1]) {
71886                 return this.cm.getIndexById(match[1]);
71887             }
71888         }
71889         return false;
71890     },
71891
71892     // private
71893     findHeaderCell : function(el) {
71894         var cell = this.findCell(el);
71895         return cell && this.fly(cell).hasClass(this.hdCls) ? cell : null;
71896     },
71897
71898     // private
71899     findHeaderIndex : function(el){
71900         return this.findCellIndex(el, this.hdCls);
71901     },
71902
71903     /**
71904      * Return the HtmlElement representing the grid row which contains the passed element.
71905      * @param {HTMLElement} el The target HTMLElement
71906      * @return {HTMLElement} The row element, or null if the target element is not within a row of this GridView.
71907      */
71908     findRow : function(el) {
71909         if (!el) {
71910             return false;
71911         }
71912         return this.fly(el).findParent(this.rowSelector, this.rowSelectorDepth);
71913     },
71914
71915     /**
71916      * Return the index of the grid row which contains the passed HTMLElement.
71917      * See also {@link #findCellIndex}
71918      * @param {HTMLElement} el The target HTMLElement
71919      * @return {Number} The row index, or <b>false</b> if the target element is not within a row of this GridView.
71920      */
71921     findRowIndex : function(el) {
71922         var row = this.findRow(el);
71923         return row ? row.rowIndex : false;
71924     },
71925
71926     /**
71927      * Return the HtmlElement representing the grid row body which contains the passed element.
71928      * @param {HTMLElement} el The target HTMLElement
71929      * @return {HTMLElement} The row body element, or null if the target element is not within a row body of this GridView.
71930      */
71931     findRowBody : function(el) {
71932         if (!el) {
71933             return false;
71934         }
71935         
71936         return this.fly(el).findParent(this.rowBodySelector, this.rowBodySelectorDepth);
71937     },
71938
71939     // getter methods for fetching elements dynamically in the grid
71940
71941     /**
71942      * Return the <tt>&lt;div></tt> HtmlElement which represents a Grid row for the specified index.
71943      * @param {Number} index The row index
71944      * @return {HtmlElement} The div element.
71945      */
71946     getRow : function(row) {
71947         return this.getRows()[row];
71948     },
71949
71950     /**
71951      * Returns the grid's <tt>&lt;td></tt> HtmlElement at the specified coordinates.
71952      * @param {Number} row The row index in which to find the cell.
71953      * @param {Number} col The column index of the cell.
71954      * @return {HtmlElement} The td at the specified coordinates.
71955      */
71956     getCell : function(row, col) {
71957         return Ext.fly(this.getRow(row)).query(this.cellSelector)[col]; 
71958     },
71959
71960     /**
71961      * Return the <tt>&lt;td></tt> HtmlElement which represents the Grid's header cell for the specified column index.
71962      * @param {Number} index The column index
71963      * @return {HtmlElement} The td element.
71964      */
71965     getHeaderCell : function(index) {
71966         return this.mainHd.dom.getElementsByTagName('td')[index];
71967     },
71968
71969     // manipulating elements
71970
71971     // private - use getRowClass to apply custom row classes
71972     addRowClass : function(rowId, cls) {
71973         var row = this.getRow(rowId);
71974         if (row) {
71975             this.fly(row).addClass(cls);
71976         }
71977     },
71978
71979     // private
71980     removeRowClass : function(row, cls) {
71981         var r = this.getRow(row);
71982         if(r){
71983             this.fly(r).removeClass(cls);
71984         }
71985     },
71986
71987     // private
71988     removeRow : function(row) {
71989         Ext.removeNode(this.getRow(row));
71990         this.syncFocusEl(row);
71991     },
71992
71993     // private
71994     removeRows : function(firstRow, lastRow) {
71995         var bd = this.mainBody.dom,
71996             rowIndex;
71997             
71998         for (rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
71999             Ext.removeNode(bd.childNodes[firstRow]);
72000         }
72001         
72002         this.syncFocusEl(firstRow);
72003     },
72004
72005     /* ----------------------------------- Scrolling functions -------------------------------------------*/
72006     
72007     // private
72008     getScrollState : function() {
72009         var sb = this.scroller.dom;
72010         
72011         return {
72012             left: sb.scrollLeft, 
72013             top : sb.scrollTop
72014         };
72015     },
72016
72017     // private
72018     restoreScroll : function(state) {
72019         var sb = this.scroller.dom;
72020         sb.scrollLeft = state.left;
72021         sb.scrollTop  = state.top;
72022     },
72023
72024     /**
72025      * Scrolls the grid to the top
72026      */
72027     scrollToTop : function() {
72028         var dom = this.scroller.dom;
72029         
72030         dom.scrollTop  = 0;
72031         dom.scrollLeft = 0;
72032     },
72033
72034     // private
72035     syncScroll : function() {
72036         this.syncHeaderScroll();
72037         var mb = this.scroller.dom;
72038         this.grid.fireEvent('bodyscroll', mb.scrollLeft, mb.scrollTop);
72039     },
72040
72041     // private
72042     syncHeaderScroll : function() {
72043         var innerHd    = this.innerHd,
72044             scrollLeft = this.scroller.dom.scrollLeft;
72045         
72046         innerHd.scrollLeft = scrollLeft;
72047         innerHd.scrollLeft = scrollLeft; // second time for IE (1/2 time first fails, other browsers ignore)
72048     },
72049     
72050     /**
72051      * @private
72052      * Ensures the given column has the given icon class
72053      */
72054     updateSortIcon : function(col, dir) {
72055         var sortClasses = this.sortClasses,
72056             sortClass   = sortClasses[dir == "DESC" ? 1 : 0],
72057             headers     = this.mainHd.select('td').removeClass(sortClasses);
72058         
72059         headers.item(col).addClass(sortClass);
72060     },
72061
72062     /**
72063      * @private
72064      * Updates the size of every column and cell in the grid
72065      */
72066     updateAllColumnWidths : function() {
72067         var totalWidth = this.getTotalWidth(),
72068             colCount   = this.cm.getColumnCount(),
72069             rows       = this.getRows(),
72070             rowCount   = rows.length,
72071             widths     = [],
72072             row, rowFirstChild, trow, i, j;
72073         
72074         for (i = 0; i < colCount; i++) {
72075             widths[i] = this.getColumnWidth(i);
72076             this.getHeaderCell(i).style.width = widths[i];
72077         }
72078         
72079         this.updateHeaderWidth();
72080         
72081         for (i = 0; i < rowCount; i++) {
72082             row = rows[i];
72083             row.style.width = totalWidth;
72084             rowFirstChild = row.firstChild;
72085             
72086             if (rowFirstChild) {
72087                 rowFirstChild.style.width = totalWidth;
72088                 trow = rowFirstChild.rows[0];
72089                 
72090                 for (j = 0; j < colCount; j++) {
72091                     trow.childNodes[j].style.width = widths[j];
72092                 }
72093             }
72094         }
72095         
72096         this.onAllColumnWidthsUpdated(widths, totalWidth);
72097     },
72098
72099     /**
72100      * @private
72101      * Called after a column's width has been updated, this resizes all of the cells for that column in each row
72102      * @param {Number} column The column index
72103      */
72104     updateColumnWidth : function(column, width) {
72105         var columnWidth = this.getColumnWidth(column),
72106             totalWidth  = this.getTotalWidth(),
72107             headerCell  = this.getHeaderCell(column),
72108             nodes       = this.getRows(),
72109             nodeCount   = nodes.length,
72110             row, i, firstChild;
72111         
72112         this.updateHeaderWidth();
72113         headerCell.style.width = columnWidth;
72114         
72115         for (i = 0; i < nodeCount; i++) {
72116             row = nodes[i];
72117             firstChild = row.firstChild;
72118             
72119             row.style.width = totalWidth;
72120             if (firstChild) {
72121                 firstChild.style.width = totalWidth;
72122                 firstChild.rows[0].childNodes[column].style.width = columnWidth;
72123             }
72124         }
72125         
72126         this.onColumnWidthUpdated(column, columnWidth, totalWidth);
72127     },
72128     
72129     /**
72130      * @private
72131      * Sets the hidden status of a given column.
72132      * @param {Number} col The column index
72133      * @param {Boolean} hidden True to make the column hidden
72134      */
72135     updateColumnHidden : function(col, hidden) {
72136         var totalWidth = this.getTotalWidth(),
72137             display    = hidden ? 'none' : '',
72138             headerCell = this.getHeaderCell(col),
72139             nodes      = this.getRows(),
72140             nodeCount  = nodes.length,
72141             row, rowFirstChild, i;
72142         
72143         this.updateHeaderWidth();
72144         headerCell.style.display = display;
72145         
72146         for (i = 0; i < nodeCount; i++) {
72147             row = nodes[i];
72148             row.style.width = totalWidth;
72149             rowFirstChild = row.firstChild;
72150             
72151             if (rowFirstChild) {
72152                 rowFirstChild.style.width = totalWidth;
72153                 rowFirstChild.rows[0].childNodes[col].style.display = display;
72154             }
72155         }
72156         
72157         this.onColumnHiddenUpdated(col, hidden, totalWidth);
72158         delete this.lastViewWidth; //recalc
72159         this.layout();
72160     },
72161
72162     /**
72163      * @private
72164      * Renders all of the rows to a string buffer and returns the string. This is called internally
72165      * by renderRows and performs the actual string building for the rows - it does not inject HTML into the DOM.
72166      * @param {Array} columns The column data acquired from getColumnData.
72167      * @param {Array} records The array of records to render
72168      * @param {Ext.data.Store} store The store to render the rows from
72169      * @param {Number} startRow The index of the first row being rendered. Sometimes we only render a subset of
72170      * the rows so this is used to maintain logic for striping etc
72171      * @param {Number} colCount The total number of columns in the column model
72172      * @param {Boolean} stripe True to stripe the rows
72173      * @return {String} A string containing the HTML for the rendered rows
72174      */
72175     doRender : function(columns, records, store, startRow, colCount, stripe) {
72176         var templates = this.templates,
72177             cellTemplate = templates.cell,
72178             rowTemplate = templates.row,
72179             last = colCount - 1,
72180             tstyle = 'width:' + this.getTotalWidth() + ';',
72181             // buffers
72182             rowBuffer = [],
72183             colBuffer = [],
72184             rowParams = {tstyle: tstyle},
72185             meta = {},
72186             len  = records.length,
72187             alt,
72188             column,
72189             record, i, j, rowIndex;
72190
72191         //build up each row's HTML
72192         for (j = 0; j < len; j++) {
72193             record    = records[j];
72194             colBuffer = [];
72195
72196             rowIndex = j + startRow;
72197
72198             //build up each column's HTML
72199             for (i = 0; i < colCount; i++) {
72200                 column = columns[i];
72201                 
72202                 meta.id    = column.id;
72203                 meta.css   = i === 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '');
72204                 meta.attr  = meta.cellAttr = '';
72205                 meta.style = column.style;
72206                 meta.value = column.renderer.call(column.scope, record.data[column.name], meta, record, rowIndex, i, store);
72207
72208                 if (Ext.isEmpty(meta.value)) {
72209                     meta.value = '&#160;';
72210                 }
72211
72212                 if (this.markDirty && record.dirty && typeof record.modified[column.name] != 'undefined') {
72213                     meta.css += ' x-grid3-dirty-cell';
72214                 }
72215
72216                 colBuffer[colBuffer.length] = cellTemplate.apply(meta);
72217             }
72218
72219             alt = [];
72220             //set up row striping and row dirtiness CSS classes
72221             if (stripe && ((rowIndex + 1) % 2 === 0)) {
72222                 alt[0] = 'x-grid3-row-alt';
72223             }
72224
72225             if (record.dirty) {
72226                 alt[1] = ' x-grid3-dirty-row';
72227             }
72228
72229             rowParams.cols = colCount;
72230
72231             if (this.getRowClass) {
72232                 alt[2] = this.getRowClass(record, rowIndex, rowParams, store);
72233             }
72234
72235             rowParams.alt   = alt.join(' ');
72236             rowParams.cells = colBuffer.join('');
72237
72238             rowBuffer[rowBuffer.length] = rowTemplate.apply(rowParams);
72239         }
72240
72241         return rowBuffer.join('');
72242     },
72243
72244     /**
72245      * @private
72246      * Adds CSS classes and rowIndex to each row
72247      * @param {Number} startRow The row to start from (defaults to 0)
72248      */
72249     processRows : function(startRow, skipStripe) {
72250         if (!this.ds || this.ds.getCount() < 1) {
72251             return;
72252         }
72253
72254         var rows   = this.getRows(),
72255             length = rows.length,
72256             row, i;
72257
72258         skipStripe = skipStripe || !this.grid.stripeRows;
72259         startRow   = startRow   || 0;
72260
72261         for (i = 0; i < length; i++) {
72262             row = rows[i];
72263             if (row) {
72264                 row.rowIndex = i;
72265                 if (!skipStripe) {
72266                     row.className = row.className.replace(this.rowClsRe, ' ');
72267                     if ((i + 1) % 2 === 0){
72268                         row.className += ' x-grid3-row-alt';
72269                     }
72270                 }
72271             }
72272         }
72273
72274         // add first/last-row classes
72275         if (startRow === 0) {
72276             Ext.fly(rows[0]).addClass(this.firstRowCls);
72277         }
72278
72279         Ext.fly(rows[length - 1]).addClass(this.lastRowCls);
72280     },
72281     
72282     /**
72283      * @private
72284      */
72285     afterRender : function() {
72286         if (!this.ds || !this.cm) {
72287             return;
72288         }
72289         
72290         this.mainBody.dom.innerHTML = this.renderBody() || '&#160;';
72291         this.processRows(0, true);
72292
72293         if (this.deferEmptyText !== true) {
72294             this.applyEmptyText();
72295         }
72296         
72297         this.grid.fireEvent('viewready', this.grid);
72298     },
72299     
72300     /**
72301      * @private
72302      * This is always intended to be called after renderUI. Sets up listeners on the UI elements
72303      * and sets up options like column menus, moving and resizing.
72304      */
72305     afterRenderUI: function() {
72306         var grid = this.grid;
72307         
72308         this.initElements();
72309
72310         // get mousedowns early
72311         Ext.fly(this.innerHd).on('click', this.handleHdDown, this);
72312
72313         this.mainHd.on({
72314             scope    : this,
72315             mouseover: this.handleHdOver,
72316             mouseout : this.handleHdOut,
72317             mousemove: this.handleHdMove
72318         });
72319
72320         this.scroller.on('scroll', this.syncScroll,  this);
72321         
72322         if (grid.enableColumnResize !== false) {
72323             this.splitZone = new Ext.grid.GridView.SplitDragZone(grid, this.mainHd.dom);
72324         }
72325
72326         if (grid.enableColumnMove) {
72327             this.columnDrag = new Ext.grid.GridView.ColumnDragZone(grid, this.innerHd);
72328             this.columnDrop = new Ext.grid.HeaderDropZone(grid, this.mainHd.dom);
72329         }
72330
72331         if (grid.enableHdMenu !== false) {
72332             this.hmenu = new Ext.menu.Menu({id: grid.id + '-hctx'});
72333             this.hmenu.add(
72334                 {itemId:'asc',  text: this.sortAscText,  cls: 'xg-hmenu-sort-asc'},
72335                 {itemId:'desc', text: this.sortDescText, cls: 'xg-hmenu-sort-desc'}
72336             );
72337
72338             if (grid.enableColumnHide !== false) {
72339                 this.colMenu = new Ext.menu.Menu({id:grid.id + '-hcols-menu'});
72340                 this.colMenu.on({
72341                     scope     : this,
72342                     beforeshow: this.beforeColMenuShow,
72343                     itemclick : this.handleHdMenuClick
72344                 });
72345                 this.hmenu.add('-', {
72346                     itemId:'columns',
72347                     hideOnClick: false,
72348                     text: this.columnsText,
72349                     menu: this.colMenu,
72350                     iconCls: 'x-cols-icon'
72351                 });
72352             }
72353
72354             this.hmenu.on('itemclick', this.handleHdMenuClick, this);
72355         }
72356
72357         if (grid.trackMouseOver) {
72358             this.mainBody.on({
72359                 scope    : this,
72360                 mouseover: this.onRowOver,
72361                 mouseout : this.onRowOut
72362             });
72363         }
72364
72365         if (grid.enableDragDrop || grid.enableDrag) {
72366             this.dragZone = new Ext.grid.GridDragZone(grid, {
72367                 ddGroup : grid.ddGroup || 'GridDD'
72368             });
72369         }
72370
72371         this.updateHeaderSortState();
72372     },
72373
72374     /**
72375      * @private
72376      * Renders each of the UI elements in turn. This is called internally, once, by this.render. It does not
72377      * render rows from the store, just the surrounding UI elements.
72378      */
72379     renderUI : function() {
72380         var templates = this.templates;
72381
72382         return templates.master.apply({
72383             body  : templates.body.apply({rows:'&#160;'}),
72384             header: this.renderHeaders(),
72385             ostyle: 'width:' + this.getOffsetWidth() + ';',
72386             bstyle: 'width:' + this.getTotalWidth()  + ';'
72387         });
72388     },
72389
72390     // private
72391     processEvent : function(name, e) {
72392         var target = e.getTarget(),
72393             grid   = this.grid,
72394             header = this.findHeaderIndex(target),
72395             row, cell, col, body;
72396
72397         grid.fireEvent(name, e);
72398
72399         if (header !== false) {
72400             grid.fireEvent('header' + name, grid, header, e);
72401         } else {
72402             row = this.findRowIndex(target);
72403
72404 //          Grid's value-added events must bubble correctly to allow cancelling via returning false: cell->column->row
72405 //          We must allow a return of false at any of these levels to cancel the event processing.
72406 //          Particularly allowing rowmousedown to be cancellable by prior handlers which need to prevent selection.
72407             if (row !== false) {
72408                 cell = this.findCellIndex(target);
72409                 if (cell !== false) {
72410                     col = grid.colModel.getColumnAt(cell);
72411                     if (grid.fireEvent('cell' + name, grid, row, cell, e) !== false) {
72412                         if (!col || (col.processEvent && (col.processEvent(name, e, grid, row, cell) !== false))) {
72413                             grid.fireEvent('row' + name, grid, row, e);
72414                         }
72415                     }
72416                 } else {
72417                     if (grid.fireEvent('row' + name, grid, row, e) !== false) {
72418                         (body = this.findRowBody(target)) && grid.fireEvent('rowbody' + name, grid, row, e);
72419                     }
72420                 }
72421             } else {
72422                 grid.fireEvent('container' + name, grid, e);
72423             }
72424         }
72425     },
72426
72427     /**
72428      * @private
72429      * Sizes the grid's header and body elements
72430      */
72431     layout : function(initial) {
72432         if (!this.mainBody) {
72433             return; // not rendered
72434         }
72435
72436         var grid       = this.grid,
72437             gridEl     = grid.getGridEl(),
72438             gridSize   = gridEl.getSize(true),
72439             gridWidth  = gridSize.width,
72440             gridHeight = gridSize.height,
72441             scroller   = this.scroller,
72442             scrollStyle, headerHeight, scrollHeight;
72443         
72444         if (gridWidth < 20 || gridHeight < 20) {
72445             return;
72446         }
72447         
72448         if (grid.autoHeight) {
72449             scrollStyle = scroller.dom.style;
72450             scrollStyle.overflow = 'visible';
72451             
72452             if (Ext.isWebKit) {
72453                 scrollStyle.position = 'static';
72454             }
72455         } else {
72456             this.el.setSize(gridWidth, gridHeight);
72457             
72458             headerHeight = this.mainHd.getHeight();
72459             scrollHeight = gridHeight - headerHeight;
72460             
72461             scroller.setSize(gridWidth, scrollHeight);
72462             
72463             if (this.innerHd) {
72464                 this.innerHd.style.width = (gridWidth) + "px";
72465             }
72466         }
72467         
72468         if (this.forceFit || (initial === true && this.autoFill)) {
72469             if (this.lastViewWidth != gridWidth) {
72470                 this.fitColumns(false, false);
72471                 this.lastViewWidth = gridWidth;
72472             }
72473         } else {
72474             this.autoExpand();
72475             this.syncHeaderScroll();
72476         }
72477         
72478         this.onLayout(gridWidth, scrollHeight);
72479     },
72480
72481     // template functions for subclasses and plugins
72482     // these functions include precalculated values
72483     onLayout : function(vw, vh) {
72484         // do nothing
72485     },
72486
72487     onColumnWidthUpdated : function(col, w, tw) {
72488         //template method
72489     },
72490
72491     onAllColumnWidthsUpdated : function(ws, tw) {
72492         //template method
72493     },
72494
72495     onColumnHiddenUpdated : function(col, hidden, tw) {
72496         // template method
72497     },
72498
72499     updateColumnText : function(col, text) {
72500         // template method
72501     },
72502
72503     afterMove : function(colIndex) {
72504         // template method
72505     },
72506
72507     /* ----------------------------------- Core Specific -------------------------------------------*/
72508     // private
72509     init : function(grid) {
72510         this.grid = grid;
72511
72512         this.initTemplates();
72513         this.initData(grid.store, grid.colModel);
72514         this.initUI(grid);
72515     },
72516
72517     // private
72518     getColumnId : function(index){
72519         return this.cm.getColumnId(index);
72520     },
72521
72522     // private
72523     getOffsetWidth : function() {
72524         return (this.cm.getTotalWidth() + this.getScrollOffset()) + 'px';
72525     },
72526
72527     // private
72528     getScrollOffset: function() {
72529         return Ext.num(this.scrollOffset, Ext.getScrollBarWidth());
72530     },
72531
72532     /**
72533      * @private
72534      * Renders the header row using the 'header' template. Does not inject the HTML into the DOM, just
72535      * returns a string.
72536      * @return {String} Rendered header row
72537      */
72538     renderHeaders : function() {
72539         var colModel   = this.cm,
72540             templates  = this.templates,
72541             headerTpl  = templates.hcell,
72542             properties = {},
72543             colCount   = colModel.getColumnCount(),
72544             last       = colCount - 1,
72545             cells      = [],
72546             i, cssCls;
72547         
72548         for (i = 0; i < colCount; i++) {
72549             if (i == 0) {
72550                 cssCls = 'x-grid3-cell-first ';
72551             } else {
72552                 cssCls = i == last ? 'x-grid3-cell-last ' : '';
72553             }
72554             
72555             properties = {
72556                 id     : colModel.getColumnId(i),
72557                 value  : colModel.getColumnHeader(i) || '',
72558                 style  : this.getColumnStyle(i, true),
72559                 css    : cssCls,
72560                 tooltip: this.getColumnTooltip(i)
72561             };
72562             
72563             if (colModel.config[i].align == 'right') {
72564                 properties.istyle = 'padding-right: 16px;';
72565             } else {
72566                 delete properties.istyle;
72567             }
72568             
72569             cells[i] = headerTpl.apply(properties);
72570         }
72571         
72572         return templates.header.apply({
72573             cells : cells.join(""),
72574             tstyle: String.format("width: {0};", this.getTotalWidth())
72575         });
72576     },
72577
72578     /**
72579      * @private
72580      */
72581     getColumnTooltip : function(i) {
72582         var tooltip = this.cm.getColumnTooltip(i);
72583         if (tooltip) {
72584             if (Ext.QuickTips.isEnabled()) {
72585                 return 'ext:qtip="' + tooltip + '"';
72586             } else {
72587                 return 'title="' + tooltip + '"';
72588             }
72589         }
72590         
72591         return '';
72592     },
72593
72594     // private
72595     beforeUpdate : function() {
72596         this.grid.stopEditing(true);
72597     },
72598
72599     /**
72600      * @private
72601      * Re-renders the headers and ensures they are sized correctly
72602      */
72603     updateHeaders : function() {
72604         this.innerHd.firstChild.innerHTML = this.renderHeaders();
72605         
72606         this.updateHeaderWidth(false);
72607     },
72608     
72609     /**
72610      * @private
72611      * Ensures that the header is sized to the total width available to it
72612      * @param {Boolean} updateMain True to update the mainBody's width also (defaults to true)
72613      */
72614     updateHeaderWidth: function(updateMain) {
72615         var innerHdChild = this.innerHd.firstChild,
72616             totalWidth   = this.getTotalWidth();
72617         
72618         innerHdChild.style.width = this.getOffsetWidth();
72619         innerHdChild.firstChild.style.width = totalWidth;
72620         
72621         if (updateMain !== false) {
72622             this.mainBody.dom.style.width = totalWidth;
72623         }
72624     },
72625
72626     /**
72627      * Focuses the specified row.
72628      * @param {Number} row The row index
72629      */
72630     focusRow : function(row) {
72631         this.focusCell(row, 0, false);
72632     },
72633
72634     /**
72635      * Focuses the specified cell.
72636      * @param {Number} row The row index
72637      * @param {Number} col The column index
72638      */
72639     focusCell : function(row, col, hscroll) {
72640         this.syncFocusEl(this.ensureVisible(row, col, hscroll));
72641         
72642         var focusEl = this.focusEl;
72643         
72644         if (Ext.isGecko) {
72645             focusEl.focus();
72646         } else {
72647             focusEl.focus.defer(1, focusEl);
72648         }
72649     },
72650
72651     /**
72652      * @private
72653      * Finds the Elements corresponding to the given row and column indexes
72654      */
72655     resolveCell : function(row, col, hscroll) {
72656         if (!Ext.isNumber(row)) {
72657             row = row.rowIndex;
72658         }
72659         
72660         if (!this.ds) {
72661             return null;
72662         }
72663         
72664         if (row < 0 || row >= this.ds.getCount()) {
72665             return null;
72666         }
72667         col = (col !== undefined ? col : 0);
72668
72669         var rowEl    = this.getRow(row),
72670             colModel = this.cm,
72671             colCount = colModel.getColumnCount(),
72672             cellEl;
72673             
72674         if (!(hscroll === false && col === 0)) {
72675             while (col < colCount && colModel.isHidden(col)) {
72676                 col++;
72677             }
72678             
72679             cellEl = this.getCell(row, col);
72680         }
72681
72682         return {row: rowEl, cell: cellEl};
72683     },
72684
72685     /**
72686      * @private
72687      * Returns the XY co-ordinates of a given row/cell resolution (see {@link #resolveCell})
72688      * @return {Array} X and Y coords
72689      */
72690     getResolvedXY : function(resolved) {
72691         if (!resolved) {
72692             return null;
72693         }
72694         
72695         var cell = resolved.cell,
72696             row  = resolved.row;
72697         
72698         if (cell) {
72699             return Ext.fly(cell).getXY();
72700         } else {
72701             return [this.el.getX(), Ext.fly(row).getY()];
72702         }
72703     },
72704
72705     /**
72706      * @private
72707      * Moves the focus element to the x and y co-ordinates of the given row and column
72708      */
72709     syncFocusEl : function(row, col, hscroll) {
72710         var xy = row;
72711         
72712         if (!Ext.isArray(xy)) {
72713             row = Math.min(row, Math.max(0, this.getRows().length-1));
72714             
72715             if (isNaN(row)) {
72716                 return;
72717             }
72718             
72719             xy = this.getResolvedXY(this.resolveCell(row, col, hscroll));
72720         }
72721         
72722         this.focusEl.setXY(xy || this.scroller.getXY());
72723     },
72724
72725     /**
72726      * @private
72727      */
72728     ensureVisible : function(row, col, hscroll) {
72729         var resolved = this.resolveCell(row, col, hscroll);
72730         
72731         if (!resolved || !resolved.row) {
72732             return null;
72733         }
72734
72735         var rowEl  = resolved.row,
72736             cellEl = resolved.cell,
72737             c = this.scroller.dom,
72738             p = rowEl,
72739             ctop = 0,
72740             stop = this.el.dom;
72741
72742         while (p && p != stop) {
72743             ctop += p.offsetTop;
72744             p = p.offsetParent;
72745         }
72746
72747         ctop -= this.mainHd.dom.offsetHeight;
72748         stop = parseInt(c.scrollTop, 10);
72749
72750         var cbot = ctop + rowEl.offsetHeight,
72751             ch = c.clientHeight,
72752             sbot = stop + ch;
72753
72754
72755         if (ctop < stop) {
72756           c.scrollTop = ctop;
72757         } else if(cbot > sbot) {
72758             c.scrollTop = cbot-ch;
72759         }
72760
72761         if (hscroll !== false) {
72762             var cleft  = parseInt(cellEl.offsetLeft, 10),
72763                 cright = cleft + cellEl.offsetWidth,
72764                 sleft  = parseInt(c.scrollLeft, 10),
72765                 sright = sleft + c.clientWidth;
72766                 
72767             if (cleft < sleft) {
72768                 c.scrollLeft = cleft;
72769             } else if(cright > sright) {
72770                 c.scrollLeft = cright-c.clientWidth;
72771             }
72772         }
72773         
72774         return this.getResolvedXY(resolved);
72775     },
72776
72777     // private
72778     insertRows : function(dm, firstRow, lastRow, isUpdate) {
72779         var last = dm.getCount() - 1;
72780         if( !isUpdate && firstRow === 0 && lastRow >= last) {
72781             this.fireEvent('beforerowsinserted', this, firstRow, lastRow);
72782                 this.refresh();
72783             this.fireEvent('rowsinserted', this, firstRow, lastRow);
72784         } else {
72785             if (!isUpdate) {
72786                 this.fireEvent('beforerowsinserted', this, firstRow, lastRow);
72787             }
72788             var html = this.renderRows(firstRow, lastRow),
72789                 before = this.getRow(firstRow);
72790             if (before) {
72791                 if(firstRow === 0){
72792                     Ext.fly(this.getRow(0)).removeClass(this.firstRowCls);
72793                 }
72794                 Ext.DomHelper.insertHtml('beforeBegin', before, html);
72795             } else {
72796                 var r = this.getRow(last - 1);
72797                 if(r){
72798                     Ext.fly(r).removeClass(this.lastRowCls);
72799                 }
72800                 Ext.DomHelper.insertHtml('beforeEnd', this.mainBody.dom, html);
72801             }
72802             if (!isUpdate) {
72803                 this.processRows(firstRow);
72804                 this.fireEvent('rowsinserted', this, firstRow, lastRow);
72805             } else if (firstRow === 0 || firstRow >= last) {
72806                 //ensure first/last row is kept after an update.
72807                 Ext.fly(this.getRow(firstRow)).addClass(firstRow === 0 ? this.firstRowCls : this.lastRowCls);
72808             }
72809         }
72810         this.syncFocusEl(firstRow);
72811     },
72812
72813     /**
72814      * @private
72815      * DEPRECATED - this doesn't appear to be called anywhere in the library, remove in 4.0. 
72816      */
72817     deleteRows : function(dm, firstRow, lastRow) {
72818         if (dm.getRowCount() < 1) {
72819             this.refresh();
72820         } else {
72821             this.fireEvent('beforerowsdeleted', this, firstRow, lastRow);
72822
72823             this.removeRows(firstRow, lastRow);
72824
72825             this.processRows(firstRow);
72826             this.fireEvent('rowsdeleted', this, firstRow, lastRow);
72827         }
72828     },
72829
72830     /**
72831      * @private
72832      * Builds a CSS string for the given column index
72833      * @param {Number} colIndex The column index
72834      * @param {Boolean} isHeader True if getting the style for the column's header
72835      * @return {String} The CSS string
72836      */
72837     getColumnStyle : function(colIndex, isHeader) {
72838         var colModel  = this.cm,
72839             colConfig = colModel.config,
72840             style     = isHeader ? '' : colConfig[colIndex].css || '',
72841             align     = colConfig[colIndex].align;
72842         
72843         style += String.format("width: {0};", this.getColumnWidth(colIndex));
72844         
72845         if (colModel.isHidden(colIndex)) {
72846             style += 'display: none; ';
72847         }
72848         
72849         if (align) {
72850             style += String.format("text-align: {0};", align);
72851         }
72852         
72853         return style;
72854     },
72855
72856     /**
72857      * @private
72858      * Returns the width of a given column minus its border width
72859      * @return {Number} The column index
72860      * @return {String|Number} The width in pixels
72861      */
72862     getColumnWidth : function(column) {
72863         var columnWidth = this.cm.getColumnWidth(column),
72864             borderWidth = this.borderWidth;
72865         
72866         if (Ext.isNumber(columnWidth)) {
72867             if (Ext.isBorderBox || (Ext.isWebKit && !Ext.isSafari2)) {
72868                 return columnWidth + "px";
72869             } else {
72870                 return Math.max(columnWidth - borderWidth, 0) + "px";
72871             }
72872         } else {
72873             return columnWidth;
72874         }
72875     },
72876
72877     /**
72878      * @private
72879      * Returns the total width of all visible columns
72880      * @return {String} 
72881      */
72882     getTotalWidth : function() {
72883         return this.cm.getTotalWidth() + 'px';
72884     },
72885
72886     /**
72887      * @private
72888      * Resizes each column to fit the available grid width.
72889      * TODO: The second argument isn't even used, remove it in 4.0
72890      * @param {Boolean} preventRefresh True to prevent resizing of each row to the new column sizes (defaults to false)
72891      * @param {null} onlyExpand NOT USED, will be removed in 4.0
72892      * @param {Number} omitColumn The index of a column to leave at its current width. Defaults to undefined
72893      * @return {Boolean} True if the operation succeeded, false if not or undefined if the grid view is not yet initialized
72894      */
72895     fitColumns : function(preventRefresh, onlyExpand, omitColumn) {
72896         var grid          = this.grid,
72897             colModel      = this.cm,
72898             totalColWidth = colModel.getTotalWidth(false),
72899             gridWidth     = this.getGridInnerWidth(),
72900             extraWidth    = gridWidth - totalColWidth,
72901             columns       = [],
72902             extraCol      = 0,
72903             width         = 0,
72904             colWidth, fraction, i;
72905         
72906         // not initialized, so don't screw up the default widths
72907         if (gridWidth < 20 || extraWidth === 0) {
72908             return false;
72909         }
72910         
72911         var visibleColCount = colModel.getColumnCount(true),
72912             totalColCount   = colModel.getColumnCount(false),
72913             adjCount        = visibleColCount - (Ext.isNumber(omitColumn) ? 1 : 0);
72914         
72915         if (adjCount === 0) {
72916             adjCount = 1;
72917             omitColumn = undefined;
72918         }
72919         
72920         //FIXME: the algorithm used here is odd and potentially confusing. Includes this for loop and the while after it.
72921         for (i = 0; i < totalColCount; i++) {
72922             if (!colModel.isFixed(i) && i !== omitColumn) {
72923                 colWidth = colModel.getColumnWidth(i);
72924                 columns.push(i, colWidth);
72925                 
72926                 if (!colModel.isHidden(i)) {
72927                     extraCol = i;
72928                     width += colWidth;
72929                 }
72930             }
72931         }
72932         
72933         fraction = (gridWidth - colModel.getTotalWidth()) / width;
72934         
72935         while (columns.length) {
72936             colWidth = columns.pop();
72937             i        = columns.pop();
72938             
72939             colModel.setColumnWidth(i, Math.max(grid.minColumnWidth, Math.floor(colWidth + colWidth * fraction)), true);
72940         }
72941         
72942         //this has been changed above so remeasure now
72943         totalColWidth = colModel.getTotalWidth(false);
72944         
72945         if (totalColWidth > gridWidth) {
72946             var adjustCol = (adjCount == visibleColCount) ? extraCol : omitColumn,
72947                 newWidth  = Math.max(1, colModel.getColumnWidth(adjustCol) - (totalColWidth - gridWidth));
72948             
72949             colModel.setColumnWidth(adjustCol, newWidth, true);
72950         }
72951         
72952         if (preventRefresh !== true) {
72953             this.updateAllColumnWidths();
72954         }
72955         
72956         return true;
72957     },
72958
72959     /**
72960      * @private
72961      * Resizes the configured autoExpandColumn to take the available width after the other columns have 
72962      * been accounted for
72963      * @param {Boolean} preventUpdate True to prevent the resizing of all rows (defaults to false)
72964      */
72965     autoExpand : function(preventUpdate) {
72966         var grid             = this.grid,
72967             colModel         = this.cm,
72968             gridWidth        = this.getGridInnerWidth(),
72969             totalColumnWidth = colModel.getTotalWidth(false),
72970             autoExpandColumn = grid.autoExpandColumn;
72971         
72972         if (!this.userResized && autoExpandColumn) {
72973             if (gridWidth != totalColumnWidth) {
72974                 //if we are not already using all available width, resize the autoExpandColumn
72975                 var colIndex     = colModel.getIndexById(autoExpandColumn),
72976                     currentWidth = colModel.getColumnWidth(colIndex),
72977                     desiredWidth = gridWidth - totalColumnWidth + currentWidth,
72978                     newWidth     = Math.min(Math.max(desiredWidth, grid.autoExpandMin), grid.autoExpandMax);
72979                 
72980                 if (currentWidth != newWidth) {
72981                     colModel.setColumnWidth(colIndex, newWidth, true);
72982                     
72983                     if (preventUpdate !== true) {
72984                         this.updateColumnWidth(colIndex, newWidth);
72985                     }
72986                 }
72987             }
72988         }
72989     },
72990     
72991     /**
72992      * Returns the total internal width available to the grid, taking the scrollbar into account
72993      * @return {Number} The total width
72994      */
72995     getGridInnerWidth: function() {
72996         return this.grid.getGridEl().getWidth(true) - this.getScrollOffset();
72997     },
72998
72999     /**
73000      * @private
73001      * Returns an array of column configurations - one for each column
73002      * @return {Array} Array of column config objects. This includes the column name, renderer, id style and renderer
73003      */
73004     getColumnData : function() {
73005         var columns  = [],
73006             colModel = this.cm,
73007             colCount = colModel.getColumnCount(),
73008             fields   = this.ds.fields,
73009             i, name;
73010         
73011         for (i = 0; i < colCount; i++) {
73012             name = colModel.getDataIndex(i);
73013             
73014             columns[i] = {
73015                 name    : Ext.isDefined(name) ? name : (fields.get(i) ? fields.get(i).name : undefined),
73016                 renderer: colModel.getRenderer(i),
73017                 scope   : colModel.getRendererScope(i),
73018                 id      : colModel.getColumnId(i),
73019                 style   : this.getColumnStyle(i)
73020             };
73021         }
73022         
73023         return columns;
73024     },
73025
73026     /**
73027      * @private
73028      * Renders rows between start and end indexes
73029      * @param {Number} startRow Index of the first row to render
73030      * @param {Number} endRow Index of the last row to render
73031      */
73032     renderRows : function(startRow, endRow) {
73033         var grid     = this.grid,
73034             store    = grid.store,
73035             stripe   = grid.stripeRows,
73036             colModel = grid.colModel,
73037             colCount = colModel.getColumnCount(),
73038             rowCount = store.getCount(),
73039             records;
73040         
73041         if (rowCount < 1) {
73042             return '';
73043         }
73044         
73045         startRow = startRow || 0;
73046         endRow   = Ext.isDefined(endRow) ? endRow : rowCount - 1;
73047         records  = store.getRange(startRow, endRow);
73048         
73049         return this.doRender(this.getColumnData(), records, store, startRow, colCount, stripe);
73050     },
73051
73052     // private
73053     renderBody : function(){
73054         var markup = this.renderRows() || '&#160;';
73055         return this.templates.body.apply({rows: markup});
73056     },
73057
73058     /**
73059      * @private
73060      * Refreshes a row by re-rendering it. Fires the rowupdated event when done
73061      */
73062     refreshRow: function(record) {
73063         var store     = this.ds,
73064             colCount  = this.cm.getColumnCount(),
73065             columns   = this.getColumnData(),
73066             last      = colCount - 1,
73067             cls       = ['x-grid3-row'],
73068             rowParams = {
73069                 tstyle: String.format("width: {0};", this.getTotalWidth())
73070             },
73071             colBuffer = [],
73072             cellTpl   = this.templates.cell,
73073             rowIndex, row, column, meta, css, i;
73074         
73075         if (Ext.isNumber(record)) {
73076             rowIndex = record;
73077             record   = store.getAt(rowIndex);
73078         } else {
73079             rowIndex = store.indexOf(record);
73080         }
73081         
73082         //the record could not be found
73083         if (!record || rowIndex < 0) {
73084             return;
73085         }
73086         
73087         //builds each column in this row
73088         for (i = 0; i < colCount; i++) {
73089             column = columns[i];
73090             
73091             if (i == 0) {
73092                 css = 'x-grid3-cell-first';
73093             } else {
73094                 css = (i == last) ? 'x-grid3-cell-last ' : '';
73095             }
73096             
73097             meta = {
73098                 id      : column.id,
73099                 style   : column.style,
73100                 css     : css,
73101                 attr    : "",
73102                 cellAttr: ""
73103             };
73104             // Need to set this after, because we pass meta to the renderer
73105             meta.value = column.renderer.call(column.scope, record.data[column.name], meta, record, rowIndex, i, store);
73106             
73107             if (Ext.isEmpty(meta.value)) {
73108                 meta.value = '&#160;';
73109             }
73110             
73111             if (this.markDirty && record.dirty && typeof record.modified[column.name] != 'undefined') {
73112                 meta.css += ' x-grid3-dirty-cell';
73113             }
73114             
73115             colBuffer[i] = cellTpl.apply(meta);
73116         }
73117         
73118         row = this.getRow(rowIndex);
73119         row.className = '';
73120         
73121         if (this.grid.stripeRows && ((rowIndex + 1) % 2 === 0)) {
73122             cls.push('x-grid3-row-alt');
73123         }
73124         
73125         if (this.getRowClass) {
73126             rowParams.cols = colCount;
73127             cls.push(this.getRowClass(record, rowIndex, rowParams, store));
73128         }
73129         
73130         this.fly(row).addClass(cls).setStyle(rowParams.tstyle);
73131         rowParams.cells = colBuffer.join("");
73132         row.innerHTML = this.templates.rowInner.apply(rowParams);
73133         
73134         this.fireEvent('rowupdated', this, rowIndex, record);
73135     },
73136
73137     /**
73138      * Refreshs the grid UI
73139      * @param {Boolean} headersToo (optional) True to also refresh the headers
73140      */
73141     refresh : function(headersToo) {
73142         this.fireEvent('beforerefresh', this);
73143         this.grid.stopEditing(true);
73144
73145         var result = this.renderBody();
73146         this.mainBody.update(result).setWidth(this.getTotalWidth());
73147         if (headersToo === true) {
73148             this.updateHeaders();
73149             this.updateHeaderSortState();
73150         }
73151         this.processRows(0, true);
73152         this.layout();
73153         this.applyEmptyText();
73154         this.fireEvent('refresh', this);
73155     },
73156
73157     /**
73158      * @private
73159      * Displays the configured emptyText if there are currently no rows to display
73160      */
73161     applyEmptyText : function() {
73162         if (this.emptyText && !this.hasRows()) {
73163             this.mainBody.update('<div class="x-grid-empty">' + this.emptyText + '</div>');
73164         }
73165     },
73166
73167     /**
73168      * @private
73169      * Adds sorting classes to the column headers based on the bound store's sortInfo. Fires the 'sortchange' event
73170      * if the sorting has changed since this function was last run.
73171      */
73172     updateHeaderSortState : function() {
73173         var state = this.ds.getSortState();
73174         if (!state) {
73175             return;
73176         }
73177
73178         if (!this.sortState || (this.sortState.field != state.field || this.sortState.direction != state.direction)) {
73179             this.grid.fireEvent('sortchange', this.grid, state);
73180         }
73181
73182         this.sortState = state;
73183
73184         var sortColumn = this.cm.findColumnIndex(state.field);
73185         if (sortColumn != -1) {
73186             var sortDir = state.direction;
73187             this.updateSortIcon(sortColumn, sortDir);
73188         }
73189     },
73190
73191     /**
73192      * @private
73193      * Removes any sorting indicator classes from the column headers
73194      */
73195     clearHeaderSortState : function() {
73196         if (!this.sortState) {
73197             return;
73198         }
73199         this.grid.fireEvent('sortchange', this.grid, null);
73200         this.mainHd.select('td').removeClass(this.sortClasses);
73201         delete this.sortState;
73202     },
73203
73204     /**
73205      * @private
73206      * Destroys all objects associated with the GridView
73207      */
73208     destroy : function() {
73209         var me              = this,
73210             grid            = me.grid,
73211             gridEl          = grid.getGridEl(),
73212             dragZone        = me.dragZone,
73213             splitZone       = me.splitZone,
73214             columnDrag      = me.columnDrag,
73215             columnDrop      = me.columnDrop,
73216             scrollToTopTask = me.scrollToTopTask,
73217             columnDragData,
73218             columnDragProxy;
73219         
73220         if (scrollToTopTask && scrollToTopTask.cancel) {
73221             scrollToTopTask.cancel();
73222         }
73223         
73224         Ext.destroyMembers(me, 'colMenu', 'hmenu');
73225
73226         me.initData(null, null);
73227         me.purgeListeners();
73228         
73229         Ext.fly(me.innerHd).un("click", me.handleHdDown, me);
73230
73231         if (grid.enableColumnMove) {
73232             columnDragData = columnDrag.dragData;
73233             columnDragProxy = columnDrag.proxy;
73234             Ext.destroy(
73235                 columnDrag.el,
73236                 columnDragProxy.ghost,
73237                 columnDragProxy.el,
73238                 columnDrop.el,
73239                 columnDrop.proxyTop,
73240                 columnDrop.proxyBottom,
73241                 columnDragData.ddel,
73242                 columnDragData.header
73243             );
73244             
73245             if (columnDragProxy.anim) {
73246                 Ext.destroy(columnDragProxy.anim);
73247             }
73248             
73249             delete columnDragProxy.ghost;
73250             delete columnDragData.ddel;
73251             delete columnDragData.header;
73252             columnDrag.destroy();
73253             
73254             delete Ext.dd.DDM.locationCache[columnDrag.id];
73255             delete columnDrag._domRef;
73256
73257             delete columnDrop.proxyTop;
73258             delete columnDrop.proxyBottom;
73259             columnDrop.destroy();
73260             delete Ext.dd.DDM.locationCache["gridHeader" + gridEl.id];
73261             delete columnDrop._domRef;
73262             delete Ext.dd.DDM.ids[columnDrop.ddGroup];
73263         }
73264
73265         if (splitZone) { // enableColumnResize
73266             splitZone.destroy();
73267             delete splitZone._domRef;
73268             delete Ext.dd.DDM.ids["gridSplitters" + gridEl.id];
73269         }
73270
73271         Ext.fly(me.innerHd).removeAllListeners();
73272         Ext.removeNode(me.innerHd);
73273         delete me.innerHd;
73274
73275         Ext.destroy(
73276             me.el,
73277             me.mainWrap,
73278             me.mainHd,
73279             me.scroller,
73280             me.mainBody,
73281             me.focusEl,
73282             me.resizeMarker,
73283             me.resizeProxy,
73284             me.activeHdBtn,
73285             me._flyweight,
73286             dragZone,
73287             splitZone
73288         );
73289
73290         delete grid.container;
73291
73292         if (dragZone) {
73293             dragZone.destroy();
73294         }
73295
73296         Ext.dd.DDM.currentTarget = null;
73297         delete Ext.dd.DDM.locationCache[gridEl.id];
73298
73299         Ext.EventManager.removeResizeListener(me.onWindowResize, me);
73300     },
73301
73302     // private
73303     onDenyColumnHide : function() {
73304
73305     },
73306
73307     // private
73308     render : function() {
73309         if (this.autoFill) {
73310             var ct = this.grid.ownerCt;
73311             
73312             if (ct && ct.getLayout()) {
73313                 ct.on('afterlayout', function() {
73314                     this.fitColumns(true, true);
73315                     this.updateHeaders();
73316                     this.updateHeaderSortState();
73317                 }, this, {single: true});
73318             }
73319         } else if (this.forceFit) {
73320             this.fitColumns(true, false);
73321         } else if (this.grid.autoExpandColumn) {
73322             this.autoExpand(true);
73323         }
73324         
73325         this.grid.getGridEl().dom.innerHTML = this.renderUI();
73326         
73327         this.afterRenderUI();
73328     },
73329
73330     /* --------------------------------- Model Events and Handlers --------------------------------*/
73331     
73332     /**
73333      * @private
73334      * Binds a new Store and ColumnModel to this GridView. Removes any listeners from the old objects (if present)
73335      * and adds listeners to the new ones
73336      * @param {Ext.data.Store} newStore The new Store instance
73337      * @param {Ext.grid.ColumnModel} newColModel The new ColumnModel instance
73338      */
73339     initData : function(newStore, newColModel) {
73340         var me = this;
73341         
73342         if (me.ds) {
73343             var oldStore = me.ds;
73344             
73345             oldStore.un('add', me.onAdd, me);
73346             oldStore.un('load', me.onLoad, me);
73347             oldStore.un('clear', me.onClear, me);
73348             oldStore.un('remove', me.onRemove, me);
73349             oldStore.un('update', me.onUpdate, me);
73350             oldStore.un('datachanged', me.onDataChange, me);
73351             
73352             if (oldStore !== newStore && oldStore.autoDestroy) {
73353                 oldStore.destroy();
73354             }
73355         }
73356         
73357         if (newStore) {
73358             newStore.on({
73359                 scope      : me,
73360                 load       : me.onLoad,
73361                 add        : me.onAdd,
73362                 remove     : me.onRemove,
73363                 update     : me.onUpdate,
73364                 clear      : me.onClear,
73365                 datachanged: me.onDataChange
73366             });
73367         }
73368         
73369         if (me.cm) {
73370             var oldColModel = me.cm;
73371             
73372             oldColModel.un('configchange', me.onColConfigChange, me);
73373             oldColModel.un('widthchange',  me.onColWidthChange, me);
73374             oldColModel.un('headerchange', me.onHeaderChange, me);
73375             oldColModel.un('hiddenchange', me.onHiddenChange, me);
73376             oldColModel.un('columnmoved',  me.onColumnMove, me);
73377         }
73378         
73379         if (newColModel) {
73380             delete me.lastViewWidth;
73381             
73382             newColModel.on({
73383                 scope       : me,
73384                 configchange: me.onColConfigChange,
73385                 widthchange : me.onColWidthChange,
73386                 headerchange: me.onHeaderChange,
73387                 hiddenchange: me.onHiddenChange,
73388                 columnmoved : me.onColumnMove
73389             });
73390         }
73391         
73392         me.ds = newStore;
73393         me.cm = newColModel;
73394     },
73395
73396     // private
73397     onDataChange : function(){
73398         this.refresh(true);
73399         this.updateHeaderSortState();
73400         this.syncFocusEl(0);
73401     },
73402
73403     // private
73404     onClear : function() {
73405         this.refresh();
73406         this.syncFocusEl(0);
73407     },
73408
73409     // private
73410     onUpdate : function(store, record) {
73411         this.refreshRow(record);
73412     },
73413
73414     // private
73415     onAdd : function(store, records, index) {
73416         this.insertRows(store, index, index + (records.length-1));
73417     },
73418
73419     // private
73420     onRemove : function(store, record, index, isUpdate) {
73421         if (isUpdate !== true) {
73422             this.fireEvent('beforerowremoved', this, index, record);
73423         }
73424         
73425         this.removeRow(index);
73426         
73427         if (isUpdate !== true) {
73428             this.processRows(index);
73429             this.applyEmptyText();
73430             this.fireEvent('rowremoved', this, index, record);
73431         }
73432     },
73433
73434     /**
73435      * @private
73436      * Called when a store is loaded, scrolls to the top row
73437      */
73438     onLoad : function() {
73439         if (Ext.isGecko) {
73440             if (!this.scrollToTopTask) {
73441                 this.scrollToTopTask = new Ext.util.DelayedTask(this.scrollToTop, this);
73442             }
73443             this.scrollToTopTask.delay(1);
73444         } else {
73445             this.scrollToTop();
73446         }
73447     },
73448
73449     // private
73450     onColWidthChange : function(cm, col, width) {
73451         this.updateColumnWidth(col, width);
73452     },
73453
73454     // private
73455     onHeaderChange : function(cm, col, text) {
73456         this.updateHeaders();
73457     },
73458
73459     // private
73460     onHiddenChange : function(cm, col, hidden) {
73461         this.updateColumnHidden(col, hidden);
73462     },
73463
73464     // private
73465     onColumnMove : function(cm, oldIndex, newIndex) {
73466         this.indexMap = null;
73467         this.refresh(true);
73468         this.restoreScroll(this.getScrollState());
73469         
73470         this.afterMove(newIndex);
73471         this.grid.fireEvent('columnmove', oldIndex, newIndex);
73472     },
73473
73474     // private
73475     onColConfigChange : function() {
73476         delete this.lastViewWidth;
73477         this.indexMap = null;
73478         this.refresh(true);
73479     },
73480
73481     /* -------------------- UI Events and Handlers ------------------------------ */
73482     // private
73483     initUI : function(grid) {
73484         grid.on('headerclick', this.onHeaderClick, this);
73485     },
73486
73487     // private
73488     initEvents : Ext.emptyFn,
73489
73490     // private
73491     onHeaderClick : function(g, index) {
73492         if (this.headersDisabled || !this.cm.isSortable(index)) {
73493             return;
73494         }
73495         g.stopEditing(true);
73496         g.store.sort(this.cm.getDataIndex(index));
73497     },
73498
73499     /**
73500      * @private
73501      * Adds the hover class to a row when hovered over
73502      */
73503     onRowOver : function(e, target) {
73504         var row = this.findRowIndex(target);
73505         
73506         if (row !== false) {
73507             this.addRowClass(row, this.rowOverCls);
73508         }
73509     },
73510
73511     /**
73512      * @private
73513      * Removes the hover class from a row on mouseout
73514      */
73515     onRowOut : function(e, target) {
73516         var row = this.findRowIndex(target);
73517         
73518         if (row !== false && !e.within(this.getRow(row), true)) {
73519             this.removeRowClass(row, this.rowOverCls);
73520         }
73521     },
73522
73523     // private
73524     onRowSelect : function(row) {
73525         this.addRowClass(row, this.selectedRowClass);
73526     },
73527
73528     // private
73529     onRowDeselect : function(row) {
73530         this.removeRowClass(row, this.selectedRowClass);
73531     },
73532
73533     // private
73534     onCellSelect : function(row, col) {
73535         var cell = this.getCell(row, col);
73536         if (cell) {
73537             this.fly(cell).addClass('x-grid3-cell-selected');
73538         }
73539     },
73540
73541     // private
73542     onCellDeselect : function(row, col) {
73543         var cell = this.getCell(row, col);
73544         if (cell) {
73545             this.fly(cell).removeClass('x-grid3-cell-selected');
73546         }
73547     },
73548
73549     // private
73550     handleWheel : function(e) {
73551         e.stopPropagation();
73552     },
73553
73554     /**
73555      * @private
73556      * Called by the SplitDragZone when a drag has been completed. Resizes the columns
73557      */
73558     onColumnSplitterMoved : function(cellIndex, width) {
73559         this.userResized = true;
73560         this.grid.colModel.setColumnWidth(cellIndex, width, true);
73561
73562         if (this.forceFit) {
73563             this.fitColumns(true, false, cellIndex);
73564             this.updateAllColumnWidths();
73565         } else {
73566             this.updateColumnWidth(cellIndex, width);
73567             this.syncHeaderScroll();
73568         }
73569
73570         this.grid.fireEvent('columnresize', cellIndex, width);
73571     },
73572
73573     /**
73574      * @private
73575      * Click handler for the shared column dropdown menu, called on beforeshow. Builds the menu
73576      * which displays the list of columns for the user to show or hide.
73577      */
73578     beforeColMenuShow : function() {
73579         var colModel = this.cm,
73580             colCount = colModel.getColumnCount(),
73581             colMenu  = this.colMenu,
73582             i;
73583
73584         colMenu.removeAll();
73585
73586         for (i = 0; i < colCount; i++) {
73587             if (colModel.config[i].hideable !== false) {
73588                 colMenu.add(new Ext.menu.CheckItem({
73589                     text       : colModel.getColumnHeader(i),
73590                     itemId     : 'col-' + colModel.getColumnId(i),
73591                     checked    : !colModel.isHidden(i),
73592                     disabled   : colModel.config[i].hideable === false,
73593                     hideOnClick: false
73594                 }));
73595             }
73596         }
73597     },
73598     
73599     /**
73600      * @private
73601      * Attached as the 'itemclick' handler to the header menu and the column show/hide submenu (if available).
73602      * Performs sorting if the sorter buttons were clicked, otherwise hides/shows the column that was clicked.
73603      */
73604     handleHdMenuClick : function(item) {
73605         var store     = this.ds,
73606             dataIndex = this.cm.getDataIndex(this.hdCtxIndex);
73607
73608         switch (item.getItemId()) {
73609             case 'asc':
73610                 store.sort(dataIndex, 'ASC');
73611                 break;
73612             case 'desc':
73613                 store.sort(dataIndex, 'DESC');
73614                 break;
73615             default:
73616                 this.handleHdMenuClickDefault(item);
73617         }
73618         return true;
73619     },
73620     
73621     /**
73622      * Called by handleHdMenuClick if any button except a sort ASC/DESC button was clicked. The default implementation provides
73623      * the column hide/show functionality based on the check state of the menu item. A different implementation can be provided
73624      * if needed.
73625      * @param {Ext.menu.BaseItem} item The menu item that was clicked
73626      */
73627     handleHdMenuClickDefault: function(item) {
73628         var colModel = this.cm,
73629             itemId   = item.getItemId(),
73630             index    = colModel.getIndexById(itemId.substr(4));
73631
73632         if (index != -1) {
73633             if (item.checked && colModel.getColumnsBy(this.isHideableColumn, this).length <= 1) {
73634                 this.onDenyColumnHide();
73635                 return;
73636             }
73637             colModel.setHidden(index, item.checked);
73638         }
73639     },
73640
73641     /**
73642      * @private
73643      * Called when a header cell is clicked - shows the menu if the click happened over a trigger button
73644      */
73645     handleHdDown : function(e, target) {
73646         if (Ext.fly(target).hasClass('x-grid3-hd-btn')) {
73647             e.stopEvent();
73648             
73649             var colModel  = this.cm,
73650                 header    = this.findHeaderCell(target),
73651                 index     = this.getCellIndex(header),
73652                 sortable  = colModel.isSortable(index),
73653                 menu      = this.hmenu,
73654                 menuItems = menu.items,
73655                 menuCls   = this.headerMenuOpenCls;
73656             
73657             this.hdCtxIndex = index;
73658             
73659             Ext.fly(header).addClass(menuCls);
73660             menuItems.get('asc').setDisabled(!sortable);
73661             menuItems.get('desc').setDisabled(!sortable);
73662             
73663             menu.on('hide', function() {
73664                 Ext.fly(header).removeClass(menuCls);
73665             }, this, {single:true});
73666             
73667             menu.show(target, 'tl-bl?');
73668         }
73669     },
73670
73671     /**
73672      * @private
73673      * Attached to the headers' mousemove event. This figures out the CSS cursor to use based on where the mouse is currently
73674      * pointed. If the mouse is currently hovered over the extreme left or extreme right of any header cell and the cell next 
73675      * to it is resizable it is given the resize cursor, otherwise the cursor is set to an empty string.
73676      */
73677     handleHdMove : function(e) {
73678         var header = this.findHeaderCell(this.activeHdRef);
73679         
73680         if (header && !this.headersDisabled) {
73681             var handleWidth  = this.splitHandleWidth || 5,
73682                 activeRegion = this.activeHdRegion,
73683                 headerStyle  = header.style,
73684                 colModel     = this.cm,
73685                 cursor       = '',
73686                 pageX        = e.getPageX();
73687                 
73688             if (this.grid.enableColumnResize !== false) {
73689                 var activeHeaderIndex = this.activeHdIndex,
73690                     previousVisible   = this.getPreviousVisible(activeHeaderIndex),
73691                     currentResizable  = colModel.isResizable(activeHeaderIndex),
73692                     previousResizable = previousVisible && colModel.isResizable(previousVisible),
73693                     inLeftResizer     = pageX - activeRegion.left <= handleWidth,
73694                     inRightResizer    = activeRegion.right - pageX <= (!this.activeHdBtn ? handleWidth : 2);
73695                 
73696                 if (inLeftResizer && previousResizable) {
73697                     cursor = Ext.isAir ? 'move' : Ext.isWebKit ? 'e-resize' : 'col-resize'; // col-resize not always supported
73698                 } else if (inRightResizer && currentResizable) {
73699                     cursor = Ext.isAir ? 'move' : Ext.isWebKit ? 'w-resize' : 'col-resize';
73700                 }
73701             }
73702             
73703             headerStyle.cursor = cursor;
73704         }
73705     },
73706     
73707     /**
73708      * @private
73709      * Returns the index of the nearest currently visible header to the left of the given index.
73710      * @param {Number} index The header index
73711      * @return {Number/undefined} The index of the nearest visible header
73712      */
73713     getPreviousVisible: function(index) {
73714         while (index > 0) {
73715             if (!this.cm.isHidden(index - 1)) {
73716                 return index;
73717             }
73718             index--;
73719         }
73720         return undefined;
73721     },
73722
73723     /**
73724      * @private
73725      * Tied to the header element's mouseover event - adds the over class to the header cell if the menu is not disabled
73726      * for that cell
73727      */
73728     handleHdOver : function(e, target) {
73729         var header = this.findHeaderCell(target);
73730         
73731         if (header && !this.headersDisabled) {
73732             var fly = this.fly(header);
73733             
73734             this.activeHdRef = target;
73735             this.activeHdIndex = this.getCellIndex(header);
73736             this.activeHdRegion = fly.getRegion();
73737             
73738             if (!this.isMenuDisabled(this.activeHdIndex, fly)) {
73739                 fly.addClass('x-grid3-hd-over');
73740                 this.activeHdBtn = fly.child('.x-grid3-hd-btn');
73741                 
73742                 if (this.activeHdBtn) {
73743                     this.activeHdBtn.dom.style.height = (header.firstChild.offsetHeight - 1) + 'px';
73744                 }
73745             }
73746         }
73747     },
73748
73749     /**
73750      * @private
73751      * Tied to the header element's mouseout event. Removes the hover class from the header cell
73752      */
73753     handleHdOut : function(e, target) {
73754         var header = this.findHeaderCell(target);
73755         
73756         if (header && (!Ext.isIE || !e.within(header, true))) {
73757             this.activeHdRef = null;
73758             this.fly(header).removeClass('x-grid3-hd-over');
73759             header.style.cursor = '';
73760         }
73761     },
73762     
73763     /**
73764      * @private
73765      * Used by {@link #handleHdOver} to determine whether or not to show the header menu class on cell hover
73766      * @param {Number} cellIndex The header cell index
73767      * @param {Ext.Element} el The cell element currently being hovered over
73768      */
73769     isMenuDisabled: function(cellIndex, el) {
73770         return this.cm.isMenuDisabled(cellIndex);
73771     },
73772
73773     /**
73774      * @private
73775      * Returns true if there are any rows rendered into the GridView
73776      * @return {Boolean} True if any rows have been rendered
73777      */
73778     hasRows : function() {
73779         var fc = this.mainBody.dom.firstChild;
73780         return fc && fc.nodeType == 1 && fc.className != 'x-grid-empty';
73781     },
73782     
73783     /**
73784      * @private
73785      */
73786     isHideableColumn : function(c) {
73787         return !c.hidden;
73788     },
73789
73790     /**
73791      * @private
73792      * DEPRECATED - will be removed in Ext JS 5.0
73793      */
73794     bind : function(d, c) {
73795         this.initData(d, c);
73796     }
73797 });
73798
73799
73800 // private
73801 // This is a support class used internally by the Grid components
73802 Ext.grid.GridView.SplitDragZone = Ext.extend(Ext.dd.DDProxy, {
73803
73804     constructor: function(grid, hd){
73805         this.grid = grid;
73806         this.view = grid.getView();
73807         this.marker = this.view.resizeMarker;
73808         this.proxy = this.view.resizeProxy;
73809         Ext.grid.GridView.SplitDragZone.superclass.constructor.call(this, hd,
73810             'gridSplitters' + this.grid.getGridEl().id, {
73811             dragElId : Ext.id(this.proxy.dom), resizeFrame:false
73812         });
73813         this.scroll = false;
73814         this.hw = this.view.splitHandleWidth || 5;
73815     },
73816
73817     b4StartDrag : function(x, y){
73818         this.dragHeadersDisabled = this.view.headersDisabled;
73819         this.view.headersDisabled = true;
73820         var h = this.view.mainWrap.getHeight();
73821         this.marker.setHeight(h);
73822         this.marker.show();
73823         this.marker.alignTo(this.view.getHeaderCell(this.cellIndex), 'tl-tl', [-2, 0]);
73824         this.proxy.setHeight(h);
73825         var w = this.cm.getColumnWidth(this.cellIndex),
73826             minw = Math.max(w-this.grid.minColumnWidth, 0);
73827         this.resetConstraints();
73828         this.setXConstraint(minw, 1000);
73829         this.setYConstraint(0, 0);
73830         this.minX = x - minw;
73831         this.maxX = x + 1000;
73832         this.startPos = x;
73833         Ext.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
73834     },
73835
73836     allowHeaderDrag : function(e){
73837         return true;
73838     },
73839
73840     handleMouseDown : function(e){
73841         var t = this.view.findHeaderCell(e.getTarget());
73842         if(t && this.allowHeaderDrag(e)){
73843             var xy = this.view.fly(t).getXY(), 
73844                 x = xy[0],
73845                 exy = e.getXY(), 
73846                 ex = exy[0],
73847                 w = t.offsetWidth, 
73848                 adjust = false;
73849                 
73850             if((ex - x) <= this.hw){
73851                 adjust = -1;
73852             }else if((x+w) - ex <= this.hw){
73853                 adjust = 0;
73854             }
73855             if(adjust !== false){
73856                 this.cm = this.grid.colModel;
73857                 var ci = this.view.getCellIndex(t);
73858                 if(adjust == -1){
73859                   if (ci + adjust < 0) {
73860                     return;
73861                   }
73862                     while(this.cm.isHidden(ci+adjust)){
73863                         --adjust;
73864                         if(ci+adjust < 0){
73865                             return;
73866                         }
73867                     }
73868                 }
73869                 this.cellIndex = ci+adjust;
73870                 this.split = t.dom;
73871                 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
73872                     Ext.grid.GridView.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
73873                 }
73874             }else if(this.view.columnDrag){
73875                 this.view.columnDrag.callHandleMouseDown(e);
73876             }
73877         }
73878     },
73879
73880     endDrag : function(e){
73881         this.marker.hide();
73882         var v = this.view,
73883             endX = Math.max(this.minX, e.getPageX()),
73884             diff = endX - this.startPos,
73885             disabled = this.dragHeadersDisabled;
73886             
73887         v.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
73888         setTimeout(function(){
73889             v.headersDisabled = disabled;
73890         }, 50);
73891     },
73892
73893     autoOffset : function(){
73894         this.setDelta(0,0);
73895     }
73896 });
73897 /**
73898  * @class Ext.grid.PivotGridView
73899  * @extends Ext.grid.GridView
73900  * Specialised GridView for rendering Pivot Grid components. Config can be passed to the PivotGridView via the PivotGrid constructor's
73901  * viewConfig option:
73902 <pre><code>
73903 new Ext.grid.PivotGrid({
73904     viewConfig: {
73905         title: 'My Pivot Grid',
73906         getCellCls: function(value) {
73907             return value > 10 'red' : 'green';
73908         }
73909     }
73910 });
73911 </code></pre>
73912  * <p>Currently {@link #title} and {@link #getCellCls} are the only configuration options accepted by PivotGridView. All other 
73913  * interaction is performed via the {@link Ext.grid.PivotGrid PivotGrid} class.</p>
73914  */
73915 Ext.grid.PivotGridView = Ext.extend(Ext.grid.GridView, {
73916     
73917     /**
73918      * The CSS class added to all group header cells. Defaults to 'grid-hd-group-cell'
73919      * @property colHeaderCellCls
73920      * @type String
73921      */
73922     colHeaderCellCls: 'grid-hd-group-cell',
73923     
73924     /**
73925      * @cfg {String} title Optional title to be placed in the top left corner of the PivotGrid. Defaults to an empty string.
73926      */
73927     title: '',
73928     
73929     /**
73930      * @cfg {Function} getCellCls Optional function which should return a CSS class name for each cell value. This is useful when
73931      * color coding cells based on their value. Defaults to undefined.
73932      */
73933     
73934     /**
73935      * Returns the headers to be rendered at the top of the grid. Should be a 2-dimensional array, where each item specifies the number
73936      * of columns it groups (column in this case refers to normal grid columns). In the example below we have 5 city groups, which are
73937      * each part of a continent supergroup. The colspan for each city group refers to the number of normal grid columns that group spans,
73938      * so in this case the grid would be expected to have a total of 12 columns:
73939 <pre><code>
73940 [
73941     {
73942         items: [
73943             {header: 'England',   colspan: 5},
73944             {header: 'USA',       colspan: 3}
73945         ]
73946     },
73947     {
73948         items: [
73949             {header: 'London',    colspan: 2},
73950             {header: 'Cambridge', colspan: 3},
73951             {header: 'Palo Alto', colspan: 3}
73952         ]
73953     }
73954 ]
73955 </code></pre>
73956      * In the example above we have cities nested under countries. The nesting could be deeper if desired - e.g. Continent -> Country ->
73957      * State -> City, or any other structure. The only constaint is that the same depth must be used throughout the structure.
73958      * @return {Array} A tree structure containing the headers to be rendered. Must include the colspan property at each level, which should
73959      * be the sum of all child nodes beneath this node.
73960      */
73961     getColumnHeaders: function() {
73962         return this.grid.topAxis.buildHeaders();;
73963     },
73964     
73965     /**
73966      * Returns the headers to be rendered on the left of the grid. Should be a 2-dimensional array, where each item specifies the number
73967      * of rows it groups. In the example below we have 5 city groups, which are each part of a continent supergroup. The rowspan for each 
73968      * city group refers to the number of normal grid columns that group spans, so in this case the grid would be expected to have a 
73969      * total of 12 rows:
73970 <pre><code>
73971 [
73972     {
73973         width: 90,
73974         items: [
73975             {header: 'England',   rowspan: 5},
73976             {header: 'USA',       rowspan: 3}
73977         ]
73978     },
73979     {
73980         width: 50,
73981         items: [
73982             {header: 'London',    rowspan: 2},
73983             {header: 'Cambridge', rowspan: 3},
73984             {header: 'Palo Alto', rowspan: 3}
73985         ]
73986     }
73987 ]
73988 </code></pre>
73989      * In the example above we have cities nested under countries. The nesting could be deeper if desired - e.g. Continent -> Country ->
73990      * State -> City, or any other structure. The only constaint is that the same depth must be used throughout the structure.
73991      * @return {Array} A tree structure containing the headers to be rendered. Must include the colspan property at each level, which should
73992      * be the sum of all child nodes beneath this node.
73993      * Each group may specify the width it should be rendered with.
73994      * @return {Array} The row groups
73995      */
73996     getRowHeaders: function() {
73997         return this.grid.leftAxis.buildHeaders();
73998     },
73999     
74000     /**
74001      * @private
74002      * Renders rows between start and end indexes
74003      * @param {Number} startRow Index of the first row to render
74004      * @param {Number} endRow Index of the last row to render
74005      */
74006     renderRows : function(startRow, endRow) {
74007         var grid          = this.grid,
74008             rows          = grid.extractData(),
74009             rowCount      = rows.length,
74010             templates     = this.templates,
74011             renderer      = grid.renderer,
74012             hasRenderer   = typeof renderer == 'function',
74013             getCellCls    = this.getCellCls,
74014             hasGetCellCls = typeof getCellCls == 'function',
74015             cellTemplate  = templates.cell,
74016             rowTemplate   = templates.row,
74017             rowBuffer     = [],
74018             meta          = {},
74019             tstyle        = 'width:' + this.getGridInnerWidth() + 'px;',
74020             colBuffer, column, i;
74021         
74022         startRow = startRow || 0;
74023         endRow   = Ext.isDefined(endRow) ? endRow : rowCount - 1;
74024         
74025         for (i = 0; i < rowCount; i++) {
74026             row = rows[i];
74027             colCount  = row.length;
74028             colBuffer = [];
74029             
74030             rowIndex = startRow + i;
74031
74032             //build up each column's HTML
74033             for (j = 0; j < colCount; j++) {
74034                 cell = row[j];
74035
74036                 meta.css   = j === 0 ? 'x-grid3-cell-first ' : (j == (colCount - 1) ? 'x-grid3-cell-last ' : '');
74037                 meta.attr  = meta.cellAttr = '';
74038                 meta.value = cell;
74039
74040                 if (Ext.isEmpty(meta.value)) {
74041                     meta.value = '&#160;';
74042                 }
74043                 
74044                 if (hasRenderer) {
74045                     meta.value = renderer(meta.value);
74046                 }
74047                 
74048                 if (hasGetCellCls) {
74049                     meta.css += getCellCls(meta.value) + ' ';
74050                 }
74051
74052                 colBuffer[colBuffer.length] = cellTemplate.apply(meta);
74053             }
74054             
74055             rowBuffer[rowBuffer.length] = rowTemplate.apply({
74056                 tstyle: tstyle,
74057                 cols  : colCount,
74058                 cells : colBuffer.join(""),
74059                 alt   : ''
74060             });
74061         }
74062         
74063         return rowBuffer.join("");
74064     },
74065     
74066     /**
74067      * The master template to use when rendering the GridView. Has a default template
74068      * @property Ext.Template
74069      * @type masterTpl
74070      */
74071     masterTpl: new Ext.Template(
74072         '<div class="x-grid3 x-pivotgrid" hidefocus="true">',
74073             '<div class="x-grid3-viewport">',
74074                 '<div class="x-grid3-header">',
74075                     '<div class="x-grid3-header-title"><span>{title}</span></div>',
74076                     '<div class="x-grid3-header-inner">',
74077                         '<div class="x-grid3-header-offset" style="{ostyle}"></div>',
74078                     '</div>',
74079                     '<div class="x-clear"></div>',
74080                 '</div>',
74081                 '<div class="x-grid3-scroller">',
74082                     '<div class="x-grid3-row-headers"></div>',
74083                     '<div class="x-grid3-body" style="{bstyle}">{body}</div>',
74084                     '<a href="#" class="x-grid3-focus" tabIndex="-1"></a>',
74085                 '</div>',
74086             '</div>',
74087             '<div class="x-grid3-resize-marker">&#160;</div>',
74088             '<div class="x-grid3-resize-proxy">&#160;</div>',
74089         '</div>'
74090     ),
74091     
74092     /**
74093      * @private
74094      * Adds a gcell template to the internal templates object. This is used to render the headers in a multi-level column header.
74095      */
74096     initTemplates: function() {
74097         Ext.grid.PivotGridView.superclass.initTemplates.apply(this, arguments);
74098         
74099         var templates = this.templates || {};
74100         if (!templates.gcell) {
74101             templates.gcell = new Ext.XTemplate(
74102                 '<td class="x-grid3-hd x-grid3-gcell x-grid3-td-{id} ux-grid-hd-group-row-{row} ' + this.colHeaderCellCls + '" style="{style}">',
74103                     '<div {tooltip} class="x-grid3-hd-inner x-grid3-hd-{id}" unselectable="on" style="{istyle}">', 
74104                         this.grid.enableHdMenu ? '<a class="x-grid3-hd-btn" href="#"></a>' : '', '{value}',
74105                     '</div>',
74106                 '</td>'
74107             );
74108         }
74109         
74110         this.templates = templates;
74111         this.hrowRe = new RegExp("ux-grid-hd-group-row-(\\d+)", "");
74112     },
74113     
74114     /**
74115      * @private
74116      * Sets up the reference to the row headers element
74117      */
74118     initElements: function() {
74119         Ext.grid.PivotGridView.superclass.initElements.apply(this, arguments);
74120         
74121         /**
74122          * @property rowHeadersEl
74123          * @type Ext.Element
74124          * The element containing all row headers
74125          */
74126         this.rowHeadersEl = new Ext.Element(this.scroller.child('div.x-grid3-row-headers'));
74127         
74128         /**
74129          * @property headerTitleEl
74130          * @type Ext.Element
74131          * The element that contains the optional title (top left section of the pivot grid)
74132          */
74133         this.headerTitleEl = new Ext.Element(this.mainHd.child('div.x-grid3-header-title'));
74134     },
74135     
74136     /**
74137      * @private
74138      * Takes row headers into account when calculating total available width
74139      */
74140     getGridInnerWidth: function() {
74141         var previousWidth = Ext.grid.PivotGridView.superclass.getGridInnerWidth.apply(this, arguments);
74142         
74143         return previousWidth - this.getTotalRowHeaderWidth();
74144     },
74145     
74146     /**
74147      * Returns the total width of all row headers as specified by {@link #getRowHeaders}
74148      * @return {Number} The total width
74149      */
74150     getTotalRowHeaderWidth: function() {
74151         var headers = this.getRowHeaders(),
74152             length  = headers.length,
74153             total   = 0,
74154             i;
74155         
74156         for (i = 0; i< length; i++) {
74157             total += headers[i].width;
74158         }
74159         
74160         return total;
74161     },
74162     
74163     /**
74164      * @private
74165      * Returns the total height of all column headers
74166      * @return {Number} The total height
74167      */
74168     getTotalColumnHeaderHeight: function() {
74169         return this.getColumnHeaders().length * 21;
74170     },
74171     
74172     /**
74173      * @private
74174      * Slight specialisation of the GridView renderUI - just adds the row headers
74175      */
74176     renderUI : function() {
74177         var templates  = this.templates,
74178             innerWidth = this.getGridInnerWidth();
74179             
74180         return templates.master.apply({
74181             body  : templates.body.apply({rows:'&#160;'}),
74182             ostyle: 'width:' + innerWidth + 'px',
74183             bstyle: 'width:' + innerWidth + 'px'
74184         });
74185     },
74186     
74187     /**
74188      * @private
74189      * Make sure that the headers and rows are all sized correctly during layout
74190      */
74191     onLayout: function(width, height) {
74192         Ext.grid.PivotGridView.superclass.onLayout.apply(this, arguments);
74193         
74194         var width = this.getGridInnerWidth();
74195         
74196         this.resizeColumnHeaders(width);
74197         this.resizeAllRows(width);
74198     },
74199     
74200     /**
74201      * Refreshs the grid UI
74202      * @param {Boolean} headersToo (optional) True to also refresh the headers
74203      */
74204     refresh : function(headersToo) {
74205         this.fireEvent('beforerefresh', this);
74206         this.grid.stopEditing(true);
74207         
74208         var result = this.renderBody();
74209         this.mainBody.update(result).setWidth(this.getGridInnerWidth());
74210         if (headersToo === true) {
74211             this.updateHeaders();
74212             this.updateHeaderSortState();
74213         }
74214         this.processRows(0, true);
74215         this.layout();
74216         this.applyEmptyText();
74217         this.fireEvent('refresh', this);
74218     },
74219     
74220     /**
74221      * @private
74222      * Bypasses GridView's renderHeaders as they are taken care of separately by the PivotAxis instances
74223      */
74224     renderHeaders: Ext.emptyFn,
74225     
74226     /**
74227      * @private
74228      * Taken care of by PivotAxis
74229      */
74230     fitColumns: Ext.emptyFn,
74231     
74232     /**
74233      * @private
74234      * Called on layout, ensures that the width of each column header is correct. Omitting this can lead to faulty
74235      * layouts when nested in a container.
74236      * @param {Number} width The new width
74237      */
74238     resizeColumnHeaders: function(width) {
74239         var topAxis = this.grid.topAxis;
74240         
74241         if (topAxis.rendered) {
74242             topAxis.el.setWidth(width);
74243         }
74244     },
74245     
74246     /**
74247      * @private
74248      * Sets the row header div to the correct width. Should be called after rendering and reconfiguration of headers
74249      */
74250     resizeRowHeaders: function() {
74251         var rowHeaderWidth = this.getTotalRowHeaderWidth(),
74252             marginStyle    = String.format("margin-left: {0}px;", rowHeaderWidth);
74253         
74254         this.rowHeadersEl.setWidth(rowHeaderWidth);
74255         this.mainBody.applyStyles(marginStyle);
74256         Ext.fly(this.innerHd).applyStyles(marginStyle);
74257         
74258         this.headerTitleEl.setWidth(rowHeaderWidth);
74259         this.headerTitleEl.setHeight(this.getTotalColumnHeaderHeight());
74260     },
74261     
74262     /**
74263      * @private
74264      * Resizes all rendered rows to the given width. Usually called by onLayout
74265      * @param {Number} width The new width
74266      */
74267     resizeAllRows: function(width) {
74268         var rows   = this.getRows(),
74269             length = rows.length,
74270             i;
74271         
74272         for (i = 0; i < length; i++) {
74273             Ext.fly(rows[i]).setWidth(width);
74274             Ext.fly(rows[i]).child('table').setWidth(width);
74275         }
74276     },
74277     
74278     /**
74279      * @private
74280      * Updates the Row Headers, deferring the updating of Column Headers to GridView
74281      */
74282     updateHeaders: function() {
74283         this.renderGroupRowHeaders();
74284         this.renderGroupColumnHeaders();
74285     },
74286     
74287     /**
74288      * @private
74289      * Renders all row header groups at all levels based on the structure fetched from {@link #getGroupRowHeaders}
74290      */
74291     renderGroupRowHeaders: function() {
74292         var leftAxis = this.grid.leftAxis;
74293         
74294         this.resizeRowHeaders();
74295         leftAxis.rendered = false;
74296         leftAxis.render(this.rowHeadersEl);
74297         
74298         this.setTitle(this.title);
74299     },
74300     
74301     /**
74302      * Sets the title text in the top left segment of the PivotGridView
74303      * @param {String} title The title
74304      */
74305     setTitle: function(title) {
74306         this.headerTitleEl.child('span').dom.innerHTML = title;
74307     },
74308     
74309     /**
74310      * @private
74311      * Renders all column header groups at all levels based on the structure fetched from {@link #getColumnHeaders}
74312      */
74313     renderGroupColumnHeaders: function() {
74314         var topAxis = this.grid.topAxis;
74315         
74316         topAxis.rendered = false;
74317         topAxis.render(this.innerHd.firstChild);
74318     },
74319     
74320     /**
74321      * @private
74322      * Overridden to test whether the user is hovering over a group cell, in which case we don't show the menu
74323      */
74324     isMenuDisabled: function(cellIndex, el) {
74325         return true;
74326     }
74327 });/**
74328  * @class Ext.grid.PivotAxis
74329  * @extends Ext.Component
74330  * <p>PivotAxis is a class that supports a {@link Ext.grid.PivotGrid}. Each PivotGrid contains two PivotAxis instances - the left
74331  * axis and the top axis. Each PivotAxis defines an ordered set of dimensions, each of which should correspond to a field in a
74332  * Store's Record (see {@link Ext.grid.PivotGrid} documentation for further explanation).</p>
74333  * <p>Developers should have little interaction with the PivotAxis instances directly as most of their management is performed by
74334  * the PivotGrid. An exception is the dynamic reconfiguration of axes at run time - to achieve this we use PivotAxis's 
74335  * {@link #setDimensions} function and refresh the grid:</p>
74336 <pre><code>
74337 var pivotGrid = new Ext.grid.PivotGrid({
74338     //some PivotGrid config here
74339 });
74340
74341 //change the left axis dimensions
74342 pivotGrid.leftAxis.setDimensions([
74343     {
74344         dataIndex: 'person',
74345         direction: 'DESC',
74346         width    : 100
74347     },
74348     {
74349         dataIndex: 'product',
74350         direction: 'ASC',
74351         width    : 80
74352     }
74353 ]);
74354
74355 pivotGrid.view.refresh(true);
74356 </code></pre>
74357  * This clears the previous dimensions on the axis and redraws the grid with the new dimensions.
74358  */
74359 Ext.grid.PivotAxis = Ext.extend(Ext.Component, {
74360     /**
74361      * @cfg {String} orientation One of 'vertical' or 'horizontal'. Defaults to horizontal
74362      */
74363     orientation: 'horizontal',
74364     
74365     /**
74366      * @cfg {Number} defaultHeaderWidth The width to render each row header that does not have a width specified via 
74367      {@link #getRowGroupHeaders}. Defaults to 80.
74368      */
74369     defaultHeaderWidth: 80,
74370     
74371     /**
74372      * @private
74373      * @cfg {Number} paddingWidth The amount of padding used by each cell.
74374      * TODO: From 4.x onwards this can be removed as it won't be needed. For now it is used to account for the differences between
74375      * the content box and border box measurement models
74376      */
74377     paddingWidth: 7,
74378     
74379     /**
74380      * Updates the dimensions used by this axis
74381      * @param {Array} dimensions The new dimensions
74382      */
74383     setDimensions: function(dimensions) {
74384         this.dimensions = dimensions;
74385     },
74386     
74387     /**
74388      * @private
74389      * Builds the html table that contains the dimensions for this axis. This branches internally between vertical
74390      * and horizontal orientations because the table structure is slightly different in each case
74391      */
74392     onRender: function(ct, position) {
74393         var rows = this.orientation == 'horizontal'
74394                  ? this.renderHorizontalRows()
74395                  : this.renderVerticalRows();
74396         
74397         this.el = Ext.DomHelper.overwrite(ct.dom, {tag: 'table', cn: rows}, true);
74398     },
74399     
74400     /**
74401      * @private
74402      * Specialised renderer for horizontal oriented axes
74403      * @return {Object} The HTML Domspec for a horizontal oriented axis
74404      */
74405     renderHorizontalRows: function() {
74406         var headers  = this.buildHeaders(),
74407             rowCount = headers.length,
74408             rows     = [],
74409             cells, cols, colCount, i, j;
74410         
74411         for (i = 0; i < rowCount; i++) {
74412             cells = [];
74413             cols  = headers[i].items;
74414             colCount = cols.length;
74415
74416             for (j = 0; j < colCount; j++) {
74417                 cells.push({
74418                     tag: 'td',
74419                     html: cols[j].header,
74420                     colspan: cols[j].span
74421                 });
74422             }
74423
74424             rows[i] = {
74425                 tag: 'tr',
74426                 cn: cells
74427             };
74428         }
74429         
74430         return rows;
74431     },
74432     
74433     /**
74434      * @private
74435      * Specialised renderer for vertical oriented axes
74436      * @return {Object} The HTML Domspec for a vertical oriented axis
74437      */
74438     renderVerticalRows: function() {
74439         var headers  = this.buildHeaders(),
74440             colCount = headers.length,
74441             rowCells = [],
74442             rows     = [],
74443             rowCount, col, row, colWidth, i, j;
74444         
74445         for (i = 0; i < colCount; i++) {
74446             col = headers[i];
74447             colWidth = col.width || 80;
74448             rowCount = col.items.length;
74449             
74450             for (j = 0; j < rowCount; j++) {
74451                 row = col.items[j];
74452                 
74453                 rowCells[row.start] = rowCells[row.start] || [];
74454                 rowCells[row.start].push({
74455                     tag    : 'td',
74456                     html   : row.header,
74457                     rowspan: row.span,
74458                     width  : Ext.isBorderBox ? colWidth : colWidth - this.paddingWidth
74459                 });
74460             }
74461         }
74462         
74463         rowCount = rowCells.length;
74464         for (i = 0; i < rowCount; i++) {
74465             rows[i] = {
74466                 tag: 'tr',
74467                 cn : rowCells[i]
74468             };
74469         }
74470         
74471         return rows;
74472     },
74473     
74474     /**
74475      * @private
74476      * Returns the set of all unique tuples based on the bound store and dimension definitions.
74477      * Internally we construct a new, temporary store to make use of the multi-sort capabilities of Store. In
74478      * 4.x this functionality should have been moved to MixedCollection so this step should not be needed.
74479      * @return {Array} All unique tuples
74480      */
74481     getTuples: function() {
74482         var newStore = new Ext.data.Store({});
74483         
74484         newStore.data = this.store.data.clone();
74485         newStore.fields = this.store.fields;
74486         
74487         var sorters    = [],
74488             dimensions = this.dimensions,
74489             length     = dimensions.length,
74490             i;
74491         
74492         for (i = 0; i < length; i++) {
74493             sorters.push({
74494                 field    : dimensions[i].dataIndex,
74495                 direction: dimensions[i].direction || 'ASC'
74496             });
74497         }
74498         
74499         newStore.sort(sorters);
74500         
74501         var records = newStore.data.items,
74502             hashes  = [],
74503             tuples  = [],
74504             recData, hash, info, data, key;
74505         
74506         length = records.length;
74507         
74508         for (i = 0; i < length; i++) {
74509             info = this.getRecordInfo(records[i]);
74510             data = info.data;
74511             hash = "";
74512             
74513             for (key in data) {
74514                 hash += data[key] + '---';
74515             }
74516             
74517             if (hashes.indexOf(hash) == -1) {
74518                 hashes.push(hash);
74519                 tuples.push(info);
74520             }
74521         }
74522         
74523         newStore.destroy();
74524         
74525         return tuples;
74526     },
74527     
74528     /**
74529      * @private
74530      */
74531     getRecordInfo: function(record) {
74532         var dimensions = this.dimensions,
74533             length  = dimensions.length,
74534             data    = {},
74535             dimension, dataIndex, i;
74536         
74537         //get an object containing just the data we are interested in based on the configured dimensions
74538         for (i = 0; i < length; i++) {
74539             dimension = dimensions[i];
74540             dataIndex = dimension.dataIndex;
74541             
74542             data[dataIndex] = record.get(dataIndex);
74543         }
74544         
74545         //creates a specialised matcher function for a given tuple. The returned function will return
74546         //true if the record passed to it matches the dataIndex values of each dimension in this axis
74547         var createMatcherFunction = function(data) {
74548             return function(record) {
74549                 for (var dataIndex in data) {
74550                     if (record.get(dataIndex) != data[dataIndex]) {
74551                         return false;
74552                     }
74553                 }
74554                 
74555                 return true;
74556             };
74557         };
74558         
74559         return {
74560             data: data,
74561             matcher: createMatcherFunction(data)
74562         };
74563     },
74564     
74565     /**
74566      * @private
74567      * Uses the calculated set of tuples to build an array of headers that can be rendered into a table using rowspan or
74568      * colspan. Basically this takes the set of tuples and spans any cells that run into one another, so if we had dimensions
74569      * of Person and Product and several tuples containing different Products for the same Person, those Products would be
74570      * spanned.
74571      * @return {Array} The headers
74572      */
74573     buildHeaders: function() {
74574         var tuples     = this.getTuples(),
74575             rowCount   = tuples.length,
74576             dimensions = this.dimensions,
74577             colCount   = dimensions.length,
74578             headers    = [],
74579             tuple, rows, currentHeader, previousHeader, span, start, isLast, changed, i, j;
74580         
74581         for (i = 0; i < colCount; i++) {
74582             dimension = dimensions[i];
74583             rows  = [];
74584             span  = 0;
74585             start = 0;
74586             
74587             for (j = 0; j < rowCount; j++) {
74588                 tuple  = tuples[j];
74589                 isLast = j == (rowCount - 1);
74590                 currentHeader = tuple.data[dimension.dataIndex];
74591                 
74592                 /*
74593                  * 'changed' indicates that we need to create a new cell. This should be true whenever the cell
74594                  * above (previousHeader) is different from this cell, or when the cell on the previous dimension
74595                  * changed (e.g. if the current dimension is Product and the previous was Person, we need to start
74596                  * a new cell if Product is the same but Person changed, so we check the previous dimension and tuple)
74597                  */
74598                 changed = previousHeader != undefined && previousHeader != currentHeader;
74599                 if (i > 0 && j > 0) {
74600                     changed = changed || tuple.data[dimensions[i-1].dataIndex] != tuples[j-1].data[dimensions[i-1].dataIndex];
74601                 }
74602                 
74603                 if (changed) {                    
74604                     rows.push({
74605                         header: previousHeader,
74606                         span  : span,
74607                         start : start
74608                     });
74609                     
74610                     start += span;
74611                     span = 0;
74612                 }
74613                 
74614                 if (isLast) {
74615                     rows.push({
74616                         header: currentHeader,
74617                         span  : span + 1,
74618                         start : start
74619                     });
74620                     
74621                     start += span;
74622                     span = 0;
74623                 }
74624                 
74625                 previousHeader = currentHeader;
74626                 span++;
74627             }
74628             
74629             headers.push({
74630                 items: rows,
74631                 width: dimension.width || this.defaultHeaderWidth
74632             });
74633             
74634             previousHeader = undefined;
74635         }
74636         
74637         return headers;
74638     }
74639 });
74640 // private
74641 // This is a support class used internally by the Grid components
74642 Ext.grid.HeaderDragZone = Ext.extend(Ext.dd.DragZone, {
74643     maxDragWidth: 120,
74644     
74645     constructor : function(grid, hd, hd2){
74646         this.grid = grid;
74647         this.view = grid.getView();
74648         this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
74649         Ext.grid.HeaderDragZone.superclass.constructor.call(this, hd);
74650         if(hd2){
74651             this.setHandleElId(Ext.id(hd));
74652             this.setOuterHandleElId(Ext.id(hd2));
74653         }
74654         this.scroll = false;
74655     },
74656     
74657     getDragData : function(e){
74658         var t = Ext.lib.Event.getTarget(e),
74659             h = this.view.findHeaderCell(t);
74660         if(h){
74661             return {ddel: h.firstChild, header:h};
74662         }
74663         return false;
74664     },
74665
74666     onInitDrag : function(e){
74667         // keep the value here so we can restore it;
74668         this.dragHeadersDisabled = this.view.headersDisabled;
74669         this.view.headersDisabled = true;
74670         var clone = this.dragData.ddel.cloneNode(true);
74671         clone.id = Ext.id();
74672         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
74673         this.proxy.update(clone);
74674         return true;
74675     },
74676
74677     afterValidDrop : function(){
74678         this.completeDrop();
74679     },
74680
74681     afterInvalidDrop : function(){
74682         this.completeDrop();
74683     },
74684     
74685     completeDrop: function(){
74686         var v = this.view,
74687             disabled = this.dragHeadersDisabled;
74688         setTimeout(function(){
74689             v.headersDisabled = disabled;
74690         }, 50);
74691     }
74692 });
74693
74694 // private
74695 // This is a support class used internally by the Grid components
74696 Ext.grid.HeaderDropZone = Ext.extend(Ext.dd.DropZone, {
74697     proxyOffsets : [-4, -9],
74698     fly: Ext.Element.fly,
74699     
74700     constructor : function(grid, hd, hd2){
74701         this.grid = grid;
74702         this.view = grid.getView();
74703         // split the proxies so they don't interfere with mouse events
74704         this.proxyTop = Ext.DomHelper.append(document.body, {
74705             cls:"col-move-top", html:"&#160;"
74706         }, true);
74707         this.proxyBottom = Ext.DomHelper.append(document.body, {
74708             cls:"col-move-bottom", html:"&#160;"
74709         }, true);
74710         this.proxyTop.hide = this.proxyBottom.hide = function(){
74711             this.setLeftTop(-100,-100);
74712             this.setStyle("visibility", "hidden");
74713         };
74714         this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
74715         // temporarily disabled
74716         //Ext.dd.ScrollManager.register(this.view.scroller.dom);
74717         Ext.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
74718     },
74719
74720     getTargetFromEvent : function(e){
74721         var t = Ext.lib.Event.getTarget(e),
74722             cindex = this.view.findCellIndex(t);
74723         if(cindex !== false){
74724             return this.view.getHeaderCell(cindex);
74725         }
74726     },
74727
74728     nextVisible : function(h){
74729         var v = this.view, cm = this.grid.colModel;
74730         h = h.nextSibling;
74731         while(h){
74732             if(!cm.isHidden(v.getCellIndex(h))){
74733                 return h;
74734             }
74735             h = h.nextSibling;
74736         }
74737         return null;
74738     },
74739
74740     prevVisible : function(h){
74741         var v = this.view, cm = this.grid.colModel;
74742         h = h.prevSibling;
74743         while(h){
74744             if(!cm.isHidden(v.getCellIndex(h))){
74745                 return h;
74746             }
74747             h = h.prevSibling;
74748         }
74749         return null;
74750     },
74751
74752     positionIndicator : function(h, n, e){
74753         var x = Ext.lib.Event.getPageX(e),
74754             r = Ext.lib.Dom.getRegion(n.firstChild),
74755             px, 
74756             pt, 
74757             py = r.top + this.proxyOffsets[1];
74758         if((r.right - x) <= (r.right-r.left)/2){
74759             px = r.right+this.view.borderWidth;
74760             pt = "after";
74761         }else{
74762             px = r.left;
74763             pt = "before";
74764         }
74765
74766         if(this.grid.colModel.isFixed(this.view.getCellIndex(n))){
74767             return false;
74768         }
74769
74770         px +=  this.proxyOffsets[0];
74771         this.proxyTop.setLeftTop(px, py);
74772         this.proxyTop.show();
74773         if(!this.bottomOffset){
74774             this.bottomOffset = this.view.mainHd.getHeight();
74775         }
74776         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
74777         this.proxyBottom.show();
74778         return pt;
74779     },
74780
74781     onNodeEnter : function(n, dd, e, data){
74782         if(data.header != n){
74783             this.positionIndicator(data.header, n, e);
74784         }
74785     },
74786
74787     onNodeOver : function(n, dd, e, data){
74788         var result = false;
74789         if(data.header != n){
74790             result = this.positionIndicator(data.header, n, e);
74791         }
74792         if(!result){
74793             this.proxyTop.hide();
74794             this.proxyBottom.hide();
74795         }
74796         return result ? this.dropAllowed : this.dropNotAllowed;
74797     },
74798
74799     onNodeOut : function(n, dd, e, data){
74800         this.proxyTop.hide();
74801         this.proxyBottom.hide();
74802     },
74803
74804     onNodeDrop : function(n, dd, e, data){
74805         var h = data.header;
74806         if(h != n){
74807             var cm = this.grid.colModel,
74808                 x = Ext.lib.Event.getPageX(e),
74809                 r = Ext.lib.Dom.getRegion(n.firstChild),
74810                 pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before",
74811                 oldIndex = this.view.getCellIndex(h),
74812                 newIndex = this.view.getCellIndex(n);
74813             if(pt == "after"){
74814                 newIndex++;
74815             }
74816             if(oldIndex < newIndex){
74817                 newIndex--;
74818             }
74819             cm.moveColumn(oldIndex, newIndex);
74820             return true;
74821         }
74822         return false;
74823     }
74824 });
74825
74826 Ext.grid.GridView.ColumnDragZone = Ext.extend(Ext.grid.HeaderDragZone, {
74827     
74828     constructor : function(grid, hd){
74829         Ext.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
74830         this.proxy.el.addClass('x-grid3-col-dd');
74831     },
74832     
74833     handleMouseDown : function(e){
74834     },
74835
74836     callHandleMouseDown : function(e){
74837         Ext.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
74838     }
74839 });// private
74840 // This is a support class used internally by the Grid components
74841 Ext.grid.SplitDragZone = Ext.extend(Ext.dd.DDProxy, {
74842     fly: Ext.Element.fly,
74843     
74844     constructor : function(grid, hd, hd2){
74845         this.grid = grid;
74846         this.view = grid.getView();
74847         this.proxy = this.view.resizeProxy;
74848         Ext.grid.SplitDragZone.superclass.constructor.call(this, hd,
74849             "gridSplitters" + this.grid.getGridEl().id, {
74850             dragElId : Ext.id(this.proxy.dom), resizeFrame:false
74851         });
74852         this.setHandleElId(Ext.id(hd));
74853         this.setOuterHandleElId(Ext.id(hd2));
74854         this.scroll = false;
74855     },
74856
74857     b4StartDrag : function(x, y){
74858         this.view.headersDisabled = true;
74859         this.proxy.setHeight(this.view.mainWrap.getHeight());
74860         var w = this.cm.getColumnWidth(this.cellIndex);
74861         var minw = Math.max(w-this.grid.minColumnWidth, 0);
74862         this.resetConstraints();
74863         this.setXConstraint(minw, 1000);
74864         this.setYConstraint(0, 0);
74865         this.minX = x - minw;
74866         this.maxX = x + 1000;
74867         this.startPos = x;
74868         Ext.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
74869     },
74870
74871
74872     handleMouseDown : function(e){
74873         var ev = Ext.EventObject.setEvent(e);
74874         var t = this.fly(ev.getTarget());
74875         if(t.hasClass("x-grid-split")){
74876             this.cellIndex = this.view.getCellIndex(t.dom);
74877             this.split = t.dom;
74878             this.cm = this.grid.colModel;
74879             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
74880                 Ext.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
74881             }
74882         }
74883     },
74884
74885     endDrag : function(e){
74886         this.view.headersDisabled = false;
74887         var endX = Math.max(this.minX, Ext.lib.Event.getPageX(e));
74888         var diff = endX - this.startPos;
74889         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
74890     },
74891
74892     autoOffset : function(){
74893         this.setDelta(0,0);
74894     }
74895 });/**
74896  * @class Ext.grid.GridDragZone
74897  * @extends Ext.dd.DragZone
74898  * <p>A customized implementation of a {@link Ext.dd.DragZone DragZone} which provides default implementations of two of the
74899  * template methods of DragZone to enable dragging of the selected rows of a GridPanel.</p>
74900  * <p>A cooperating {@link Ext.dd.DropZone DropZone} must be created who's template method implementations of
74901  * {@link Ext.dd.DropZone#onNodeEnter onNodeEnter}, {@link Ext.dd.DropZone#onNodeOver onNodeOver},
74902  * {@link Ext.dd.DropZone#onNodeOut onNodeOut} and {@link Ext.dd.DropZone#onNodeDrop onNodeDrop}</p> are able
74903  * to process the {@link #getDragData data} which is provided.
74904  */
74905 Ext.grid.GridDragZone = function(grid, config){
74906     this.view = grid.getView();
74907     Ext.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
74908     this.scroll = false;
74909     this.grid = grid;
74910     this.ddel = document.createElement('div');
74911     this.ddel.className = 'x-grid-dd-wrap';
74912 };
74913
74914 Ext.extend(Ext.grid.GridDragZone, Ext.dd.DragZone, {
74915     ddGroup : "GridDD",
74916
74917     /**
74918      * <p>The provided implementation of the getDragData method which collects the data to be dragged from the GridPanel on mousedown.</p>
74919      * <p>This data is available for processing in the {@link Ext.dd.DropZone#onNodeEnter onNodeEnter}, {@link Ext.dd.DropZone#onNodeOver onNodeOver},
74920      * {@link Ext.dd.DropZone#onNodeOut onNodeOut} and {@link Ext.dd.DropZone#onNodeDrop onNodeDrop} methods of a cooperating {@link Ext.dd.DropZone DropZone}.</p>
74921      * <p>The data object contains the following properties:<ul>
74922      * <li><b>grid</b> : Ext.Grid.GridPanel<div class="sub-desc">The GridPanel from which the data is being dragged.</div></li>
74923      * <li><b>ddel</b> : htmlElement<div class="sub-desc">An htmlElement which provides the "picture" of the data being dragged.</div></li>
74924      * <li><b>rowIndex</b> : Number<div class="sub-desc">The index of the row which receieved the mousedown gesture which triggered the drag.</div></li>
74925      * <li><b>selections</b> : Array<div class="sub-desc">An Array of the selected Records which are being dragged from the GridPanel.</div></li>
74926      * </ul></p>
74927      */
74928     getDragData : function(e){
74929         var t = Ext.lib.Event.getTarget(e);
74930         var rowIndex = this.view.findRowIndex(t);
74931         if(rowIndex !== false){
74932             var sm = this.grid.selModel;
74933             if(!sm.isSelected(rowIndex) || e.hasModifier()){
74934                 sm.handleMouseDown(this.grid, rowIndex, e);
74935             }
74936             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
74937         }
74938         return false;
74939     },
74940
74941     /**
74942      * <p>The provided implementation of the onInitDrag method. Sets the <tt>innerHTML</tt> of the drag proxy which provides the "picture"
74943      * of the data being dragged.</p>
74944      * <p>The <tt>innerHTML</tt> data is found by calling the owning GridPanel's {@link Ext.grid.GridPanel#getDragDropText getDragDropText}.</p>
74945      */
74946     onInitDrag : function(e){
74947         var data = this.dragData;
74948         this.ddel.innerHTML = this.grid.getDragDropText();
74949         this.proxy.update(this.ddel);
74950         // fire start drag?
74951     },
74952
74953     /**
74954      * An empty immplementation. Implement this to provide behaviour after a repair of an invalid drop. An implementation might highlight
74955      * the selected rows to show that they have not been dragged.
74956      */
74957     afterRepair : function(){
74958         this.dragging = false;
74959     },
74960
74961     /**
74962      * <p>An empty implementation. Implement this to provide coordinates for the drag proxy to slide back to after an invalid drop.</p>
74963      * <p>Called before a repair of an invalid drop to get the XY to animate to.</p>
74964      * @param {EventObject} e The mouse up event
74965      * @return {Array} The xy location (e.g. [100, 200])
74966      */
74967     getRepairXY : function(e, data){
74968         return false;
74969     },
74970
74971     onEndDrag : function(data, e){
74972         // fire end drag?
74973     },
74974
74975     onValidDrop : function(dd, e, id){
74976         // fire drag drop?
74977         this.hideProxy();
74978     },
74979
74980     beforeInvalidDrop : function(e, id){
74981
74982     }
74983 });
74984 /**
74985  * @class Ext.grid.ColumnModel
74986  * @extends Ext.util.Observable
74987  * <p>After the data has been read into the client side cache (<b>{@link Ext.data.Store Store}</b>),
74988  * the ColumnModel is used to configure how and what parts of that data will be displayed in the
74989  * vertical slices (columns) of the grid. The Ext.grid.ColumnModel Class is the default implementation
74990  * of a ColumnModel used by implentations of {@link Ext.grid.GridPanel GridPanel}.</p>
74991  * <p>Data is mapped into the store's records and then indexed into the ColumnModel using the
74992  * <tt>{@link Ext.grid.Column#dataIndex dataIndex}</tt>:</p>
74993  * <pre><code>
74994 {data source} == mapping ==> {data store} == <b><tt>{@link Ext.grid.Column#dataIndex dataIndex}</tt></b> ==> {ColumnModel}
74995  * </code></pre>
74996  * <p>Each {@link Ext.grid.Column Column} in the grid's ColumnModel is configured with a
74997  * <tt>{@link Ext.grid.Column#dataIndex dataIndex}</tt> to specify how the data within
74998  * each record in the store is indexed into the ColumnModel.</p>
74999  * <p>There are two ways to initialize the ColumnModel class:</p>
75000  * <p><u>Initialization Method 1: an Array</u></p>
75001 <pre><code>
75002  var colModel = new Ext.grid.ColumnModel([
75003     { header: "Ticker", width: 60, sortable: true},
75004     { header: "Company Name", width: 150, sortable: true, id: 'company'},
75005     { header: "Market Cap.", width: 100, sortable: true},
75006     { header: "$ Sales", width: 100, sortable: true, renderer: money},
75007     { header: "Employees", width: 100, sortable: true, resizable: false}
75008  ]);
75009  </code></pre>
75010  * <p>The ColumnModel may be initialized with an Array of {@link Ext.grid.Column} column configuration
75011  * objects to define the initial layout / display of the columns in the Grid. The order of each
75012  * {@link Ext.grid.Column} column configuration object within the specified Array defines the initial
75013  * order of the column display.  A Column's display may be initially hidden using the
75014  * <tt>{@link Ext.grid.Column#hidden hidden}</tt></b> config property (and then shown using the column
75015  * header menu).  Fields that are not included in the ColumnModel will not be displayable at all.</p>
75016  * <p>How each column in the grid correlates (maps) to the {@link Ext.data.Record} field in the
75017  * {@link Ext.data.Store Store} the column draws its data from is configured through the
75018  * <b><tt>{@link Ext.grid.Column#dataIndex dataIndex}</tt></b>.  If the
75019  * <b><tt>{@link Ext.grid.Column#dataIndex dataIndex}</tt></b> is not explicitly defined (as shown in the
75020  * example above) it will use the column configuration's index in the Array as the index.</p>
75021  * <p>See <b><tt>{@link Ext.grid.Column}</tt></b> for additional configuration options for each column.</p>
75022  * <p><u>Initialization Method 2: an Object</u></p>
75023  * <p>In order to use configuration options from <tt>Ext.grid.ColumnModel</tt>, an Object may be used to
75024  * initialize the ColumnModel.  The column configuration Array will be specified in the <tt><b>{@link #columns}</b></tt>
75025  * config property. The <tt><b>{@link #defaults}</b></tt> config property can be used to apply defaults
75026  * for all columns, e.g.:</p><pre><code>
75027  var colModel = new Ext.grid.ColumnModel({
75028     columns: [
75029         { header: "Ticker", width: 60, menuDisabled: false},
75030         { header: "Company Name", width: 150, id: 'company'},
75031         { header: "Market Cap."},
75032         { header: "$ Sales", renderer: money},
75033         { header: "Employees", resizable: false}
75034     ],
75035     defaults: {
75036         sortable: true,
75037         menuDisabled: true,
75038         width: 100
75039     },
75040     listeners: {
75041         {@link #hiddenchange}: function(cm, colIndex, hidden) {
75042             saveConfig(colIndex, hidden);
75043         }
75044     }
75045 });
75046  </code></pre>
75047  * <p>In both examples above, the ability to apply a CSS class to all cells in a column (including the
75048  * header) is demonstrated through the use of the <b><tt>{@link Ext.grid.Column#id id}</tt></b> config
75049  * option. This column could be styled by including the following css:</p><pre><code>
75050  //add this css *after* the core css is loaded
75051 .x-grid3-td-company {
75052     color: red; // entire column will have red font
75053 }
75054 // modify the header row only, adding an icon to the column header
75055 .x-grid3-hd-company {
75056     background: transparent
75057         url(../../resources/images/icons/silk/building.png)
75058         no-repeat 3px 3px ! important;
75059         padding-left:20px;
75060 }
75061  </code></pre>
75062  * Note that the "Company Name" column could be specified as the
75063  * <b><tt>{@link Ext.grid.GridPanel}.{@link Ext.grid.GridPanel#autoExpandColumn autoExpandColumn}</tt></b>.
75064  * @constructor
75065  * @param {Mixed} config Specify either an Array of {@link Ext.grid.Column} configuration objects or specify
75066  * a configuration Object (see introductory section discussion utilizing Initialization Method 2 above).
75067  */
75068 Ext.grid.ColumnModel = Ext.extend(Ext.util.Observable, {
75069     /**
75070      * @cfg {Number} defaultWidth (optional) The width of columns which have no <tt>{@link #width}</tt>
75071      * specified (defaults to <tt>100</tt>).  This property shall preferably be configured through the
75072      * <tt><b>{@link #defaults}</b></tt> config property.
75073      */
75074     defaultWidth: 100,
75075
75076     /**
75077      * @cfg {Boolean} defaultSortable (optional) Default sortable of columns which have no
75078      * sortable specified (defaults to <tt>false</tt>).  This property shall preferably be configured
75079      * through the <tt><b>{@link #defaults}</b></tt> config property.
75080      */
75081     defaultSortable: false,
75082
75083     /**
75084      * @cfg {Array} columns An Array of object literals.  The config options defined by
75085      * <b>{@link Ext.grid.Column}</b> are the options which may appear in the object literal for each
75086      * individual column definition.
75087      */
75088
75089     /**
75090      * @cfg {Object} defaults Object literal which will be used to apply {@link Ext.grid.Column}
75091      * configuration options to all <tt><b>{@link #columns}</b></tt>.  Configuration options specified with
75092      * individual {@link Ext.grid.Column column} configs will supersede these <tt><b>{@link #defaults}</b></tt>.
75093      */
75094
75095     constructor : function(config) {
75096         /**
75097              * An Array of {@link Ext.grid.Column Column definition} objects representing the configuration
75098              * of this ColumnModel.  See {@link Ext.grid.Column} for the configuration properties that may
75099              * be specified.
75100              * @property config
75101              * @type Array
75102              */
75103             if (config.columns) {
75104                 Ext.apply(this, config);
75105                 this.setConfig(config.columns, true);
75106             } else {
75107                 this.setConfig(config, true);
75108             }
75109             
75110             this.addEvents(
75111                 /**
75112                  * @event widthchange
75113                  * Fires when the width of a column is programmaticially changed using
75114                  * <code>{@link #setColumnWidth}</code>.
75115                  * Note internal resizing suppresses the event from firing. See also
75116                  * {@link Ext.grid.GridPanel}.<code>{@link #columnresize}</code>.
75117                  * @param {ColumnModel} this
75118                  * @param {Number} columnIndex The column index
75119                  * @param {Number} newWidth The new width
75120                  */
75121                 "widthchange",
75122                 
75123                 /**
75124                  * @event headerchange
75125                  * Fires when the text of a header changes.
75126                  * @param {ColumnModel} this
75127                  * @param {Number} columnIndex The column index
75128                  * @param {String} newText The new header text
75129                  */
75130                 "headerchange",
75131                 
75132                 /**
75133                  * @event hiddenchange
75134                  * Fires when a column is hidden or "unhidden".
75135                  * @param {ColumnModel} this
75136                  * @param {Number} columnIndex The column index
75137                  * @param {Boolean} hidden true if hidden, false otherwise
75138                  */
75139                 "hiddenchange",
75140                 
75141                 /**
75142                  * @event columnmoved
75143                  * Fires when a column is moved.
75144                  * @param {ColumnModel} this
75145                  * @param {Number} oldIndex
75146                  * @param {Number} newIndex
75147                  */
75148                 "columnmoved",
75149                 
75150                 /**
75151                  * @event configchange
75152                  * Fires when the configuration is changed
75153                  * @param {ColumnModel} this
75154                  */
75155                 "configchange"
75156             );
75157             
75158             Ext.grid.ColumnModel.superclass.constructor.call(this);
75159     },
75160
75161     /**
75162      * Returns the id of the column at the specified index.
75163      * @param {Number} index The column index
75164      * @return {String} the id
75165      */
75166     getColumnId : function(index) {
75167         return this.config[index].id;
75168     },
75169
75170     getColumnAt : function(index) {
75171         return this.config[index];
75172     },
75173
75174     /**
75175      * <p>Reconfigures this column model according to the passed Array of column definition objects.
75176      * For a description of the individual properties of a column definition object, see the
75177      * <a href="#Ext.grid.ColumnModel-configs">Config Options</a>.</p>
75178      * <p>Causes the {@link #configchange} event to be fired. A {@link Ext.grid.GridPanel GridPanel}
75179      * using this ColumnModel will listen for this event and refresh its UI automatically.</p>
75180      * @param {Array} config Array of Column definition objects.
75181      * @param {Boolean} initial Specify <tt>true</tt> to bypass cleanup which deletes the <tt>totalWidth</tt>
75182      * and destroys existing editors.
75183      */
75184     setConfig : function(config, initial) {
75185         var i, c, len;
75186         
75187         if (!initial) { // cleanup
75188             delete this.totalWidth;
75189             
75190             for (i = 0, len = this.config.length; i < len; i++) {
75191                 c = this.config[i];
75192                 
75193                 if (c.setEditor) {
75194                     //check here, in case we have a special column like a CheckboxSelectionModel
75195                     c.setEditor(null);
75196                 }
75197             }
75198         }
75199
75200         // backward compatibility
75201         this.defaults = Ext.apply({
75202             width: this.defaultWidth,
75203             sortable: this.defaultSortable
75204         }, this.defaults);
75205
75206         this.config = config;
75207         this.lookup = {};
75208
75209         for (i = 0, len = config.length; i < len; i++) {
75210             c = Ext.applyIf(config[i], this.defaults);
75211             
75212             // if no id, create one using column's ordinal position
75213             if (Ext.isEmpty(c.id)) {
75214                 c.id = i;
75215             }
75216             
75217             if (!c.isColumn) {
75218                 var Cls = Ext.grid.Column.types[c.xtype || 'gridcolumn'];
75219                 c = new Cls(c);
75220                 config[i] = c;
75221             }
75222             
75223             this.lookup[c.id] = c;
75224         }
75225         
75226         if (!initial) {
75227             this.fireEvent('configchange', this);
75228         }
75229     },
75230
75231     /**
75232      * Returns the column for a specified id.
75233      * @param {String} id The column id
75234      * @return {Object} the column
75235      */
75236     getColumnById : function(id) {
75237         return this.lookup[id];
75238     },
75239
75240     /**
75241      * Returns the index for a specified column id.
75242      * @param {String} id The column id
75243      * @return {Number} the index, or -1 if not found
75244      */
75245     getIndexById : function(id) {
75246         for (var i = 0, len = this.config.length; i < len; i++) {
75247             if (this.config[i].id == id) {
75248                 return i;
75249             }
75250         }
75251         return -1;
75252     },
75253
75254     /**
75255      * Moves a column from one position to another.
75256      * @param {Number} oldIndex The index of the column to move.
75257      * @param {Number} newIndex The position at which to reinsert the coolumn.
75258      */
75259     moveColumn : function(oldIndex, newIndex) {
75260         var config = this.config,
75261             c      = config[oldIndex];
75262             
75263         config.splice(oldIndex, 1);
75264         config.splice(newIndex, 0, c);
75265         this.dataMap = null;
75266         this.fireEvent("columnmoved", this, oldIndex, newIndex);
75267     },
75268
75269     /**
75270      * Returns the number of columns.
75271      * @param {Boolean} visibleOnly Optional. Pass as true to only include visible columns.
75272      * @return {Number}
75273      */
75274     getColumnCount : function(visibleOnly) {
75275         var length = this.config.length,
75276             c = 0,
75277             i;
75278         
75279         if (visibleOnly === true) {
75280             for (i = 0; i < length; i++) {
75281                 if (!this.isHidden(i)) {
75282                     c++;
75283                 }
75284             }
75285             
75286             return c;
75287         }
75288         
75289         return length;
75290     },
75291
75292     /**
75293      * Returns the column configs that return true by the passed function that is called
75294      * with (columnConfig, index)
75295 <pre><code>
75296 // returns an array of column config objects for all hidden columns
75297 var columns = grid.getColumnModel().getColumnsBy(function(c){
75298   return c.hidden;
75299 });
75300 </code></pre>
75301      * @param {Function} fn A function which, when passed a {@link Ext.grid.Column Column} object, must
75302      * return <code>true</code> if the column is to be included in the returned Array.
75303      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function
75304      * is executed. Defaults to this ColumnModel.
75305      * @return {Array} result
75306      */
75307     getColumnsBy : function(fn, scope) {
75308         var config = this.config,
75309             length = config.length,
75310             result = [],
75311             i, c;
75312             
75313         for (i = 0; i < length; i++){
75314             c = config[i];
75315             
75316             if (fn.call(scope || this, c, i) === true) {
75317                 result[result.length] = c;
75318             }
75319         }
75320         
75321         return result;
75322     },
75323
75324     /**
75325      * Returns true if the specified column is sortable.
75326      * @param {Number} col The column index
75327      * @return {Boolean}
75328      */
75329     isSortable : function(col) {
75330         return !!this.config[col].sortable;
75331     },
75332
75333     /**
75334      * Returns true if the specified column menu is disabled.
75335      * @param {Number} col The column index
75336      * @return {Boolean}
75337      */
75338     isMenuDisabled : function(col) {
75339         return !!this.config[col].menuDisabled;
75340     },
75341
75342     /**
75343      * Returns the rendering (formatting) function defined for the column.
75344      * @param {Number} col The column index.
75345      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
75346      */
75347     getRenderer : function(col) {
75348         return this.config[col].renderer || Ext.grid.ColumnModel.defaultRenderer;
75349     },
75350
75351     getRendererScope : function(col) {
75352         return this.config[col].scope;
75353     },
75354
75355     /**
75356      * Sets the rendering (formatting) function for a column.  See {@link Ext.util.Format} for some
75357      * default formatting functions.
75358      * @param {Number} col The column index
75359      * @param {Function} fn The function to use to process the cell's raw data
75360      * to return HTML markup for the grid view. The render function is called with
75361      * the following parameters:<ul>
75362      * <li><b>value</b> : Object<p class="sub-desc">The data value for the cell.</p></li>
75363      * <li><b>metadata</b> : Object<p class="sub-desc">An object in which you may set the following attributes:<ul>
75364      * <li><b>css</b> : String<p class="sub-desc">A CSS class name to add to the cell's TD element.</p></li>
75365      * <li><b>attr</b> : String<p class="sub-desc">An HTML attribute definition string to apply to the data container element <i>within</i> the table cell
75366      * (e.g. 'style="color:red;"').</p></li></ul></p></li>
75367      * <li><b>record</b> : Ext.data.record<p class="sub-desc">The {@link Ext.data.Record} from which the data was extracted.</p></li>
75368      * <li><b>rowIndex</b> : Number<p class="sub-desc">Row index</p></li>
75369      * <li><b>colIndex</b> : Number<p class="sub-desc">Column index</p></li>
75370      * <li><b>store</b> : Ext.data.Store<p class="sub-desc">The {@link Ext.data.Store} object from which the Record was extracted.</p></li></ul>
75371      */
75372     setRenderer : function(col, fn) {
75373         this.config[col].renderer = fn;
75374     },
75375
75376     /**
75377      * Returns the width for the specified column.
75378      * @param {Number} col The column index
75379      * @return {Number}
75380      */
75381     getColumnWidth : function(col) {
75382         var width = this.config[col].width;
75383         if(typeof width != 'number'){
75384             width = this.defaultWidth;
75385         }
75386         return width;
75387     },
75388
75389     /**
75390      * Sets the width for a column.
75391      * @param {Number} col The column index
75392      * @param {Number} width The new width
75393      * @param {Boolean} suppressEvent True to suppress firing the <code>{@link #widthchange}</code>
75394      * event. Defaults to false.
75395      */
75396     setColumnWidth : function(col, width, suppressEvent) {
75397         this.config[col].width = width;
75398         this.totalWidth = null;
75399         
75400         if (!suppressEvent) {
75401              this.fireEvent("widthchange", this, col, width);
75402         }
75403     },
75404
75405     /**
75406      * Returns the total width of all columns.
75407      * @param {Boolean} includeHidden True to include hidden column widths
75408      * @return {Number}
75409      */
75410     getTotalWidth : function(includeHidden) {
75411         if (!this.totalWidth) {
75412             this.totalWidth = 0;
75413             for (var i = 0, len = this.config.length; i < len; i++) {
75414                 if (includeHidden || !this.isHidden(i)) {
75415                     this.totalWidth += this.getColumnWidth(i);
75416                 }
75417             }
75418         }
75419         return this.totalWidth;
75420     },
75421
75422     /**
75423      * Returns the header for the specified column.
75424      * @param {Number} col The column index
75425      * @return {String}
75426      */
75427     getColumnHeader : function(col) {
75428         return this.config[col].header;
75429     },
75430
75431     /**
75432      * Sets the header for a column.
75433      * @param {Number} col The column index
75434      * @param {String} header The new header
75435      */
75436     setColumnHeader : function(col, header) {
75437         this.config[col].header = header;
75438         this.fireEvent("headerchange", this, col, header);
75439     },
75440
75441     /**
75442      * Returns the tooltip for the specified column.
75443      * @param {Number} col The column index
75444      * @return {String}
75445      */
75446     getColumnTooltip : function(col) {
75447             return this.config[col].tooltip;
75448     },
75449     /**
75450      * Sets the tooltip for a column.
75451      * @param {Number} col The column index
75452      * @param {String} tooltip The new tooltip
75453      */
75454     setColumnTooltip : function(col, tooltip) {
75455             this.config[col].tooltip = tooltip;
75456     },
75457
75458     /**
75459      * Returns the dataIndex for the specified column.
75460 <pre><code>
75461 // Get field name for the column
75462 var fieldName = grid.getColumnModel().getDataIndex(columnIndex);
75463 </code></pre>
75464      * @param {Number} col The column index
75465      * @return {String} The column's dataIndex
75466      */
75467     getDataIndex : function(col) {
75468         return this.config[col].dataIndex;
75469     },
75470
75471     /**
75472      * Sets the dataIndex for a column.
75473      * @param {Number} col The column index
75474      * @param {String} dataIndex The new dataIndex
75475      */
75476     setDataIndex : function(col, dataIndex) {
75477         this.config[col].dataIndex = dataIndex;
75478     },
75479
75480     /**
75481      * Finds the index of the first matching column for the given dataIndex.
75482      * @param {String} col The dataIndex to find
75483      * @return {Number} The column index, or -1 if no match was found
75484      */
75485     findColumnIndex : function(dataIndex) {
75486         var c = this.config;
75487         for(var i = 0, len = c.length; i < len; i++){
75488             if(c[i].dataIndex == dataIndex){
75489                 return i;
75490             }
75491         }
75492         return -1;
75493     },
75494
75495     /**
75496      * Returns true if the cell is editable.
75497 <pre><code>
75498 var store = new Ext.data.Store({...});
75499 var colModel = new Ext.grid.ColumnModel({
75500   columns: [...],
75501   isCellEditable: function(col, row) {
75502     var record = store.getAt(row);
75503     if (record.get('readonly')) { // replace with your condition
75504       return false;
75505     }
75506     return Ext.grid.ColumnModel.prototype.isCellEditable.call(this, col, row);
75507   }
75508 });
75509 var grid = new Ext.grid.GridPanel({
75510   store: store,
75511   colModel: colModel,
75512   ...
75513 });
75514 </code></pre>
75515      * @param {Number} colIndex The column index
75516      * @param {Number} rowIndex The row index
75517      * @return {Boolean}
75518      */
75519     isCellEditable : function(colIndex, rowIndex) {
75520         var c = this.config[colIndex],
75521             ed = c.editable;
75522
75523         //force boolean
75524         return !!(ed || (!Ext.isDefined(ed) && c.editor));
75525     },
75526
75527     /**
75528      * Returns the editor defined for the cell/column.
75529      * @param {Number} colIndex The column index
75530      * @param {Number} rowIndex The row index
75531      * @return {Ext.Editor} The {@link Ext.Editor Editor} that was created to wrap
75532      * the {@link Ext.form.Field Field} used to edit the cell.
75533      */
75534     getCellEditor : function(colIndex, rowIndex) {
75535         return this.config[colIndex].getCellEditor(rowIndex);
75536     },
75537
75538     /**
75539      * Sets if a column is editable.
75540      * @param {Number} col The column index
75541      * @param {Boolean} editable True if the column is editable
75542      */
75543     setEditable : function(col, editable) {
75544         this.config[col].editable = editable;
75545     },
75546
75547     /**
75548      * Returns <tt>true</tt> if the column is <code>{@link Ext.grid.Column#hidden hidden}</code>,
75549      * <tt>false</tt> otherwise.
75550      * @param {Number} colIndex The column index
75551      * @return {Boolean}
75552      */
75553     isHidden : function(colIndex) {
75554         return !!this.config[colIndex].hidden; // ensure returns boolean
75555     },
75556
75557     /**
75558      * Returns <tt>true</tt> if the column is <code>{@link Ext.grid.Column#fixed fixed}</code>,
75559      * <tt>false</tt> otherwise.
75560      * @param {Number} colIndex The column index
75561      * @return {Boolean}
75562      */
75563     isFixed : function(colIndex) {
75564         return !!this.config[colIndex].fixed;
75565     },
75566
75567     /**
75568      * Returns true if the column can be resized
75569      * @return {Boolean}
75570      */
75571     isResizable : function(colIndex) {
75572         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
75573     },
75574     
75575     /**
75576      * Sets if a column is hidden.
75577 <pre><code>
75578 myGrid.getColumnModel().setHidden(0, true); // hide column 0 (0 = the first column).
75579 </code></pre>
75580      * @param {Number} colIndex The column index
75581      * @param {Boolean} hidden True if the column is hidden
75582      */
75583     setHidden : function(colIndex, hidden) {
75584         var c = this.config[colIndex];
75585         if(c.hidden !== hidden){
75586             c.hidden = hidden;
75587             this.totalWidth = null;
75588             this.fireEvent("hiddenchange", this, colIndex, hidden);
75589         }
75590     },
75591
75592     /**
75593      * Sets the editor for a column and destroys the prior editor.
75594      * @param {Number} col The column index
75595      * @param {Object} editor The editor object
75596      */
75597     setEditor : function(col, editor) {
75598         this.config[col].setEditor(editor);
75599     },
75600
75601     /**
75602      * Destroys this column model by purging any event listeners. Destroys and dereferences all Columns.
75603      */
75604     destroy : function() {
75605         var length = this.config.length,
75606             i = 0;
75607
75608         for (; i < length; i++){
75609             this.config[i].destroy(); // Column's destroy encapsulates all cleanup.
75610         }
75611         delete this.config;
75612         delete this.lookup;
75613         this.purgeListeners();
75614     },
75615
75616     /**
75617      * @private
75618      * Setup any saved state for the column, ensures that defaults are applied.
75619      */
75620     setState : function(col, state) {
75621         state = Ext.applyIf(state, this.defaults);
75622         Ext.apply(this.config[col], state);
75623     }
75624 });
75625
75626 // private
75627 Ext.grid.ColumnModel.defaultRenderer = function(value) {
75628     if (typeof value == "string" && value.length < 1) {
75629         return "&#160;";
75630     }
75631     return value;
75632 };/**
75633  * @class Ext.grid.AbstractSelectionModel
75634  * @extends Ext.util.Observable
75635  * Abstract base class for grid SelectionModels.  It provides the interface that should be
75636  * implemented by descendant classes.  This class should not be directly instantiated.
75637  * @constructor
75638  */
75639 Ext.grid.AbstractSelectionModel = Ext.extend(Ext.util.Observable,  {
75640     /**
75641      * The GridPanel for which this SelectionModel is handling selection. Read-only.
75642      * @type Object
75643      * @property grid
75644      */
75645
75646     constructor : function(){
75647         this.locked = false;
75648         Ext.grid.AbstractSelectionModel.superclass.constructor.call(this);
75649     },
75650
75651     /** @ignore Called by the grid automatically. Do not call directly. */
75652     init : function(grid){
75653         this.grid = grid;
75654         if(this.lockOnInit){
75655             delete this.lockOnInit;
75656             this.locked = false;
75657             this.lock();
75658         }
75659         this.initEvents();
75660     },
75661
75662     /**
75663      * Locks the selections.
75664      */
75665     lock : function(){
75666         if(!this.locked){
75667             this.locked = true;
75668             // If the grid has been set, then the view is already initialized.
75669             var g = this.grid;
75670             if(g){
75671                 g.getView().on({
75672                     scope: this,
75673                     beforerefresh: this.sortUnLock,
75674                     refresh: this.sortLock
75675                 });
75676             }else{
75677                 this.lockOnInit = true;
75678             }
75679         }
75680     },
75681
75682     // set the lock states before and after a view refresh
75683     sortLock : function() {
75684         this.locked = true;
75685     },
75686
75687     // set the lock states before and after a view refresh
75688     sortUnLock : function() {
75689         this.locked = false;
75690     },
75691
75692     /**
75693      * Unlocks the selections.
75694      */
75695     unlock : function(){
75696         if(this.locked){
75697             this.locked = false;
75698             var g = this.grid,
75699                 gv;
75700                 
75701             // If the grid has been set, then the view is already initialized.
75702             if(g){
75703                 gv = g.getView();
75704                 gv.un('beforerefresh', this.sortUnLock, this);
75705                 gv.un('refresh', this.sortLock, this);    
75706             }else{
75707                 delete this.lockOnInit;
75708             }
75709         }
75710     },
75711
75712     /**
75713      * Returns true if the selections are locked.
75714      * @return {Boolean}
75715      */
75716     isLocked : function(){
75717         return this.locked;
75718     },
75719
75720     destroy: function(){
75721         this.unlock();
75722         this.purgeListeners();
75723     }
75724 });/**
75725  * @class Ext.grid.RowSelectionModel
75726  * @extends Ext.grid.AbstractSelectionModel
75727  * The default SelectionModel used by {@link Ext.grid.GridPanel}.
75728  * It supports multiple selections and keyboard selection/navigation. The objects stored
75729  * as selections and returned by {@link #getSelected}, and {@link #getSelections} are
75730  * the {@link Ext.data.Record Record}s which provide the data for the selected rows.
75731  * @constructor
75732  * @param {Object} config
75733  */
75734 Ext.grid.RowSelectionModel = Ext.extend(Ext.grid.AbstractSelectionModel,  {
75735     /**
75736      * @cfg {Boolean} singleSelect
75737      * <tt>true</tt> to allow selection of only one row at a time (defaults to <tt>false</tt>
75738      * allowing multiple selections)
75739      */
75740     singleSelect : false,
75741     
75742     constructor : function(config){
75743         Ext.apply(this, config);
75744         this.selections = new Ext.util.MixedCollection(false, function(o){
75745             return o.id;
75746         });
75747
75748         this.last = false;
75749         this.lastActive = false;
75750
75751         this.addEvents(
75752                 /**
75753                  * @event selectionchange
75754                  * Fires when the selection changes
75755                  * @param {SelectionModel} this
75756                  */
75757                 'selectionchange',
75758                 /**
75759                  * @event beforerowselect
75760                  * Fires before a row is selected, return false to cancel the selection.
75761                  * @param {SelectionModel} this
75762                  * @param {Number} rowIndex The index to be selected
75763                  * @param {Boolean} keepExisting False if other selections will be cleared
75764                  * @param {Record} record The record to be selected
75765                  */
75766                 'beforerowselect',
75767                 /**
75768                  * @event rowselect
75769                  * Fires when a row is selected.
75770                  * @param {SelectionModel} this
75771                  * @param {Number} rowIndex The selected index
75772                  * @param {Ext.data.Record} r The selected record
75773                  */
75774                 'rowselect',
75775                 /**
75776                  * @event rowdeselect
75777                  * Fires when a row is deselected.  To prevent deselection
75778                  * {@link Ext.grid.AbstractSelectionModel#lock lock the selections}. 
75779                  * @param {SelectionModel} this
75780                  * @param {Number} rowIndex
75781                  * @param {Record} record
75782                  */
75783                 'rowdeselect'
75784         );
75785         Ext.grid.RowSelectionModel.superclass.constructor.call(this);
75786     },
75787
75788     /**
75789      * @cfg {Boolean} moveEditorOnEnter
75790      * <tt>false</tt> to turn off moving the editor to the next row down when the enter key is pressed
75791      * or the next row up when shift + enter keys are pressed.
75792      */
75793     // private
75794     initEvents : function(){
75795
75796         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
75797             this.grid.on('rowmousedown', this.handleMouseDown, this);
75798         }
75799
75800         this.rowNav = new Ext.KeyNav(this.grid.getGridEl(), {
75801             up: this.onKeyPress, 
75802             down: this.onKeyPress,
75803             scope: this
75804         });
75805
75806         this.grid.getView().on({
75807             scope: this,
75808             refresh: this.onRefresh,
75809             rowupdated: this.onRowUpdated,
75810             rowremoved: this.onRemove
75811         });
75812     },
75813     
75814     onKeyPress : function(e, name){
75815         var up = name == 'up',
75816             method = up ? 'selectPrevious' : 'selectNext',
75817             add = up ? -1 : 1,
75818             last;
75819         if(!e.shiftKey || this.singleSelect){
75820             this[method](false);
75821         }else if(this.last !== false && this.lastActive !== false){
75822             last = this.last;
75823             this.selectRange(this.last,  this.lastActive + add);
75824             this.grid.getView().focusRow(this.lastActive);
75825             if(last !== false){
75826                 this.last = last;
75827             }
75828         }else{
75829            this.selectFirstRow();
75830         }
75831     },
75832
75833     // private
75834     onRefresh : function(){
75835         var ds = this.grid.store,
75836             s = this.getSelections(),
75837             i = 0,
75838             len = s.length, 
75839             index, r;
75840             
75841         this.silent = true;
75842         this.clearSelections(true);
75843         for(; i < len; i++){
75844             r = s[i];
75845             if((index = ds.indexOfId(r.id)) != -1){
75846                 this.selectRow(index, true);
75847             }
75848         }
75849         if(s.length != this.selections.getCount()){
75850             this.fireEvent('selectionchange', this);
75851         }
75852         this.silent = false;
75853     },
75854
75855     // private
75856     onRemove : function(v, index, r){
75857         if(this.selections.remove(r) !== false){
75858             this.fireEvent('selectionchange', this);
75859         }
75860     },
75861
75862     // private
75863     onRowUpdated : function(v, index, r){
75864         if(this.isSelected(r)){
75865             v.onRowSelect(index);
75866         }
75867     },
75868
75869     /**
75870      * Select records.
75871      * @param {Array} records The records to select
75872      * @param {Boolean} keepExisting (optional) <tt>true</tt> to keep existing selections
75873      */
75874     selectRecords : function(records, keepExisting){
75875         if(!keepExisting){
75876             this.clearSelections();
75877         }
75878         var ds = this.grid.store,
75879             i = 0,
75880             len = records.length;
75881         for(; i < len; i++){
75882             this.selectRow(ds.indexOf(records[i]), true);
75883         }
75884     },
75885
75886     /**
75887      * Gets the number of selected rows.
75888      * @return {Number}
75889      */
75890     getCount : function(){
75891         return this.selections.length;
75892     },
75893
75894     /**
75895      * Selects the first row in the grid.
75896      */
75897     selectFirstRow : function(){
75898         this.selectRow(0);
75899     },
75900
75901     /**
75902      * Select the last row.
75903      * @param {Boolean} keepExisting (optional) <tt>true</tt> to keep existing selections
75904      */
75905     selectLastRow : function(keepExisting){
75906         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
75907     },
75908
75909     /**
75910      * Selects the row immediately following the last selected row.
75911      * @param {Boolean} keepExisting (optional) <tt>true</tt> to keep existing selections
75912      * @return {Boolean} <tt>true</tt> if there is a next row, else <tt>false</tt>
75913      */
75914     selectNext : function(keepExisting){
75915         if(this.hasNext()){
75916             this.selectRow(this.last+1, keepExisting);
75917             this.grid.getView().focusRow(this.last);
75918             return true;
75919         }
75920         return false;
75921     },
75922
75923     /**
75924      * Selects the row that precedes the last selected row.
75925      * @param {Boolean} keepExisting (optional) <tt>true</tt> to keep existing selections
75926      * @return {Boolean} <tt>true</tt> if there is a previous row, else <tt>false</tt>
75927      */
75928     selectPrevious : function(keepExisting){
75929         if(this.hasPrevious()){
75930             this.selectRow(this.last-1, keepExisting);
75931             this.grid.getView().focusRow(this.last);
75932             return true;
75933         }
75934         return false;
75935     },
75936
75937     /**
75938      * Returns true if there is a next record to select
75939      * @return {Boolean}
75940      */
75941     hasNext : function(){
75942         return this.last !== false && (this.last+1) < this.grid.store.getCount();
75943     },
75944
75945     /**
75946      * Returns true if there is a previous record to select
75947      * @return {Boolean}
75948      */
75949     hasPrevious : function(){
75950         return !!this.last;
75951     },
75952
75953
75954     /**
75955      * Returns the selected records
75956      * @return {Array} Array of selected records
75957      */
75958     getSelections : function(){
75959         return [].concat(this.selections.items);
75960     },
75961
75962     /**
75963      * Returns the first selected record.
75964      * @return {Record}
75965      */
75966     getSelected : function(){
75967         return this.selections.itemAt(0);
75968     },
75969
75970     /**
75971      * Calls the passed function with each selection. If the function returns
75972      * <tt>false</tt>, iteration is stopped and this function returns
75973      * <tt>false</tt>. Otherwise it returns <tt>true</tt>.
75974      * @param {Function} fn The function to call upon each iteration. It is passed the selected {@link Ext.data.Record Record}.
75975      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this RowSelectionModel.
75976      * @return {Boolean} true if all selections were iterated
75977      */
75978     each : function(fn, scope){
75979         var s = this.getSelections(),
75980             i = 0,
75981             len = s.length;
75982             
75983         for(; i < len; i++){
75984             if(fn.call(scope || this, s[i], i) === false){
75985                 return false;
75986             }
75987         }
75988         return true;
75989     },
75990
75991     /**
75992      * Clears all selections if the selection model
75993      * {@link Ext.grid.AbstractSelectionModel#isLocked is not locked}.
75994      * @param {Boolean} fast (optional) <tt>true</tt> to bypass the
75995      * conditional checks and events described in {@link #deselectRow}.
75996      */
75997     clearSelections : function(fast){
75998         if(this.isLocked()){
75999             return;
76000         }
76001         if(fast !== true){
76002             var ds = this.grid.store,
76003                 s = this.selections;
76004             s.each(function(r){
76005                 this.deselectRow(ds.indexOfId(r.id));
76006             }, this);
76007             s.clear();
76008         }else{
76009             this.selections.clear();
76010         }
76011         this.last = false;
76012     },
76013
76014
76015     /**
76016      * Selects all rows if the selection model
76017      * {@link Ext.grid.AbstractSelectionModel#isLocked is not locked}. 
76018      */
76019     selectAll : function(){
76020         if(this.isLocked()){
76021             return;
76022         }
76023         this.selections.clear();
76024         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
76025             this.selectRow(i, true);
76026         }
76027     },
76028
76029     /**
76030      * Returns <tt>true</tt> if there is a selection.
76031      * @return {Boolean}
76032      */
76033     hasSelection : function(){
76034         return this.selections.length > 0;
76035     },
76036
76037     /**
76038      * Returns <tt>true</tt> if the specified row is selected.
76039      * @param {Number/Record} index The record or index of the record to check
76040      * @return {Boolean}
76041      */
76042     isSelected : function(index){
76043         var r = Ext.isNumber(index) ? this.grid.store.getAt(index) : index;
76044         return (r && this.selections.key(r.id) ? true : false);
76045     },
76046
76047     /**
76048      * Returns <tt>true</tt> if the specified record id is selected.
76049      * @param {String} id The id of record to check
76050      * @return {Boolean}
76051      */
76052     isIdSelected : function(id){
76053         return (this.selections.key(id) ? true : false);
76054     },
76055
76056     // private
76057     handleMouseDown : function(g, rowIndex, e){
76058         if(e.button !== 0 || this.isLocked()){
76059             return;
76060         }
76061         var view = this.grid.getView();
76062         if(e.shiftKey && !this.singleSelect && this.last !== false){
76063             var last = this.last;
76064             this.selectRange(last, rowIndex, e.ctrlKey);
76065             this.last = last; // reset the last
76066             view.focusRow(rowIndex);
76067         }else{
76068             var isSelected = this.isSelected(rowIndex);
76069             if(e.ctrlKey && isSelected){
76070                 this.deselectRow(rowIndex);
76071             }else if(!isSelected || this.getCount() > 1){
76072                 this.selectRow(rowIndex, e.ctrlKey || e.shiftKey);
76073                 view.focusRow(rowIndex);
76074             }
76075         }
76076     },
76077
76078     /**
76079      * Selects multiple rows.
76080      * @param {Array} rows Array of the indexes of the row to select
76081      * @param {Boolean} keepExisting (optional) <tt>true</tt> to keep
76082      * existing selections (defaults to <tt>false</tt>)
76083      */
76084     selectRows : function(rows, keepExisting){
76085         if(!keepExisting){
76086             this.clearSelections();
76087         }
76088         for(var i = 0, len = rows.length; i < len; i++){
76089             this.selectRow(rows[i], true);
76090         }
76091     },
76092
76093     /**
76094      * Selects a range of rows if the selection model
76095      * {@link Ext.grid.AbstractSelectionModel#isLocked is not locked}.
76096      * All rows in between startRow and endRow are also selected.
76097      * @param {Number} startRow The index of the first row in the range
76098      * @param {Number} endRow The index of the last row in the range
76099      * @param {Boolean} keepExisting (optional) True to retain existing selections
76100      */
76101     selectRange : function(startRow, endRow, keepExisting){
76102         var i;
76103         if(this.isLocked()){
76104             return;
76105         }
76106         if(!keepExisting){
76107             this.clearSelections();
76108         }
76109         if(startRow <= endRow){
76110             for(i = startRow; i <= endRow; i++){
76111                 this.selectRow(i, true);
76112             }
76113         }else{
76114             for(i = startRow; i >= endRow; i--){
76115                 this.selectRow(i, true);
76116             }
76117         }
76118     },
76119
76120     /**
76121      * Deselects a range of rows if the selection model
76122      * {@link Ext.grid.AbstractSelectionModel#isLocked is not locked}.  
76123      * All rows in between startRow and endRow are also deselected.
76124      * @param {Number} startRow The index of the first row in the range
76125      * @param {Number} endRow The index of the last row in the range
76126      */
76127     deselectRange : function(startRow, endRow, preventViewNotify){
76128         if(this.isLocked()){
76129             return;
76130         }
76131         for(var i = startRow; i <= endRow; i++){
76132             this.deselectRow(i, preventViewNotify);
76133         }
76134     },
76135
76136     /**
76137      * Selects a row.  Before selecting a row, checks if the selection model
76138      * {@link Ext.grid.AbstractSelectionModel#isLocked is locked} and fires the
76139      * {@link #beforerowselect} event.  If these checks are satisfied the row
76140      * will be selected and followed up by  firing the {@link #rowselect} and
76141      * {@link #selectionchange} events.
76142      * @param {Number} row The index of the row to select
76143      * @param {Boolean} keepExisting (optional) <tt>true</tt> to keep existing selections
76144      * @param {Boolean} preventViewNotify (optional) Specify <tt>true</tt> to
76145      * prevent notifying the view (disables updating the selected appearance)
76146      */
76147     selectRow : function(index, keepExisting, preventViewNotify){
76148         if(this.isLocked() || (index < 0 || index >= this.grid.store.getCount()) || (keepExisting && this.isSelected(index))){
76149             return;
76150         }
76151         var r = this.grid.store.getAt(index);
76152         if(r && this.fireEvent('beforerowselect', this, index, keepExisting, r) !== false){
76153             if(!keepExisting || this.singleSelect){
76154                 this.clearSelections();
76155             }
76156             this.selections.add(r);
76157             this.last = this.lastActive = index;
76158             if(!preventViewNotify){
76159                 this.grid.getView().onRowSelect(index);
76160             }
76161             if(!this.silent){
76162                 this.fireEvent('rowselect', this, index, r);
76163                 this.fireEvent('selectionchange', this);
76164             }
76165         }
76166     },
76167
76168     /**
76169      * Deselects a row.  Before deselecting a row, checks if the selection model
76170      * {@link Ext.grid.AbstractSelectionModel#isLocked is locked}.
76171      * If this check is satisfied the row will be deselected and followed up by
76172      * firing the {@link #rowdeselect} and {@link #selectionchange} events.
76173      * @param {Number} row The index of the row to deselect
76174      * @param {Boolean} preventViewNotify (optional) Specify <tt>true</tt> to
76175      * prevent notifying the view (disables updating the selected appearance)
76176      */
76177     deselectRow : function(index, preventViewNotify){
76178         if(this.isLocked()){
76179             return;
76180         }
76181         if(this.last == index){
76182             this.last = false;
76183         }
76184         if(this.lastActive == index){
76185             this.lastActive = false;
76186         }
76187         var r = this.grid.store.getAt(index);
76188         if(r){
76189             this.selections.remove(r);
76190             if(!preventViewNotify){
76191                 this.grid.getView().onRowDeselect(index);
76192             }
76193             this.fireEvent('rowdeselect', this, index, r);
76194             this.fireEvent('selectionchange', this);
76195         }
76196     },
76197
76198     // private
76199     acceptsNav : function(row, col, cm){
76200         return !cm.isHidden(col) && cm.isCellEditable(col, row);
76201     },
76202
76203     // private
76204     onEditorKey : function(field, e){
76205         var k = e.getKey(), 
76206             newCell, 
76207             g = this.grid, 
76208             last = g.lastEdit,
76209             ed = g.activeEditor,
76210             shift = e.shiftKey,
76211             ae, last, r, c;
76212             
76213         if(k == e.TAB){
76214             e.stopEvent();
76215             ed.completeEdit();
76216             if(shift){
76217                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
76218             }else{
76219                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
76220             }
76221         }else if(k == e.ENTER){
76222             if(this.moveEditorOnEnter !== false){
76223                 if(shift){
76224                     newCell = g.walkCells(last.row - 1, last.col, -1, this.acceptsNav, this);
76225                 }else{
76226                     newCell = g.walkCells(last.row + 1, last.col, 1, this.acceptsNav, this);
76227                 }
76228             }
76229         }
76230         if(newCell){
76231             r = newCell[0];
76232             c = newCell[1];
76233
76234             this.onEditorSelect(r, last.row);
76235
76236             if(g.isEditor && g.editing){ // *** handle tabbing while editorgrid is in edit mode
76237                 ae = g.activeEditor;
76238                 if(ae && ae.field.triggerBlur){
76239                     // *** if activeEditor is a TriggerField, explicitly call its triggerBlur() method
76240                     ae.field.triggerBlur();
76241                 }
76242             }
76243             g.startEditing(r, c);
76244         }
76245     },
76246     
76247     onEditorSelect: function(row, lastRow){
76248         if(lastRow != row){
76249             this.selectRow(row); // *** highlight newly-selected cell and update selection
76250         }
76251     },
76252     
76253     destroy : function(){
76254         Ext.destroy(this.rowNav);
76255         this.rowNav = null;
76256         Ext.grid.RowSelectionModel.superclass.destroy.call(this);
76257     }
76258 });
76259 /**
76260  * @class Ext.grid.Column
76261  * <p>This class encapsulates column configuration data to be used in the initialization of a
76262  * {@link Ext.grid.ColumnModel ColumnModel}.</p>
76263  * <p>While subclasses are provided to render data in different ways, this class renders a passed
76264  * data field unchanged and is usually used for textual columns.</p>
76265  */
76266 Ext.grid.Column = Ext.extend(Ext.util.Observable, {
76267     /**
76268      * @cfg {Boolean} editable Optional. Defaults to <tt>true</tt>, enabling the configured
76269      * <tt>{@link #editor}</tt>.  Set to <tt>false</tt> to initially disable editing on this column.
76270      * The initial configuration may be dynamically altered using
76271      * {@link Ext.grid.ColumnModel}.{@link Ext.grid.ColumnModel#setEditable setEditable()}.
76272      */
76273     /**
76274      * @cfg {String} id Optional. A name which identifies this column (defaults to the column's initial
76275      * ordinal position.) The <tt>id</tt> is used to create a CSS <b>class</b> name which is applied to all
76276      * table cells (including headers) in that column (in this context the <tt>id</tt> does not need to be
76277      * unique). The class name takes the form of <pre>x-grid3-td-<b>id</b></pre>
76278      * Header cells will also receive this class name, but will also have the class <pre>x-grid3-hd</pre>
76279      * So, to target header cells, use CSS selectors such as:<pre>.x-grid3-hd-row .x-grid3-td-<b>id</b></pre>
76280      * The {@link Ext.grid.GridPanel#autoExpandColumn} grid config option references the column via this
76281      * unique identifier.
76282      */
76283     /**
76284      * @cfg {String} header Optional. The header text to be used as innerHTML
76285      * (html tags are accepted) to display in the Grid view.  <b>Note</b>: to
76286      * have a clickable header with no text displayed use <tt>'&amp;#160;'</tt>.
76287      */
76288     /**
76289      * @cfg {Boolean} groupable Optional. If the grid is being rendered by an {@link Ext.grid.GroupingView}, this option
76290      * may be used to disable the header menu item to group by the column selected. Defaults to <tt>true</tt>,
76291      * which enables the header menu group option.  Set to <tt>false</tt> to disable (but still show) the
76292      * group option in the header menu for the column. See also <code>{@link #groupName}</code>.
76293      */
76294     /**
76295      * @cfg {String} groupName Optional. If the grid is being rendered by an {@link Ext.grid.GroupingView}, this option
76296      * may be used to specify the text with which to prefix the group field value in the group header line.
76297      * See also {@link #groupRenderer} and
76298      * {@link Ext.grid.GroupingView}.{@link Ext.grid.GroupingView#showGroupName showGroupName}.
76299      */
76300     /**
76301      * @cfg {Function} groupRenderer <p>Optional. If the grid is being rendered by an {@link Ext.grid.GroupingView}, this option
76302      * may be used to specify the function used to format the grouping field value for display in the group
76303      * {@link #groupName header}.  If a <tt><b>groupRenderer</b></tt> is not specified, the configured
76304      * <tt><b>{@link #renderer}</b></tt> will be called; if a <tt><b>{@link #renderer}</b></tt> is also not specified
76305      * the new value of the group field will be used.</p>
76306      * <p>The called function (either the <tt><b>groupRenderer</b></tt> or <tt><b>{@link #renderer}</b></tt>) will be
76307      * passed the following parameters:
76308      * <div class="mdetail-params"><ul>
76309      * <li><b>v</b> : Object<p class="sub-desc">The new value of the group field.</p></li>
76310      * <li><b>unused</b> : undefined<p class="sub-desc">Unused parameter.</p></li>
76311      * <li><b>r</b> : Ext.data.Record<p class="sub-desc">The Record providing the data
76312      * for the row which caused group change.</p></li>
76313      * <li><b>rowIndex</b> : Number<p class="sub-desc">The row index of the Record which caused group change.</p></li>
76314      * <li><b>colIndex</b> : Number<p class="sub-desc">The column index of the group field.</p></li>
76315      * <li><b>ds</b> : Ext.data.Store<p class="sub-desc">The Store which is providing the data Model.</p></li>
76316      * </ul></div></p>
76317      * <p>The function should return a string value.</p>
76318      */
76319     /**
76320      * @cfg {String} emptyGroupText Optional. If the grid is being rendered by an {@link Ext.grid.GroupingView}, this option
76321      * may be used to specify the text to display when there is an empty group value. Defaults to the
76322      * {@link Ext.grid.GroupingView}.{@link Ext.grid.GroupingView#emptyGroupText emptyGroupText}.
76323      */
76324     /**
76325      * @cfg {String} dataIndex <p><b>Required</b>. The name of the field in the
76326      * grid's {@link Ext.data.Store}'s {@link Ext.data.Record} definition from
76327      * which to draw the column's value.</p>
76328      */
76329     /**
76330      * @cfg {Number} width
76331      * Optional. The initial width in pixels of the column.
76332      * The width of each column can also be affected if any of the following are configured:
76333      * <div class="mdetail-params"><ul>
76334      * <li>{@link Ext.grid.GridPanel}.<tt>{@link Ext.grid.GridPanel#autoExpandColumn autoExpandColumn}</tt></li>
76335      * <li>{@link Ext.grid.GridView}.<tt>{@link Ext.grid.GridView#forceFit forceFit}</tt>
76336      * <div class="sub-desc">
76337      * <p>By specifying <tt>forceFit:true</tt>, {@link #fixed non-fixed width} columns will be
76338      * re-proportioned (based on the relative initial widths) to fill the width of the grid so
76339      * that no horizontal scrollbar is shown.</p>
76340      * </div></li>
76341      * <li>{@link Ext.grid.GridView}.<tt>{@link Ext.grid.GridView#autoFill autoFill}</tt></li>
76342      * <li>{@link Ext.grid.GridPanel}.<tt>{@link Ext.grid.GridPanel#minColumnWidth minColumnWidth}</tt></li>
76343      * <br><p><b>Note</b>: when the width of each column is determined, a space on the right side
76344      * is reserved for the vertical scrollbar.  The
76345      * {@link Ext.grid.GridView}.<tt>{@link Ext.grid.GridView#scrollOffset scrollOffset}</tt>
76346      * can be modified to reduce or eliminate the reserved offset.</p>
76347      */
76348     /**
76349      * @cfg {Boolean} sortable Optional. <tt>true</tt> if sorting is to be allowed on this column.
76350      * Defaults to the value of the <code>{@link Ext.grid.ColumnModel#defaultSortable}</code> property.
76351      * Whether local/remote sorting is used is specified in <code>{@link Ext.data.Store#remoteSort}</code>.
76352      */
76353     /**
76354      * @cfg {Boolean} fixed Optional. <tt>true</tt> if the column width cannot be changed.  Defaults to <tt>false</tt>.
76355      */
76356     /**
76357      * @cfg {Boolean} resizable Optional. <tt>false</tt> to disable column resizing. Defaults to <tt>true</tt>.
76358      */
76359     /**
76360      * @cfg {Boolean} menuDisabled Optional. <tt>true</tt> to disable the column menu. Defaults to <tt>false</tt>.
76361      */
76362     /**
76363      * @cfg {Boolean} hidden
76364      * Optional. <tt>true</tt> to initially hide this column. Defaults to <tt>false</tt>.
76365      * A hidden column {@link Ext.grid.GridPanel#enableColumnHide may be shown via the header row menu}.
76366      * If a column is never to be shown, simply do not include this column in the Column Model at all.
76367      */
76368     /**
76369      * @cfg {String} tooltip Optional. A text string to use as the column header's tooltip.  If Quicktips
76370      * are enabled, this value will be used as the text of the quick tip, otherwise it will be set as the
76371      * header's HTML title attribute. Defaults to ''.
76372      */
76373     /**
76374      * @cfg {Mixed} renderer
76375      * <p>For an alternative to specifying a renderer see <code>{@link #xtype}</code></p>
76376      * <p>Optional. A renderer is an 'interceptor' method which can be used transform data (value,
76377      * appearance, etc.) before it is rendered). This may be specified in either of three ways:
76378      * <div class="mdetail-params"><ul>
76379      * <li>A renderer function used to return HTML markup for a cell given the cell's data value.</li>
76380      * <li>A string which references a property name of the {@link Ext.util.Format} class which
76381      * provides a renderer function.</li>
76382      * <li>An object specifying both the renderer function, and its execution scope (<tt><b>this</b></tt>
76383      * reference) e.g.:<pre style="margin-left:1.2em"><code>
76384 {
76385     fn: this.gridRenderer,
76386     scope: this
76387 }
76388 </code></pre></li></ul></div>
76389      * If not specified, the default renderer uses the raw data value.</p>
76390      * <p>For information about the renderer function (passed parameters, etc.), see
76391      * {@link Ext.grid.ColumnModel#setRenderer}. An example of specifying renderer function inline:</p><pre><code>
76392 var companyColumn = {
76393    header: 'Company Name',
76394    dataIndex: 'company',
76395    renderer: function(value, metaData, record, rowIndex, colIndex, store) {
76396       // provide the logic depending on business rules
76397       // name of your own choosing to manipulate the cell depending upon
76398       // the data in the underlying Record object.
76399       if (value == 'whatever') {
76400           //metaData.css : String : A CSS class name to add to the TD element of the cell.
76401           //metaData.attr : String : An html attribute definition string to apply to
76402           //                         the data container element within the table
76403           //                         cell (e.g. 'style="color:red;"').
76404           metaData.css = 'name-of-css-class-you-will-define';
76405       }
76406       return value;
76407    }
76408 }
76409      * </code></pre>
76410      * See also {@link #scope}.
76411      */
76412     /**
76413      * @cfg {String} xtype Optional. A String which references a predefined {@link Ext.grid.Column} subclass
76414      * type which is preconfigured with an appropriate <code>{@link #renderer}</code> to be easily
76415      * configured into a ColumnModel. The predefined {@link Ext.grid.Column} subclass types are:
76416      * <div class="mdetail-params"><ul>
76417      * <li><b><tt>gridcolumn</tt></b> : {@link Ext.grid.Column} (<b>Default</b>)<p class="sub-desc"></p></li>
76418      * <li><b><tt>booleancolumn</tt></b> : {@link Ext.grid.BooleanColumn}<p class="sub-desc"></p></li>
76419      * <li><b><tt>numbercolumn</tt></b> : {@link Ext.grid.NumberColumn}<p class="sub-desc"></p></li>
76420      * <li><b><tt>datecolumn</tt></b> : {@link Ext.grid.DateColumn}<p class="sub-desc"></p></li>
76421      * <li><b><tt>templatecolumn</tt></b> : {@link Ext.grid.TemplateColumn}<p class="sub-desc"></p></li>
76422      * </ul></div>
76423      * <p>Configuration properties for the specified <code>xtype</code> may be specified with
76424      * the Column configuration properties, for example:</p>
76425      * <pre><code>
76426 var grid = new Ext.grid.GridPanel({
76427     ...
76428     columns: [{
76429         header: 'Last Updated',
76430         dataIndex: 'lastChange',
76431         width: 85,
76432         sortable: true,
76433         //renderer: Ext.util.Format.dateRenderer('m/d/Y'),
76434         xtype: 'datecolumn', // use xtype instead of renderer
76435         format: 'M/d/Y' // configuration property for {@link Ext.grid.DateColumn}
76436     }, {
76437         ...
76438     }]
76439 });
76440      * </code></pre>
76441      */
76442     /**
76443      * @cfg {Object} scope Optional. The scope (<tt><b>this</b></tt> reference) in which to execute the
76444      * renderer.  Defaults to the Column configuration object.
76445      */
76446     /**
76447      * @cfg {String} align Optional. Set the CSS text-align property of the column.  Defaults to undefined.
76448      */
76449     /**
76450      * @cfg {String} css Optional. An inline style definition string which is applied to all table cells in the column
76451      * (excluding headers). Defaults to undefined.
76452      */
76453     /**
76454      * @cfg {Boolean} hideable Optional. Specify as <tt>false</tt> to prevent the user from hiding this column
76455      * (defaults to true).  To disallow column hiding globally for all columns in the grid, use
76456      * {@link Ext.grid.GridPanel#enableColumnHide} instead.
76457      */
76458     /**
76459      * @cfg {Ext.form.Field} editor Optional. The {@link Ext.form.Field} to use when editing values in this column
76460      * if editing is supported by the grid. See <tt>{@link #editable}</tt> also.
76461      */
76462
76463     /**
76464      * @private
76465      * @cfg {Boolean} isColumn
76466      * Used by ColumnModel setConfig method to avoid reprocessing a Column
76467      * if <code>isColumn</code> is not set ColumnModel will recreate a new Ext.grid.Column
76468      * Defaults to true.
76469      */
76470     isColumn : true,
76471
76472     constructor : function(config){
76473         Ext.apply(this, config);
76474
76475         if(Ext.isString(this.renderer)){
76476             this.renderer = Ext.util.Format[this.renderer];
76477         }else if(Ext.isObject(this.renderer)){
76478             this.scope = this.renderer.scope;
76479             this.renderer = this.renderer.fn;
76480         }
76481         if(!this.scope){
76482             this.scope = this;
76483         }
76484
76485         var ed = this.editor;
76486         delete this.editor;
76487         this.setEditor(ed);
76488         this.addEvents(
76489             /**
76490              * @event click
76491              * Fires when this Column is clicked.
76492              * @param {Column} this
76493              * @param {Grid} The owning GridPanel
76494              * @param {Number} rowIndex
76495              * @param {Ext.EventObject} e
76496              */
76497             'click',
76498             /**
76499              * @event contextmenu
76500              * Fires when this Column is right clicked.
76501              * @param {Column} this
76502              * @param {Grid} The owning GridPanel
76503              * @param {Number} rowIndex
76504              * @param {Ext.EventObject} e
76505              */
76506             'contextmenu',
76507             /**
76508              * @event dblclick
76509              * Fires when this Column is double clicked.
76510              * @param {Column} this
76511              * @param {Grid} The owning GridPanel
76512              * @param {Number} rowIndex
76513              * @param {Ext.EventObject} e
76514              */
76515             'dblclick',
76516             /**
76517              * @event mousedown
76518              * Fires when this Column receives a mousedown event.
76519              * @param {Column} this
76520              * @param {Grid} The owning GridPanel
76521              * @param {Number} rowIndex
76522              * @param {Ext.EventObject} e
76523              */
76524             'mousedown'
76525         );
76526         Ext.grid.Column.superclass.constructor.call(this);
76527     },
76528
76529     /**
76530      * @private
76531      * Process and refire events routed from the GridView's processEvent method.
76532      * Returns the event handler's status to allow cancelling of GridView's bubbling process.
76533      */
76534     processEvent : function(name, e, grid, rowIndex, colIndex){
76535         return this.fireEvent(name, this, grid, rowIndex, e);
76536     },
76537
76538     /**
76539      * @private
76540      * Clean up. Remove any Editor. Remove any listeners.
76541      */
76542     destroy: function() {
76543         if(this.setEditor){
76544             this.setEditor(null);
76545         }
76546         this.purgeListeners();
76547     },
76548
76549     /**
76550      * Optional. A function which returns displayable data when passed the following parameters:
76551      * <div class="mdetail-params"><ul>
76552      * <li><b>value</b> : Object<p class="sub-desc">The data value for the cell.</p></li>
76553      * <li><b>metadata</b> : Object<p class="sub-desc">An object in which you may set the following attributes:<ul>
76554      * <li><b>css</b> : String<p class="sub-desc">A CSS class name to add to the cell's TD element.</p></li>
76555      * <li><b>attr</b> : String<p class="sub-desc">An HTML attribute definition string to apply to the data container
76556      * element <i>within</i> the table cell (e.g. 'style="color:red;"').</p></li></ul></p></li>
76557      * <li><b>record</b> : Ext.data.record<p class="sub-desc">The {@link Ext.data.Record} from which the data was
76558      * extracted.</p></li>
76559      * <li><b>rowIndex</b> : Number<p class="sub-desc">Row index</p></li>
76560      * <li><b>colIndex</b> : Number<p class="sub-desc">Column index</p></li>
76561      * <li><b>store</b> : Ext.data.Store<p class="sub-desc">The {@link Ext.data.Store} object from which the Record
76562      * was extracted.</p></li>
76563      * </ul></div>
76564      * @property renderer
76565      * @type Function
76566      */
76567     renderer : function(value){
76568         return value;
76569     },
76570
76571     // private
76572     getEditor: function(rowIndex){
76573         return this.editable !== false ? this.editor : null;
76574     },
76575
76576     /**
76577      * Sets a new editor for this column.
76578      * @param {Ext.Editor/Ext.form.Field} editor The editor to set
76579      */
76580     setEditor : function(editor){
76581         var ed = this.editor;
76582         if(ed){
76583             if(ed.gridEditor){
76584                 ed.gridEditor.destroy();
76585                 delete ed.gridEditor;
76586             }else{
76587                 ed.destroy();
76588             }
76589         }
76590         this.editor = null;
76591         if(editor){
76592             //not an instance, create it
76593             if(!editor.isXType){
76594                 editor = Ext.create(editor, 'textfield');
76595             }
76596             this.editor = editor;
76597         }
76598     },
76599
76600     /**
76601      * Returns the {@link Ext.Editor editor} defined for this column that was created to wrap the {@link Ext.form.Field Field}
76602      * used to edit the cell.
76603      * @param {Number} rowIndex The row index
76604      * @return {Ext.Editor}
76605      */
76606     getCellEditor: function(rowIndex){
76607         var ed = this.getEditor(rowIndex);
76608         if(ed){
76609             if(!ed.startEdit){
76610                 if(!ed.gridEditor){
76611                     ed.gridEditor = new Ext.grid.GridEditor(ed);
76612                 }
76613                 ed = ed.gridEditor;
76614             }
76615         }
76616         return ed;
76617     }
76618 });
76619
76620 /**
76621  * @class Ext.grid.BooleanColumn
76622  * @extends Ext.grid.Column
76623  * <p>A Column definition class which renders boolean data fields.  See the {@link Ext.grid.Column#xtype xtype}
76624  * config option of {@link Ext.grid.Column} for more details.</p>
76625  */
76626 Ext.grid.BooleanColumn = Ext.extend(Ext.grid.Column, {
76627     /**
76628      * @cfg {String} trueText
76629      * The string returned by the renderer when the column value is not falsy (defaults to <tt>'true'</tt>).
76630      */
76631     trueText: 'true',
76632     /**
76633      * @cfg {String} falseText
76634      * The string returned by the renderer when the column value is falsy (but not undefined) (defaults to
76635      * <tt>'false'</tt>).
76636      */
76637     falseText: 'false',
76638     /**
76639      * @cfg {String} undefinedText
76640      * The string returned by the renderer when the column value is undefined (defaults to <tt>'&amp;#160;'</tt>).
76641      */
76642     undefinedText: '&#160;',
76643
76644     constructor: function(cfg){
76645         Ext.grid.BooleanColumn.superclass.constructor.call(this, cfg);
76646         var t = this.trueText, f = this.falseText, u = this.undefinedText;
76647         this.renderer = function(v){
76648             if(v === undefined){
76649                 return u;
76650             }
76651             if(!v || v === 'false'){
76652                 return f;
76653             }
76654             return t;
76655         };
76656     }
76657 });
76658
76659 /**
76660  * @class Ext.grid.NumberColumn
76661  * @extends Ext.grid.Column
76662  * <p>A Column definition class which renders a numeric data field according to a {@link #format} string.  See the
76663  * {@link Ext.grid.Column#xtype xtype} config option of {@link Ext.grid.Column} for more details.</p>
76664  */
76665 Ext.grid.NumberColumn = Ext.extend(Ext.grid.Column, {
76666     /**
76667      * @cfg {String} format
76668      * A formatting string as used by {@link Ext.util.Format#number} to format a numeric value for this Column
76669      * (defaults to <tt>'0,000.00'</tt>).
76670      */
76671     format : '0,000.00',
76672     constructor: function(cfg){
76673         Ext.grid.NumberColumn.superclass.constructor.call(this, cfg);
76674         this.renderer = Ext.util.Format.numberRenderer(this.format);
76675     }
76676 });
76677
76678 /**
76679  * @class Ext.grid.DateColumn
76680  * @extends Ext.grid.Column
76681  * <p>A Column definition class which renders a passed date according to the default locale, or a configured
76682  * {@link #format}. See the {@link Ext.grid.Column#xtype xtype} config option of {@link Ext.grid.Column}
76683  * for more details.</p>
76684  */
76685 Ext.grid.DateColumn = Ext.extend(Ext.grid.Column, {
76686     /**
76687      * @cfg {String} format
76688      * A formatting string as used by {@link Date#format} to format a Date for this Column
76689      * (defaults to <tt>'m/d/Y'</tt>).
76690      */
76691     format : 'm/d/Y',
76692     constructor: function(cfg){
76693         Ext.grid.DateColumn.superclass.constructor.call(this, cfg);
76694         this.renderer = Ext.util.Format.dateRenderer(this.format);
76695     }
76696 });
76697
76698 /**
76699  * @class Ext.grid.TemplateColumn
76700  * @extends Ext.grid.Column
76701  * <p>A Column definition class which renders a value by processing a {@link Ext.data.Record Record}'s
76702  * {@link Ext.data.Record#data data} using a {@link #tpl configured} {@link Ext.XTemplate XTemplate}.
76703  * See the {@link Ext.grid.Column#xtype xtype} config option of {@link Ext.grid.Column} for more
76704  * details.</p>
76705  */
76706 Ext.grid.TemplateColumn = Ext.extend(Ext.grid.Column, {
76707     /**
76708      * @cfg {String/XTemplate} tpl
76709      * An {@link Ext.XTemplate XTemplate}, or an XTemplate <i>definition string</i> to use to process a
76710      * {@link Ext.data.Record Record}'s {@link Ext.data.Record#data data} to produce a column's rendered value.
76711      */
76712     constructor: function(cfg){
76713         Ext.grid.TemplateColumn.superclass.constructor.call(this, cfg);
76714         var tpl = (!Ext.isPrimitive(this.tpl) && this.tpl.compile) ? this.tpl : new Ext.XTemplate(this.tpl);
76715         this.renderer = function(value, p, r){
76716             return tpl.apply(r.data);
76717         };
76718         this.tpl = tpl;
76719     }
76720 });
76721
76722 /**
76723  * @class Ext.grid.ActionColumn
76724  * @extends Ext.grid.Column
76725  * <p>A Grid column type which renders an icon, or a series of icons in a grid cell, and offers a scoped click
76726  * handler for each icon. Example usage:</p>
76727 <pre><code>
76728 new Ext.grid.GridPanel({
76729     store: myStore,
76730     columns: [
76731         {
76732             xtype: 'actioncolumn',
76733             width: 50,
76734             items: [
76735                 {
76736                     icon   : 'sell.gif',                // Use a URL in the icon config
76737                     tooltip: 'Sell stock',
76738                     handler: function(grid, rowIndex, colIndex) {
76739                         var rec = store.getAt(rowIndex);
76740                         alert("Sell " + rec.get('company'));
76741                     }
76742                 },
76743                 {
76744                     getClass: function(v, meta, rec) {  // Or return a class from a function
76745                         if (rec.get('change') < 0) {
76746                             this.items[1].tooltip = 'Do not buy!';
76747                             return 'alert-col';
76748                         } else {
76749                             this.items[1].tooltip = 'Buy stock';
76750                             return 'buy-col';
76751                         }
76752                     },
76753                     handler: function(grid, rowIndex, colIndex) {
76754                         var rec = store.getAt(rowIndex);
76755                         alert("Buy " + rec.get('company'));
76756                     }
76757                 }
76758             ]
76759         }
76760         //any other columns here
76761     ]
76762 });
76763 </pre></code>
76764  * <p>The action column can be at any index in the columns array, and a grid can have any number of
76765  * action columns. </p>
76766  */
76767 Ext.grid.ActionColumn = Ext.extend(Ext.grid.Column, {
76768     /**
76769      * @cfg {String} icon
76770      * The URL of an image to display as the clickable element in the column. 
76771      * Optional - defaults to <code>{@link Ext#BLANK_IMAGE_URL Ext.BLANK_IMAGE_URL}</code>.
76772      */
76773     /**
76774      * @cfg {String} iconCls
76775      * A CSS class to apply to the icon image. To determine the class dynamically, configure the Column with a <code>{@link #getClass}</code> function.
76776      */
76777     /**
76778      * @cfg {Function} handler A function called when the icon is clicked.
76779      * The handler is passed the following parameters:<div class="mdetail-params"><ul>
76780      * <li><code>grid</code> : GridPanel<div class="sub-desc">The owning GridPanel.</div></li>
76781      * <li><code>rowIndex</code> : Number<div class="sub-desc">The row index clicked on.</div></li>
76782      * <li><code>colIndex</code> : Number<div class="sub-desc">The column index clicked on.</div></li>
76783      * <li><code>item</code> : Object<div class="sub-desc">The clicked item (or this Column if multiple 
76784      * {@link #items} were not configured).</div></li>
76785      * <li><code>e</code> : Event<div class="sub-desc">The click event.</div></li>
76786      * </ul></div>
76787      */
76788     /**
76789      * @cfg {Object} scope The scope (<tt><b>this</b></tt> reference) in which the <code>{@link #handler}</code>
76790      * and <code>{@link #getClass}</code> fuctions are executed. Defaults to this Column.
76791      */
76792     /**
76793      * @cfg {String} tooltip A tooltip message to be displayed on hover. {@link Ext.QuickTips#init Ext.QuickTips} must have 
76794      * been initialized.
76795      */
76796     /**
76797      * @cfg {Boolean} stopSelection Defaults to <code>true</code>. Prevent grid <i>row</i> selection upon mousedown.
76798      */
76799     /**
76800      * @cfg {Function} getClass A function which returns the CSS class to apply to the icon image.
76801      * The function is passed the following parameters:<div class="mdetail-params"><ul>
76802      *     <li><b>v</b> : Object<p class="sub-desc">The value of the column's configured field (if any).</p></li>
76803      *     <li><b>metadata</b> : Object<p class="sub-desc">An object in which you may set the following attributes:<ul>
76804      *         <li><b>css</b> : String<p class="sub-desc">A CSS class name to add to the cell's TD element.</p></li>
76805      *         <li><b>attr</b> : String<p class="sub-desc">An HTML attribute definition string to apply to the data container element <i>within</i> the table cell
76806      *         (e.g. 'style="color:red;"').</p></li>
76807      *     </ul></p></li>
76808      *     <li><b>r</b> : Ext.data.Record<p class="sub-desc">The Record providing the data.</p></li>
76809      *     <li><b>rowIndex</b> : Number<p class="sub-desc">The row index..</p></li>
76810      *     <li><b>colIndex</b> : Number<p class="sub-desc">The column index.</p></li>
76811      *     <li><b>store</b> : Ext.data.Store<p class="sub-desc">The Store which is providing the data Model.</p></li>
76812      * </ul></div>
76813      */
76814     /**
76815      * @cfg {Array} items An Array which may contain multiple icon definitions, each element of which may contain:
76816      * <div class="mdetail-params"><ul>
76817      * <li><code>icon</code> : String<div class="sub-desc">The url of an image to display as the clickable element 
76818      * in the column.</div></li>
76819      * <li><code>iconCls</code> : String<div class="sub-desc">A CSS class to apply to the icon image.
76820      * To determine the class dynamically, configure the item with a <code>getClass</code> function.</div></li>
76821      * <li><code>getClass</code> : Function<div class="sub-desc">A function which returns the CSS class to apply to the icon image.
76822      * The function is passed the following parameters:<ul>
76823      *     <li><b>v</b> : Object<p class="sub-desc">The value of the column's configured field (if any).</p></li>
76824      *     <li><b>metadata</b> : Object<p class="sub-desc">An object in which you may set the following attributes:<ul>
76825      *         <li><b>css</b> : String<p class="sub-desc">A CSS class name to add to the cell's TD element.</p></li>
76826      *         <li><b>attr</b> : String<p class="sub-desc">An HTML attribute definition string to apply to the data container element <i>within</i> the table cell
76827      *         (e.g. 'style="color:red;"').</p></li>
76828      *     </ul></p></li>
76829      *     <li><b>r</b> : Ext.data.Record<p class="sub-desc">The Record providing the data.</p></li>
76830      *     <li><b>rowIndex</b> : Number<p class="sub-desc">The row index..</p></li>
76831      *     <li><b>colIndex</b> : Number<p class="sub-desc">The column index.</p></li>
76832      *     <li><b>store</b> : Ext.data.Store<p class="sub-desc">The Store which is providing the data Model.</p></li>
76833      * </ul></div></li>
76834      * <li><code>handler</code> : Function<div class="sub-desc">A function called when the icon is clicked.</div></li>
76835      * <li><code>scope</code> : Scope<div class="sub-desc">The scope (<code><b>this</b></code> reference) in which the 
76836      * <code>handler</code> and <code>getClass</code> functions are executed. Fallback defaults are this Column's
76837      * configured scope, then this Column.</div></li>
76838      * <li><code>tooltip</code> : String<div class="sub-desc">A tooltip message to be displayed on hover. 
76839      * {@link Ext.QuickTips#init Ext.QuickTips} must have been initialized.</div></li>
76840      * </ul></div>
76841      */
76842     header: '&#160;',
76843
76844     actionIdRe: /x-action-col-(\d+)/,
76845     
76846     /**
76847      * @cfg {String} altText The alt text to use for the image element. Defaults to <tt>''</tt>.
76848      */
76849     altText: '',
76850
76851     constructor: function(cfg) {
76852         var me = this,
76853             items = cfg.items || (me.items = [me]),
76854             l = items.length,
76855             i,
76856             item;
76857
76858         Ext.grid.ActionColumn.superclass.constructor.call(me, cfg);
76859
76860 //      Renderer closure iterates through items creating an <img> element for each and tagging with an identifying 
76861 //      class name x-action-col-{n}
76862         me.renderer = function(v, meta) {
76863 //          Allow a configured renderer to create initial value (And set the other values in the "metadata" argument!)
76864             v = Ext.isFunction(cfg.renderer) ? cfg.renderer.apply(this, arguments)||'' : '';
76865
76866             meta.css += ' x-action-col-cell';
76867             for (i = 0; i < l; i++) {
76868                 item = items[i];
76869                 v += '<img alt="' + me.altText + '" src="' + (item.icon || Ext.BLANK_IMAGE_URL) +
76870                     '" class="x-action-col-icon x-action-col-' + String(i) + ' ' + (item.iconCls || '') +
76871                     ' ' + (Ext.isFunction(item.getClass) ? item.getClass.apply(item.scope||this.scope||this, arguments) : '') + '"' +
76872                     ((item.tooltip) ? ' ext:qtip="' + item.tooltip + '"' : '') + ' />';
76873             }
76874             return v;
76875         };
76876     },
76877
76878     destroy: function() {
76879         delete this.items;
76880         delete this.renderer;
76881         return Ext.grid.ActionColumn.superclass.destroy.apply(this, arguments);
76882     },
76883
76884     /**
76885      * @private
76886      * Process and refire events routed from the GridView's processEvent method.
76887      * Also fires any configured click handlers. By default, cancels the mousedown event to prevent selection.
76888      * Returns the event handler's status to allow cancelling of GridView's bubbling process.
76889      */
76890     processEvent : function(name, e, grid, rowIndex, colIndex){
76891         var m = e.getTarget().className.match(this.actionIdRe),
76892             item, fn;
76893         if (m && (item = this.items[parseInt(m[1], 10)])) {
76894             if (name == 'click') {
76895                 (fn = item.handler || this.handler) && fn.call(item.scope||this.scope||this, grid, rowIndex, colIndex, item, e);
76896             } else if ((name == 'mousedown') && (item.stopSelection !== false)) {
76897                 return false;
76898             }
76899         }
76900         return Ext.grid.ActionColumn.superclass.processEvent.apply(this, arguments);
76901     }
76902 });
76903
76904 /*
76905  * @property types
76906  * @type Object
76907  * @member Ext.grid.Column
76908  * @static
76909  * <p>An object containing predefined Column classes keyed by a mnemonic code which may be referenced
76910  * by the {@link Ext.grid.ColumnModel#xtype xtype} config option of ColumnModel.</p>
76911  * <p>This contains the following properties</p><div class="mdesc-details"><ul>
76912  * <li>gridcolumn : <b>{@link Ext.grid.Column Column constructor}</b></li>
76913  * <li>booleancolumn : <b>{@link Ext.grid.BooleanColumn BooleanColumn constructor}</b></li>
76914  * <li>numbercolumn : <b>{@link Ext.grid.NumberColumn NumberColumn constructor}</b></li>
76915  * <li>datecolumn : <b>{@link Ext.grid.DateColumn DateColumn constructor}</b></li>
76916  * <li>templatecolumn : <b>{@link Ext.grid.TemplateColumn TemplateColumn constructor}</b></li>
76917  * </ul></div>
76918  */
76919 Ext.grid.Column.types = {
76920     gridcolumn : Ext.grid.Column,
76921     booleancolumn: Ext.grid.BooleanColumn,
76922     numbercolumn: Ext.grid.NumberColumn,
76923     datecolumn: Ext.grid.DateColumn,
76924     templatecolumn: Ext.grid.TemplateColumn,
76925     actioncolumn: Ext.grid.ActionColumn
76926 };/**
76927  * @class Ext.grid.RowNumberer
76928  * This is a utility class that can be passed into a {@link Ext.grid.ColumnModel} as a column config that provides
76929  * an automatic row numbering column.
76930  * <br>Usage:<br>
76931  <pre><code>
76932  // This is a typical column config with the first column providing row numbers
76933  var colModel = new Ext.grid.ColumnModel([
76934     new Ext.grid.RowNumberer(),
76935     {header: "Name", width: 80, sortable: true},
76936     {header: "Code", width: 50, sortable: true},
76937     {header: "Description", width: 200, sortable: true}
76938  ]);
76939  </code></pre>
76940  * @constructor
76941  * @param {Object} config The configuration options
76942  */
76943 Ext.grid.RowNumberer = Ext.extend(Object, {
76944     /**
76945      * @cfg {String} header Any valid text or HTML fragment to display in the header cell for the row
76946      * number column (defaults to '').
76947      */
76948     header: "",
76949     /**
76950      * @cfg {Number} width The default width in pixels of the row number column (defaults to 23).
76951      */
76952     width: 23,
76953     /**
76954      * @cfg {Boolean} sortable True if the row number column is sortable (defaults to false).
76955      * @hide
76956      */
76957     sortable: false,
76958     
76959     constructor : function(config){
76960         Ext.apply(this, config);
76961         if(this.rowspan){
76962             this.renderer = this.renderer.createDelegate(this);
76963         }
76964     },
76965
76966     // private
76967     fixed:true,
76968     hideable: false,
76969     menuDisabled:true,
76970     dataIndex: '',
76971     id: 'numberer',
76972     rowspan: undefined,
76973
76974     // private
76975     renderer : function(v, p, record, rowIndex){
76976         if(this.rowspan){
76977             p.cellAttr = 'rowspan="'+this.rowspan+'"';
76978         }
76979         return rowIndex+1;
76980     }
76981 });/**
76982  * @class Ext.grid.CheckboxSelectionModel
76983  * @extends Ext.grid.RowSelectionModel
76984  * A custom selection model that renders a column of checkboxes that can be toggled to select or deselect rows.
76985  * @constructor
76986  * @param {Object} config The configuration options
76987  */
76988 Ext.grid.CheckboxSelectionModel = Ext.extend(Ext.grid.RowSelectionModel, {
76989
76990     /**
76991      * @cfg {Boolean} checkOnly <tt>true</tt> if rows can only be selected by clicking on the
76992      * checkbox column (defaults to <tt>false</tt>).
76993      */
76994     /**
76995      * @cfg {String} header Any valid text or HTML fragment to display in the header cell for the
76996      * checkbox column.  Defaults to:<pre><code>
76997      * '&lt;div class="x-grid3-hd-checker">&#38;#160;&lt;/div>'</tt>
76998      * </code></pre>
76999      * The default CSS class of <tt>'x-grid3-hd-checker'</tt> displays a checkbox in the header
77000      * and provides support for automatic check all/none behavior on header click. This string
77001      * can be replaced by any valid HTML fragment, including a simple text string (e.g.,
77002      * <tt>'Select Rows'</tt>), but the automatic check all/none behavior will only work if the
77003      * <tt>'x-grid3-hd-checker'</tt> class is supplied.
77004      */
77005     header : '<div class="x-grid3-hd-checker">&#160;</div>',
77006     /**
77007      * @cfg {Number} width The default width in pixels of the checkbox column (defaults to <tt>20</tt>).
77008      */
77009     width : 20,
77010     /**
77011      * @cfg {Boolean} sortable <tt>true</tt> if the checkbox column is sortable (defaults to
77012      * <tt>false</tt>).
77013      */
77014     sortable : false,
77015
77016     // private
77017     menuDisabled : true,
77018     fixed : true,
77019     hideable: false,
77020     dataIndex : '',
77021     id : 'checker',
77022     isColumn: true, // So that ColumnModel doesn't feed this through the Column constructor
77023
77024     constructor : function(){
77025         Ext.grid.CheckboxSelectionModel.superclass.constructor.apply(this, arguments);
77026         if(this.checkOnly){
77027             this.handleMouseDown = Ext.emptyFn;
77028         }
77029     },
77030
77031     // private
77032     initEvents : function(){
77033         Ext.grid.CheckboxSelectionModel.superclass.initEvents.call(this);
77034         this.grid.on('render', function(){
77035             Ext.fly(this.grid.getView().innerHd).on('mousedown', this.onHdMouseDown, this);
77036         }, this);
77037     },
77038
77039     /**
77040      * @private
77041      * Process and refire events routed from the GridView's processEvent method.
77042      */
77043     processEvent : function(name, e, grid, rowIndex, colIndex){
77044         if (name == 'mousedown') {
77045             this.onMouseDown(e, e.getTarget());
77046             return false;
77047         } else {
77048             return Ext.grid.Column.prototype.processEvent.apply(this, arguments);
77049         }
77050     },
77051
77052     // private
77053     onMouseDown : function(e, t){
77054         if(e.button === 0 && t.className == 'x-grid3-row-checker'){ // Only fire if left-click
77055             e.stopEvent();
77056             var row = e.getTarget('.x-grid3-row');
77057             if(row){
77058                 var index = row.rowIndex;
77059                 if(this.isSelected(index)){
77060                     this.deselectRow(index);
77061                 }else{
77062                     this.selectRow(index, true);
77063                     this.grid.getView().focusRow(index);
77064                 }
77065             }
77066         }
77067     },
77068
77069     // private
77070     onHdMouseDown : function(e, t) {
77071         if(t.className == 'x-grid3-hd-checker'){
77072             e.stopEvent();
77073             var hd = Ext.fly(t.parentNode);
77074             var isChecked = hd.hasClass('x-grid3-hd-checker-on');
77075             if(isChecked){
77076                 hd.removeClass('x-grid3-hd-checker-on');
77077                 this.clearSelections();
77078             }else{
77079                 hd.addClass('x-grid3-hd-checker-on');
77080                 this.selectAll();
77081             }
77082         }
77083     },
77084
77085     // private
77086     renderer : function(v, p, record){
77087         return '<div class="x-grid3-row-checker">&#160;</div>';
77088     },
77089     
77090     onEditorSelect: function(row, lastRow){
77091         if(lastRow != row && !this.checkOnly){
77092             this.selectRow(row); // *** highlight newly-selected cell and update selection
77093         }
77094     }
77095 });/**
77096  * @class Ext.grid.CellSelectionModel
77097  * @extends Ext.grid.AbstractSelectionModel
77098  * This class provides the basic implementation for <i>single</i> <b>cell</b> selection in a grid.
77099  * The object stored as the selection contains the following properties:
77100  * <div class="mdetail-params"><ul>
77101  * <li><b>cell</b> : see {@link #getSelectedCell} 
77102  * <li><b>record</b> : Ext.data.record The {@link Ext.data.Record Record}
77103  * which provides the data for the row containing the selection</li>
77104  * </ul></div>
77105  * @constructor
77106  * @param {Object} config The object containing the configuration of this model.
77107  */
77108 Ext.grid.CellSelectionModel = Ext.extend(Ext.grid.AbstractSelectionModel,  {
77109     
77110     constructor : function(config){
77111         Ext.apply(this, config);
77112
77113             this.selection = null;
77114         
77115             this.addEvents(
77116                 /**
77117                  * @event beforecellselect
77118                  * Fires before a cell is selected, return false to cancel the selection.
77119                  * @param {SelectionModel} this
77120                  * @param {Number} rowIndex The selected row index
77121                  * @param {Number} colIndex The selected cell index
77122                  */
77123                 "beforecellselect",
77124                 /**
77125                  * @event cellselect
77126                  * Fires when a cell is selected.
77127                  * @param {SelectionModel} this
77128                  * @param {Number} rowIndex The selected row index
77129                  * @param {Number} colIndex The selected cell index
77130                  */
77131                 "cellselect",
77132                 /**
77133                  * @event selectionchange
77134                  * Fires when the active selection changes.
77135                  * @param {SelectionModel} this
77136                  * @param {Object} selection null for no selection or an object with two properties
77137                  * <div class="mdetail-params"><ul>
77138                  * <li><b>cell</b> : see {@link #getSelectedCell} 
77139                  * <li><b>record</b> : Ext.data.record<p class="sub-desc">The {@link Ext.data.Record Record}
77140                  * which provides the data for the row containing the selection</p></li>
77141                  * </ul></div>
77142                  */
77143                 "selectionchange"
77144             );
77145         
77146             Ext.grid.CellSelectionModel.superclass.constructor.call(this);
77147     },
77148
77149     /** @ignore */
77150     initEvents : function(){
77151         this.grid.on('cellmousedown', this.handleMouseDown, this);
77152         this.grid.on(Ext.EventManager.getKeyEvent(), this.handleKeyDown, this);
77153         this.grid.getView().on({
77154             scope: this,
77155             refresh: this.onViewChange,
77156             rowupdated: this.onRowUpdated,
77157             beforerowremoved: this.clearSelections,
77158             beforerowsinserted: this.clearSelections
77159         });
77160         if(this.grid.isEditor){
77161             this.grid.on('beforeedit', this.beforeEdit,  this);
77162         }
77163     },
77164
77165         //private
77166     beforeEdit : function(e){
77167         this.select(e.row, e.column, false, true, e.record);
77168     },
77169
77170         //private
77171     onRowUpdated : function(v, index, r){
77172         if(this.selection && this.selection.record == r){
77173             v.onCellSelect(index, this.selection.cell[1]);
77174         }
77175     },
77176
77177         //private
77178     onViewChange : function(){
77179         this.clearSelections(true);
77180     },
77181
77182         /**
77183      * Returns an array containing the row and column indexes of the currently selected cell
77184      * (e.g., [0, 0]), or null if none selected. The array has elements:
77185      * <div class="mdetail-params"><ul>
77186      * <li><b>rowIndex</b> : Number<p class="sub-desc">The index of the selected row</p></li>
77187      * <li><b>cellIndex</b> : Number<p class="sub-desc">The index of the selected cell. 
77188      * Due to possible column reordering, the cellIndex should <b>not</b> be used as an
77189      * index into the Record's data. Instead, use the cellIndex to determine the <i>name</i>
77190      * of the selected cell and use the field name to retrieve the data value from the record:<pre><code>
77191 // get name
77192 var fieldName = grid.getColumnModel().getDataIndex(cellIndex);
77193 // get data value based on name
77194 var data = record.get(fieldName);
77195      * </code></pre></p></li>
77196      * </ul></div>
77197      * @return {Array} An array containing the row and column indexes of the selected cell, or null if none selected.
77198          */
77199     getSelectedCell : function(){
77200         return this.selection ? this.selection.cell : null;
77201     },
77202
77203     /**
77204      * If anything is selected, clears all selections and fires the selectionchange event.
77205      * @param {Boolean} preventNotify <tt>true</tt> to prevent the gridview from
77206      * being notified about the change.
77207      */
77208     clearSelections : function(preventNotify){
77209         var s = this.selection;
77210         if(s){
77211             if(preventNotify !== true){
77212                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
77213             }
77214             this.selection = null;
77215             this.fireEvent("selectionchange", this, null);
77216         }
77217     },
77218
77219     /**
77220      * Returns <tt>true</tt> if there is a selection.
77221      * @return {Boolean}
77222      */
77223     hasSelection : function(){
77224         return this.selection ? true : false;
77225     },
77226
77227     /** @ignore */
77228     handleMouseDown : function(g, row, cell, e){
77229         if(e.button !== 0 || this.isLocked()){
77230             return;
77231         }
77232         this.select(row, cell);
77233     },
77234
77235     /**
77236      * Selects a cell.  Before selecting a cell, fires the
77237      * {@link #beforecellselect} event.  If this check is satisfied the cell
77238      * will be selected and followed up by  firing the {@link #cellselect} and
77239      * {@link #selectionchange} events.
77240      * @param {Number} rowIndex The index of the row to select
77241      * @param {Number} colIndex The index of the column to select
77242      * @param {Boolean} preventViewNotify (optional) Specify <tt>true</tt> to
77243      * prevent notifying the view (disables updating the selected appearance)
77244      * @param {Boolean} preventFocus (optional) Whether to prevent the cell at
77245      * the specified rowIndex / colIndex from being focused.
77246      * @param {Ext.data.Record} r (optional) The record to select
77247      */
77248     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
77249         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
77250             this.clearSelections();
77251             r = r || this.grid.store.getAt(rowIndex);
77252             this.selection = {
77253                 record : r,
77254                 cell : [rowIndex, colIndex]
77255             };
77256             if(!preventViewNotify){
77257                 var v = this.grid.getView();
77258                 v.onCellSelect(rowIndex, colIndex);
77259                 if(preventFocus !== true){
77260                     v.focusCell(rowIndex, colIndex);
77261                 }
77262             }
77263             this.fireEvent("cellselect", this, rowIndex, colIndex);
77264             this.fireEvent("selectionchange", this, this.selection);
77265         }
77266     },
77267
77268         //private
77269     isSelectable : function(rowIndex, colIndex, cm){
77270         return !cm.isHidden(colIndex);
77271     },
77272     
77273     // private
77274     onEditorKey: function(field, e){
77275         if(e.getKey() == e.TAB){
77276             this.handleKeyDown(e);
77277         }
77278     },
77279
77280     /** @ignore */
77281     handleKeyDown : function(e){
77282         if(!e.isNavKeyPress()){
77283             return;
77284         }
77285         
77286         var k = e.getKey(),
77287             g = this.grid,
77288             s = this.selection,
77289             sm = this,
77290             walk = function(row, col, step){
77291                 return g.walkCells(
77292                     row,
77293                     col,
77294                     step,
77295                     g.isEditor && g.editing ? sm.acceptsNav : sm.isSelectable, // *** handle tabbing while editorgrid is in edit mode
77296                     sm
77297                 );
77298             },
77299             cell, newCell, r, c, ae;
77300
77301         switch(k){
77302             case e.ESC:
77303             case e.PAGE_UP:
77304             case e.PAGE_DOWN:
77305                 // do nothing
77306                 break;
77307             default:
77308                 // *** call e.stopEvent() only for non ESC, PAGE UP/DOWN KEYS
77309                 e.stopEvent();
77310                 break;
77311         }
77312
77313         if(!s){
77314             cell = walk(0, 0, 1); // *** use private walk() function defined above
77315             if(cell){
77316                 this.select(cell[0], cell[1]);
77317             }
77318             return;
77319         }
77320
77321         cell = s.cell;  // currently selected cell
77322         r = cell[0];    // current row
77323         c = cell[1];    // current column
77324         
77325         switch(k){
77326             case e.TAB:
77327                 if(e.shiftKey){
77328                     newCell = walk(r, c - 1, -1);
77329                 }else{
77330                     newCell = walk(r, c + 1, 1);
77331                 }
77332                 break;
77333             case e.DOWN:
77334                 newCell = walk(r + 1, c, 1);
77335                 break;
77336             case e.UP:
77337                 newCell = walk(r - 1, c, -1);
77338                 break;
77339             case e.RIGHT:
77340                 newCell = walk(r, c + 1, 1);
77341                 break;
77342             case e.LEFT:
77343                 newCell = walk(r, c - 1, -1);
77344                 break;
77345             case e.ENTER:
77346                 if (g.isEditor && !g.editing) {
77347                     g.startEditing(r, c);
77348                     return;
77349                 }
77350                 break;
77351         }
77352
77353         if(newCell){
77354             // *** reassign r & c variables to newly-selected cell's row and column
77355             r = newCell[0];
77356             c = newCell[1];
77357
77358             this.select(r, c); // *** highlight newly-selected cell and update selection
77359
77360             if(g.isEditor && g.editing){ // *** handle tabbing while editorgrid is in edit mode
77361                 ae = g.activeEditor;
77362                 if(ae && ae.field.triggerBlur){
77363                     // *** if activeEditor is a TriggerField, explicitly call its triggerBlur() method
77364                     ae.field.triggerBlur();
77365                 }
77366                 g.startEditing(r, c);
77367             }
77368         }
77369     },
77370
77371     acceptsNav : function(row, col, cm){
77372         return !cm.isHidden(col) && cm.isCellEditable(col, row);
77373     }
77374 });/**
77375  * @class Ext.grid.EditorGridPanel
77376  * @extends Ext.grid.GridPanel
77377  * <p>This class extends the {@link Ext.grid.GridPanel GridPanel Class} to provide cell editing
77378  * on selected {@link Ext.grid.Column columns}. The editable columns are specified by providing
77379  * an {@link Ext.grid.ColumnModel#editor editor} in the {@link Ext.grid.Column column configuration}.</p>
77380  * <p>Editability of columns may be controlled programatically by inserting an implementation
77381  * of {@link Ext.grid.ColumnModel#isCellEditable isCellEditable} into the
77382  * {@link Ext.grid.ColumnModel ColumnModel}.</p>
77383  * <p>Editing is performed on the value of the <i>field</i> specified by the column's
77384  * <tt>{@link Ext.grid.ColumnModel#dataIndex dataIndex}</tt> in the backing {@link Ext.data.Store Store}
77385  * (so if you are using a {@link Ext.grid.ColumnModel#setRenderer renderer} in order to display
77386  * transformed data, this must be accounted for).</p>
77387  * <p>If a value-to-description mapping is used to render a column, then a {@link Ext.form.Field#ComboBox ComboBox}
77388  * which uses the same {@link Ext.form.Field#valueField value}-to-{@link Ext.form.Field#displayFieldField description}
77389  * mapping would be an appropriate editor.</p>
77390  * If there is a more complex mismatch between the visible data in the grid, and the editable data in
77391  * the {@link Edt.data.Store Store}, then code to transform the data both before and after editing can be
77392  * injected using the {@link #beforeedit} and {@link #afteredit} events.
77393  * @constructor
77394  * @param {Object} config The config object
77395  * @xtype editorgrid
77396  */
77397 Ext.grid.EditorGridPanel = Ext.extend(Ext.grid.GridPanel, {
77398     /**
77399      * @cfg {Number} clicksToEdit
77400      * <p>The number of clicks on a cell required to display the cell's editor (defaults to 2).</p>
77401      * <p>Setting this option to 'auto' means that mousedown <i>on the selected cell</i> starts
77402      * editing that cell.</p>
77403      */
77404     clicksToEdit: 2,
77405
77406     /**
77407     * @cfg {Boolean} forceValidation
77408     * True to force validation even if the value is unmodified (defaults to false)
77409     */
77410     forceValidation: false,
77411
77412     // private
77413     isEditor : true,
77414     // private
77415     detectEdit: false,
77416
77417     /**
77418      * @cfg {Boolean} autoEncode
77419      * True to automatically HTML encode and decode values pre and post edit (defaults to false)
77420      */
77421     autoEncode : false,
77422
77423     /**
77424      * @cfg {Boolean} trackMouseOver @hide
77425      */
77426     // private
77427     trackMouseOver: false, // causes very odd FF errors
77428
77429     // private
77430     initComponent : function(){
77431         Ext.grid.EditorGridPanel.superclass.initComponent.call(this);
77432
77433         if(!this.selModel){
77434             /**
77435              * @cfg {Object} selModel Any subclass of AbstractSelectionModel that will provide the selection model for
77436              * the grid (defaults to {@link Ext.grid.CellSelectionModel} if not specified).
77437              */
77438             this.selModel = new Ext.grid.CellSelectionModel();
77439         }
77440
77441         this.activeEditor = null;
77442
77443         this.addEvents(
77444             /**
77445              * @event beforeedit
77446              * Fires before cell editing is triggered. The edit event object has the following properties <br />
77447              * <ul style="padding:5px;padding-left:16px;">
77448              * <li>grid - This grid</li>
77449              * <li>record - The record being edited</li>
77450              * <li>field - The field name being edited</li>
77451              * <li>value - The value for the field being edited.</li>
77452              * <li>row - The grid row index</li>
77453              * <li>column - The grid column index</li>
77454              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
77455              * </ul>
77456              * @param {Object} e An edit event (see above for description)
77457              */
77458             "beforeedit",
77459             /**
77460              * @event afteredit
77461              * Fires after a cell is edited. The edit event object has the following properties <br />
77462              * <ul style="padding:5px;padding-left:16px;">
77463              * <li>grid - This grid</li>
77464              * <li>record - The record being edited</li>
77465              * <li>field - The field name being edited</li>
77466              * <li>value - The value being set</li>
77467              * <li>originalValue - The original value for the field, before the edit.</li>
77468              * <li>row - The grid row index</li>
77469              * <li>column - The grid column index</li>
77470              * </ul>
77471              *
77472              * <pre><code>
77473 grid.on('afteredit', afterEdit, this );
77474
77475 function afterEdit(e) {
77476     // execute an XHR to send/commit data to the server, in callback do (if successful):
77477     e.record.commit();
77478 };
77479              * </code></pre>
77480              * @param {Object} e An edit event (see above for description)
77481              */
77482             "afteredit",
77483             /**
77484              * @event validateedit
77485              * Fires after a cell is edited, but before the value is set in the record. Return false
77486              * to cancel the change. The edit event object has the following properties <br />
77487              * <ul style="padding:5px;padding-left:16px;">
77488              * <li>grid - This grid</li>
77489              * <li>record - The record being edited</li>
77490              * <li>field - The field name being edited</li>
77491              * <li>value - The value being set</li>
77492              * <li>originalValue - The original value for the field, before the edit.</li>
77493              * <li>row - The grid row index</li>
77494              * <li>column - The grid column index</li>
77495              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
77496              * </ul>
77497              * Usage example showing how to remove the red triangle (dirty record indicator) from some
77498              * records (not all).  By observing the grid's validateedit event, it can be cancelled if
77499              * the edit occurs on a targeted row (for example) and then setting the field's new value
77500              * in the Record directly:
77501              * <pre><code>
77502 grid.on('validateedit', function(e) {
77503   var myTargetRow = 6;
77504
77505   if (e.row == myTargetRow) {
77506     e.cancel = true;
77507     e.record.data[e.field] = e.value;
77508   }
77509 });
77510              * </code></pre>
77511              * @param {Object} e An edit event (see above for description)
77512              */
77513             "validateedit"
77514         );
77515     },
77516
77517     // private
77518     initEvents : function(){
77519         Ext.grid.EditorGridPanel.superclass.initEvents.call(this);
77520
77521         this.getGridEl().on('mousewheel', this.stopEditing.createDelegate(this, [true]), this);
77522         this.on('columnresize', this.stopEditing, this, [true]);
77523
77524         if(this.clicksToEdit == 1){
77525             this.on("cellclick", this.onCellDblClick, this);
77526         }else {
77527             var view = this.getView();
77528             if(this.clicksToEdit == 'auto' && view.mainBody){
77529                 view.mainBody.on('mousedown', this.onAutoEditClick, this);
77530             }
77531             this.on('celldblclick', this.onCellDblClick, this);
77532         }
77533     },
77534
77535     onResize : function(){
77536         Ext.grid.EditorGridPanel.superclass.onResize.apply(this, arguments);
77537         var ae = this.activeEditor;
77538         if(this.editing && ae){
77539             ae.realign(true);
77540         }
77541     },
77542
77543     // private
77544     onCellDblClick : function(g, row, col){
77545         this.startEditing(row, col);
77546     },
77547
77548     // private
77549     onAutoEditClick : function(e, t){
77550         if(e.button !== 0){
77551             return;
77552         }
77553         var row = this.view.findRowIndex(t),
77554             col = this.view.findCellIndex(t);
77555         if(row !== false && col !== false){
77556             this.stopEditing();
77557             if(this.selModel.getSelectedCell){ // cell sm
77558                 var sc = this.selModel.getSelectedCell();
77559                 if(sc && sc[0] === row && sc[1] === col){
77560                     this.startEditing(row, col);
77561                 }
77562             }else{
77563                 if(this.selModel.isSelected(row)){
77564                     this.startEditing(row, col);
77565                 }
77566             }
77567         }
77568     },
77569
77570     // private
77571     onEditComplete : function(ed, value, startValue){
77572         this.editing = false;
77573         this.lastActiveEditor = this.activeEditor;
77574         this.activeEditor = null;
77575
77576         var r = ed.record,
77577             field = this.colModel.getDataIndex(ed.col);
77578         value = this.postEditValue(value, startValue, r, field);
77579         if(this.forceValidation === true || String(value) !== String(startValue)){
77580             var e = {
77581                 grid: this,
77582                 record: r,
77583                 field: field,
77584                 originalValue: startValue,
77585                 value: value,
77586                 row: ed.row,
77587                 column: ed.col,
77588                 cancel:false
77589             };
77590             if(this.fireEvent("validateedit", e) !== false && !e.cancel && String(value) !== String(startValue)){
77591                 r.set(field, e.value);
77592                 delete e.cancel;
77593                 this.fireEvent("afteredit", e);
77594             }
77595         }
77596         this.view.focusCell(ed.row, ed.col);
77597     },
77598
77599     /**
77600      * Starts editing the specified for the specified row/column
77601      * @param {Number} rowIndex
77602      * @param {Number} colIndex
77603      */
77604     startEditing : function(row, col){
77605         this.stopEditing();
77606         if(this.colModel.isCellEditable(col, row)){
77607             this.view.ensureVisible(row, col, true);
77608             var r = this.store.getAt(row),
77609                 field = this.colModel.getDataIndex(col),
77610                 e = {
77611                     grid: this,
77612                     record: r,
77613                     field: field,
77614                     value: r.data[field],
77615                     row: row,
77616                     column: col,
77617                     cancel:false
77618                 };
77619             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
77620                 this.editing = true;
77621                 var ed = this.colModel.getCellEditor(col, row);
77622                 if(!ed){
77623                     return;
77624                 }
77625                 if(!ed.rendered){
77626                     ed.parentEl = this.view.getEditorParent(ed);
77627                     ed.on({
77628                         scope: this,
77629                         render: {
77630                             fn: function(c){
77631                                 c.field.focus(false, true);
77632                             },
77633                             single: true,
77634                             scope: this
77635                         },
77636                         specialkey: function(field, e){
77637                             this.getSelectionModel().onEditorKey(field, e);
77638                         },
77639                         complete: this.onEditComplete,
77640                         canceledit: this.stopEditing.createDelegate(this, [true])
77641                     });
77642                 }
77643                 Ext.apply(ed, {
77644                     row     : row,
77645                     col     : col,
77646                     record  : r
77647                 });
77648                 this.lastEdit = {
77649                     row: row,
77650                     col: col
77651                 };
77652                 this.activeEditor = ed;
77653                 // Set the selectSameEditor flag if we are reusing the same editor again and
77654                 // need to prevent the editor from firing onBlur on itself.
77655                 ed.selectSameEditor = (this.activeEditor == this.lastActiveEditor);
77656                 var v = this.preEditValue(r, field);
77657                 ed.startEdit(this.view.getCell(row, col).firstChild, Ext.isDefined(v) ? v : '');
77658
77659                 // Clear the selectSameEditor flag
77660                 (function(){
77661                     delete ed.selectSameEditor;
77662                 }).defer(50);
77663             }
77664         }
77665     },
77666
77667     // private
77668     preEditValue : function(r, field){
77669         var value = r.data[field];
77670         return this.autoEncode && Ext.isString(value) ? Ext.util.Format.htmlDecode(value) : value;
77671     },
77672
77673     // private
77674     postEditValue : function(value, originalValue, r, field){
77675         return this.autoEncode && Ext.isString(value) ? Ext.util.Format.htmlEncode(value) : value;
77676     },
77677
77678     /**
77679      * Stops any active editing
77680      * @param {Boolean} cancel (optional) True to cancel any changes
77681      */
77682     stopEditing : function(cancel){
77683         if(this.editing){
77684             // Store the lastActiveEditor to check if it is changing
77685             var ae = this.lastActiveEditor = this.activeEditor;
77686             if(ae){
77687                 ae[cancel === true ? 'cancelEdit' : 'completeEdit']();
77688                 this.view.focusCell(ae.row, ae.col);
77689             }
77690             this.activeEditor = null;
77691         }
77692         this.editing = false;
77693     }
77694 });
77695 Ext.reg('editorgrid', Ext.grid.EditorGridPanel);// private
77696 // This is a support class used internally by the Grid components
77697 Ext.grid.GridEditor = function(field, config){
77698     Ext.grid.GridEditor.superclass.constructor.call(this, field, config);
77699     field.monitorTab = false;
77700 };
77701
77702 Ext.extend(Ext.grid.GridEditor, Ext.Editor, {
77703     alignment: "tl-tl",
77704     autoSize: "width",
77705     hideEl : false,
77706     cls: "x-small-editor x-grid-editor",
77707     shim:false,
77708     shadow:false
77709 });/**
77710  * @class Ext.grid.PropertyRecord
77711  * A specific {@link Ext.data.Record} type that represents a name/value pair and is made to work with the
77712  * {@link Ext.grid.PropertyGrid}.  Typically, PropertyRecords do not need to be created directly as they can be
77713  * created implicitly by simply using the appropriate data configs either via the {@link Ext.grid.PropertyGrid#source}
77714  * config property or by calling {@link Ext.grid.PropertyGrid#setSource}.  However, if the need arises, these records
77715  * can also be created explicitly as shwon below.  Example usage:
77716  * <pre><code>
77717 var rec = new Ext.grid.PropertyRecord({
77718     name: 'Birthday',
77719     value: new Date(Date.parse('05/26/1972'))
77720 });
77721 // Add record to an already populated grid
77722 grid.store.addSorted(rec);
77723 </code></pre>
77724  * @constructor
77725  * @param {Object} config A data object in the format: {name: [name], value: [value]}.  The specified value's type
77726  * will be read automatically by the grid to determine the type of editor to use when displaying it.
77727  */
77728 Ext.grid.PropertyRecord = Ext.data.Record.create([
77729     {name:'name',type:'string'}, 'value'
77730 ]);
77731
77732 /**
77733  * @class Ext.grid.PropertyStore
77734  * @extends Ext.util.Observable
77735  * A custom wrapper for the {@link Ext.grid.PropertyGrid}'s {@link Ext.data.Store}. This class handles the mapping
77736  * between the custom data source objects supported by the grid and the {@link Ext.grid.PropertyRecord} format
77737  * required for compatibility with the underlying store. Generally this class should not need to be used directly --
77738  * the grid's data should be accessed from the underlying store via the {@link #store} property.
77739  * @constructor
77740  * @param {Ext.grid.Grid} grid The grid this store will be bound to
77741  * @param {Object} source The source data config object
77742  */
77743 Ext.grid.PropertyStore = Ext.extend(Ext.util.Observable, {
77744     
77745     constructor : function(grid, source){
77746         this.grid = grid;
77747         this.store = new Ext.data.Store({
77748             recordType : Ext.grid.PropertyRecord
77749         });
77750         this.store.on('update', this.onUpdate,  this);
77751         if(source){
77752             this.setSource(source);
77753         }
77754         Ext.grid.PropertyStore.superclass.constructor.call(this);    
77755     },
77756     
77757     // protected - should only be called by the grid.  Use grid.setSource instead.
77758     setSource : function(o){
77759         this.source = o;
77760         this.store.removeAll();
77761         var data = [];
77762         for(var k in o){
77763             if(this.isEditableValue(o[k])){
77764                 data.push(new Ext.grid.PropertyRecord({name: k, value: o[k]}, k));
77765             }
77766         }
77767         this.store.loadRecords({records: data}, {}, true);
77768     },
77769
77770     // private
77771     onUpdate : function(ds, record, type){
77772         if(type == Ext.data.Record.EDIT){
77773             var v = record.data.value;
77774             var oldValue = record.modified.value;
77775             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
77776                 this.source[record.id] = v;
77777                 record.commit();
77778                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
77779             }else{
77780                 record.reject();
77781             }
77782         }
77783     },
77784
77785     // private
77786     getProperty : function(row){
77787        return this.store.getAt(row);
77788     },
77789
77790     // private
77791     isEditableValue: function(val){
77792         return Ext.isPrimitive(val) || Ext.isDate(val);
77793     },
77794
77795     // private
77796     setValue : function(prop, value, create){
77797         var r = this.getRec(prop);
77798         if(r){
77799             r.set('value', value);
77800             this.source[prop] = value;
77801         }else if(create){
77802             // only create if specified.
77803             this.source[prop] = value;
77804             r = new Ext.grid.PropertyRecord({name: prop, value: value}, prop);
77805             this.store.add(r);
77806
77807         }
77808     },
77809     
77810     // private
77811     remove : function(prop){
77812         var r = this.getRec(prop);
77813         if(r){
77814             this.store.remove(r);
77815             delete this.source[prop];
77816         }
77817     },
77818     
77819     // private
77820     getRec : function(prop){
77821         return this.store.getById(prop);
77822     },
77823
77824     // protected - should only be called by the grid.  Use grid.getSource instead.
77825     getSource : function(){
77826         return this.source;
77827     }
77828 });
77829
77830 /**
77831  * @class Ext.grid.PropertyColumnModel
77832  * @extends Ext.grid.ColumnModel
77833  * A custom column model for the {@link Ext.grid.PropertyGrid}.  Generally it should not need to be used directly.
77834  * @constructor
77835  * @param {Ext.grid.Grid} grid The grid this store will be bound to
77836  * @param {Object} source The source data config object
77837  */
77838 Ext.grid.PropertyColumnModel = Ext.extend(Ext.grid.ColumnModel, {
77839     // private - strings used for locale support
77840     nameText : 'Name',
77841     valueText : 'Value',
77842     dateFormat : 'm/j/Y',
77843     trueText: 'true',
77844     falseText: 'false',
77845     
77846     constructor : function(grid, store){
77847         var g = Ext.grid,
77848                 f = Ext.form;
77849                 
77850             this.grid = grid;
77851             g.PropertyColumnModel.superclass.constructor.call(this, [
77852                 {header: this.nameText, width:50, sortable: true, dataIndex:'name', id: 'name', menuDisabled:true},
77853                 {header: this.valueText, width:50, resizable:false, dataIndex: 'value', id: 'value', menuDisabled:true}
77854             ]);
77855             this.store = store;
77856         
77857             var bfield = new f.Field({
77858                 autoCreate: {tag: 'select', children: [
77859                     {tag: 'option', value: 'true', html: this.trueText},
77860                     {tag: 'option', value: 'false', html: this.falseText}
77861                 ]},
77862                 getValue : function(){
77863                     return this.el.dom.value == 'true';
77864                 }
77865             });
77866             this.editors = {
77867                 'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
77868                 'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
77869                 'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
77870                 'boolean' : new g.GridEditor(bfield, {
77871                     autoSize: 'both'
77872                 })
77873             };
77874             this.renderCellDelegate = this.renderCell.createDelegate(this);
77875             this.renderPropDelegate = this.renderProp.createDelegate(this);
77876     },
77877
77878     // private
77879     renderDate : function(dateVal){
77880         return dateVal.dateFormat(this.dateFormat);
77881     },
77882
77883     // private
77884     renderBool : function(bVal){
77885         return this[bVal ? 'trueText' : 'falseText'];
77886     },
77887
77888     // private
77889     isCellEditable : function(colIndex, rowIndex){
77890         return colIndex == 1;
77891     },
77892
77893     // private
77894     getRenderer : function(col){
77895         return col == 1 ?
77896             this.renderCellDelegate : this.renderPropDelegate;
77897     },
77898
77899     // private
77900     renderProp : function(v){
77901         return this.getPropertyName(v);
77902     },
77903
77904     // private
77905     renderCell : function(val, meta, rec){
77906         var renderer = this.grid.customRenderers[rec.get('name')];
77907         if(renderer){
77908             return renderer.apply(this, arguments);
77909         }
77910         var rv = val;
77911         if(Ext.isDate(val)){
77912             rv = this.renderDate(val);
77913         }else if(typeof val == 'boolean'){
77914             rv = this.renderBool(val);
77915         }
77916         return Ext.util.Format.htmlEncode(rv);
77917     },
77918
77919     // private
77920     getPropertyName : function(name){
77921         var pn = this.grid.propertyNames;
77922         return pn && pn[name] ? pn[name] : name;
77923     },
77924
77925     // private
77926     getCellEditor : function(colIndex, rowIndex){
77927         var p = this.store.getProperty(rowIndex),
77928             n = p.data.name, 
77929             val = p.data.value;
77930         if(this.grid.customEditors[n]){
77931             return this.grid.customEditors[n];
77932         }
77933         if(Ext.isDate(val)){
77934             return this.editors.date;
77935         }else if(typeof val == 'number'){
77936             return this.editors.number;
77937         }else if(typeof val == 'boolean'){
77938             return this.editors['boolean'];
77939         }else{
77940             return this.editors.string;
77941         }
77942     },
77943
77944     // inherit docs
77945     destroy : function(){
77946         Ext.grid.PropertyColumnModel.superclass.destroy.call(this);
77947         this.destroyEditors(this.editors);
77948         this.destroyEditors(this.grid.customEditors);
77949     },
77950     
77951     destroyEditors: function(editors){
77952         for(var ed in editors){
77953             Ext.destroy(editors[ed]);
77954         }
77955     }
77956 });
77957
77958 /**
77959  * @class Ext.grid.PropertyGrid
77960  * @extends Ext.grid.EditorGridPanel
77961  * A specialized grid implementation intended to mimic the traditional property grid as typically seen in
77962  * development IDEs.  Each row in the grid represents a property of some object, and the data is stored
77963  * as a set of name/value pairs in {@link Ext.grid.PropertyRecord}s.  Example usage:
77964  * <pre><code>
77965 var grid = new Ext.grid.PropertyGrid({
77966     title: 'Properties Grid',
77967     autoHeight: true,
77968     width: 300,
77969     renderTo: 'grid-ct',
77970     source: {
77971         "(name)": "My Object",
77972         "Created": new Date(Date.parse('10/15/2006')),
77973         "Available": false,
77974         "Version": .01,
77975         "Description": "A test object"
77976     }
77977 });
77978 </code></pre>
77979  * @constructor
77980  * @param {Object} config The grid config object
77981  */
77982 Ext.grid.PropertyGrid = Ext.extend(Ext.grid.EditorGridPanel, {
77983     /**
77984     * @cfg {Object} propertyNames An object containing property name/display name pairs.
77985     * If specified, the display name will be shown in the name column instead of the property name.
77986     */
77987     /**
77988     * @cfg {Object} source A data object to use as the data source of the grid (see {@link #setSource} for details).
77989     */
77990     /**
77991     * @cfg {Object} customEditors An object containing name/value pairs of custom editor type definitions that allow
77992     * the grid to support additional types of editable fields.  By default, the grid supports strongly-typed editing
77993     * of strings, dates, numbers and booleans using built-in form editors, but any custom type can be supported and
77994     * associated with a custom input control by specifying a custom editor.  The name of the editor
77995     * type should correspond with the name of the property that will use the editor.  Example usage:
77996     * <pre><code>
77997 var grid = new Ext.grid.PropertyGrid({
77998     ...
77999     customEditors: {
78000         'Start Time': new Ext.grid.GridEditor(new Ext.form.TimeField({selectOnFocus:true}))
78001     },
78002     source: {
78003         'Start Time': '10:00 AM'
78004     }
78005 });
78006 </code></pre>
78007     */
78008     /**
78009     * @cfg {Object} source A data object to use as the data source of the grid (see {@link #setSource} for details).
78010     */
78011     /**
78012     * @cfg {Object} customRenderers An object containing name/value pairs of custom renderer type definitions that allow
78013     * the grid to support custom rendering of fields.  By default, the grid supports strongly-typed rendering
78014     * of strings, dates, numbers and booleans using built-in form editors, but any custom type can be supported and
78015     * associated with the type of the value.  The name of the renderer type should correspond with the name of the property
78016     * that it will render.  Example usage:
78017     * <pre><code>
78018 var grid = new Ext.grid.PropertyGrid({
78019     ...
78020     customRenderers: {
78021         Available: function(v){
78022             if(v){
78023                 return '<span style="color: green;">Yes</span>';
78024             }else{
78025                 return '<span style="color: red;">No</span>';
78026             }
78027         }
78028     },
78029     source: {
78030         Available: true
78031     }
78032 });
78033 </code></pre>
78034     */
78035
78036     // private config overrides
78037     enableColumnMove:false,
78038     stripeRows:false,
78039     trackMouseOver: false,
78040     clicksToEdit:1,
78041     enableHdMenu : false,
78042     viewConfig : {
78043         forceFit:true
78044     },
78045
78046     // private
78047     initComponent : function(){
78048         this.customRenderers = this.customRenderers || {};
78049         this.customEditors = this.customEditors || {};
78050         this.lastEditRow = null;
78051         var store = new Ext.grid.PropertyStore(this);
78052         this.propStore = store;
78053         var cm = new Ext.grid.PropertyColumnModel(this, store);
78054         store.store.sort('name', 'ASC');
78055         this.addEvents(
78056             /**
78057              * @event beforepropertychange
78058              * Fires before a property value changes.  Handlers can return false to cancel the property change
78059              * (this will internally call {@link Ext.data.Record#reject} on the property's record).
78060              * @param {Object} source The source data object for the grid (corresponds to the same object passed in
78061              * as the {@link #source} config property).
78062              * @param {String} recordId The record's id in the data store
78063              * @param {Mixed} value The current edited property value
78064              * @param {Mixed} oldValue The original property value prior to editing
78065              */
78066             'beforepropertychange',
78067             /**
78068              * @event propertychange
78069              * Fires after a property value has changed.
78070              * @param {Object} source The source data object for the grid (corresponds to the same object passed in
78071              * as the {@link #source} config property).
78072              * @param {String} recordId The record's id in the data store
78073              * @param {Mixed} value The current edited property value
78074              * @param {Mixed} oldValue The original property value prior to editing
78075              */
78076             'propertychange'
78077         );
78078         this.cm = cm;
78079         this.ds = store.store;
78080         Ext.grid.PropertyGrid.superclass.initComponent.call(this);
78081
78082                 this.mon(this.selModel, 'beforecellselect', function(sm, rowIndex, colIndex){
78083             if(colIndex === 0){
78084                 this.startEditing.defer(200, this, [rowIndex, 1]);
78085                 return false;
78086             }
78087         }, this);
78088     },
78089
78090     // private
78091     onRender : function(){
78092         Ext.grid.PropertyGrid.superclass.onRender.apply(this, arguments);
78093
78094         this.getGridEl().addClass('x-props-grid');
78095     },
78096
78097     // private
78098     afterRender: function(){
78099         Ext.grid.PropertyGrid.superclass.afterRender.apply(this, arguments);
78100         if(this.source){
78101             this.setSource(this.source);
78102         }
78103     },
78104
78105     /**
78106      * Sets the source data object containing the property data.  The data object can contain one or more name/value
78107      * pairs representing all of the properties of an object to display in the grid, and this data will automatically
78108      * be loaded into the grid's {@link #store}.  The values should be supplied in the proper data type if needed,
78109      * otherwise string type will be assumed.  If the grid already contains data, this method will replace any
78110      * existing data.  See also the {@link #source} config value.  Example usage:
78111      * <pre><code>
78112 grid.setSource({
78113     "(name)": "My Object",
78114     "Created": new Date(Date.parse('10/15/2006')),  // date type
78115     "Available": false,  // boolean type
78116     "Version": .01,      // decimal type
78117     "Description": "A test object"
78118 });
78119 </code></pre>
78120      * @param {Object} source The data object
78121      */
78122     setSource : function(source){
78123         this.propStore.setSource(source);
78124     },
78125
78126     /**
78127      * Gets the source data object containing the property data.  See {@link #setSource} for details regarding the
78128      * format of the data object.
78129      * @return {Object} The data object
78130      */
78131     getSource : function(){
78132         return this.propStore.getSource();
78133     },
78134     
78135     /**
78136      * Sets the value of a property.
78137      * @param {String} prop The name of the property to set
78138      * @param {Mixed} value The value to test
78139      * @param {Boolean} create (Optional) True to create the property if it doesn't already exist. Defaults to <tt>false</tt>.
78140      */
78141     setProperty : function(prop, value, create){
78142         this.propStore.setValue(prop, value, create);    
78143     },
78144     
78145     /**
78146      * Removes a property from the grid.
78147      * @param {String} prop The name of the property to remove
78148      */
78149     removeProperty : function(prop){
78150         this.propStore.remove(prop);
78151     }
78152
78153     /**
78154      * @cfg store
78155      * @hide
78156      */
78157     /**
78158      * @cfg colModel
78159      * @hide
78160      */
78161     /**
78162      * @cfg cm
78163      * @hide
78164      */
78165     /**
78166      * @cfg columns
78167      * @hide
78168      */
78169 });
78170 Ext.reg("propertygrid", Ext.grid.PropertyGrid);
78171 /**
78172  * @class Ext.grid.GroupingView
78173  * @extends Ext.grid.GridView
78174  * Adds the ability for single level grouping to the grid. A {@link Ext.data.GroupingStore GroupingStore}
78175  * must be used to enable grouping.  Some grouping characteristics may also be configured at the
78176  * {@link Ext.grid.Column Column level}<div class="mdetail-params"><ul>
78177  * <li><code>{@link Ext.grid.Column#emptyGroupText emptyGroupText}</code></li>
78178  * <li><code>{@link Ext.grid.Column#groupable groupable}</code></li>
78179  * <li><code>{@link Ext.grid.Column#groupName groupName}</code></li>
78180  * <li><code>{@link Ext.grid.Column#groupRender groupRender}</code></li>
78181  * </ul></div>
78182  * <p>Sample usage:</p>
78183  * <pre><code>
78184 var grid = new Ext.grid.GridPanel({
78185     // A groupingStore is required for a GroupingView
78186     store: new {@link Ext.data.GroupingStore}({
78187         autoDestroy: true,
78188         reader: reader,
78189         data: xg.dummyData,
78190         sortInfo: {field: 'company', direction: 'ASC'},
78191         {@link Ext.data.GroupingStore#groupOnSort groupOnSort}: true,
78192         {@link Ext.data.GroupingStore#remoteGroup remoteGroup}: true,
78193         {@link Ext.data.GroupingStore#groupField groupField}: 'industry'
78194     }),
78195     colModel: new {@link Ext.grid.ColumnModel}({
78196         columns:[
78197             {id:'company',header: 'Company', width: 60, dataIndex: 'company'},
78198             // {@link Ext.grid.Column#groupable groupable}, {@link Ext.grid.Column#groupName groupName}, {@link Ext.grid.Column#groupRender groupRender} are also configurable at column level
78199             {header: 'Price', renderer: Ext.util.Format.usMoney, dataIndex: 'price', {@link Ext.grid.Column#groupable groupable}: false},
78200             {header: 'Change', dataIndex: 'change', renderer: Ext.util.Format.usMoney},
78201             {header: 'Industry', dataIndex: 'industry'},
78202             {header: 'Last Updated', renderer: Ext.util.Format.dateRenderer('m/d/Y'), dataIndex: 'lastChange'}
78203         ],
78204         defaults: {
78205             sortable: true,
78206             menuDisabled: false,
78207             width: 20
78208         }
78209     }),
78210
78211     view: new Ext.grid.GroupingView({
78212         {@link Ext.grid.GridView#forceFit forceFit}: true,
78213         // custom grouping text template to display the number of items per group
78214         {@link #groupTextTpl}: '{text} ({[values.rs.length]} {[values.rs.length > 1 ? "Items" : "Item"]})'
78215     }),
78216
78217     frame:true,
78218     width: 700,
78219     height: 450,
78220     collapsible: true,
78221     animCollapse: false,
78222     title: 'Grouping Example',
78223     iconCls: 'icon-grid',
78224     renderTo: document.body
78225 });
78226  * </code></pre>
78227  * @constructor
78228  * @param {Object} config
78229  */
78230 Ext.grid.GroupingView = Ext.extend(Ext.grid.GridView, {
78231
78232     /**
78233      * @cfg {String} groupByText Text displayed in the grid header menu for grouping by a column
78234      * (defaults to 'Group By This Field').
78235      */
78236     groupByText : 'Group By This Field',
78237     /**
78238      * @cfg {String} showGroupsText Text displayed in the grid header for enabling/disabling grouping
78239      * (defaults to 'Show in Groups').
78240      */
78241     showGroupsText : 'Show in Groups',
78242     /**
78243      * @cfg {Boolean} hideGroupedColumn <tt>true</tt> to hide the column that is currently grouped (defaults to <tt>false</tt>)
78244      */
78245     hideGroupedColumn : false,
78246     /**
78247      * @cfg {Boolean} showGroupName If <tt>true</tt> will display a prefix plus a ': ' before the group field value
78248      * in the group header line.  The prefix will consist of the <tt><b>{@link Ext.grid.Column#groupName groupName}</b></tt>
78249      * (or the configured <tt><b>{@link Ext.grid.Column#header header}</b></tt> if not provided) configured in the
78250      * {@link Ext.grid.Column} for each set of grouped rows (defaults to <tt>true</tt>).
78251      */
78252     showGroupName : true,
78253     /**
78254      * @cfg {Boolean} startCollapsed <tt>true</tt> to start all groups collapsed (defaults to <tt>false</tt>)
78255      */
78256     startCollapsed : false,
78257     /**
78258      * @cfg {Boolean} enableGrouping <tt>false</tt> to disable grouping functionality (defaults to <tt>true</tt>)
78259      */
78260     enableGrouping : true,
78261     /**
78262      * @cfg {Boolean} enableGroupingMenu <tt>true</tt> to enable the grouping control in the column menu (defaults to <tt>true</tt>)
78263      */
78264     enableGroupingMenu : true,
78265     /**
78266      * @cfg {Boolean} enableNoGroups <tt>true</tt> to allow the user to turn off grouping (defaults to <tt>true</tt>)
78267      */
78268     enableNoGroups : true,
78269     /**
78270      * @cfg {String} emptyGroupText The text to display when there is an empty group value (defaults to <tt>'(None)'</tt>).
78271      * May also be specified per column, see {@link Ext.grid.Column}.{@link Ext.grid.Column#emptyGroupText emptyGroupText}.
78272      */
78273     emptyGroupText : '(None)',
78274     /**
78275      * @cfg {Boolean} ignoreAdd <tt>true</tt> to skip refreshing the view when new rows are added (defaults to <tt>false</tt>)
78276      */
78277     ignoreAdd : false,
78278     /**
78279      * @cfg {String} groupTextTpl The template used to render the group header (defaults to <tt>'{text}'</tt>).
78280      * This is used to format an object which contains the following properties:
78281      * <div class="mdetail-params"><ul>
78282      * <li><b>group</b> : String<p class="sub-desc">The <i>rendered</i> value of the group field.
78283      * By default this is the unchanged value of the group field. If a <tt><b>{@link Ext.grid.Column#groupRenderer groupRenderer}</b></tt>
78284      * is specified, it is the result of a call to that function.</p></li>
78285      * <li><b>gvalue</b> : Object<p class="sub-desc">The <i>raw</i> value of the group field.</p></li>
78286      * <li><b>text</b> : String<p class="sub-desc">The configured header (as described in <tt>{@link #showGroupName})</tt>
78287      * if <tt>{@link #showGroupName}</tt> is <tt>true</tt>) plus the <i>rendered</i> group field value.</p></li>
78288      * <li><b>groupId</b> : String<p class="sub-desc">A unique, generated ID which is applied to the
78289      * View Element which contains the group.</p></li>
78290      * <li><b>startRow</b> : Number<p class="sub-desc">The row index of the Record which caused group change.</p></li>
78291      * <li><b>rs</b> : Array<p class="sub-desc">Contains a single element: The Record providing the data
78292      * for the row which caused group change.</p></li>
78293      * <li><b>cls</b> : String<p class="sub-desc">The generated class name string to apply to the group header Element.</p></li>
78294      * <li><b>style</b> : String<p class="sub-desc">The inline style rules to apply to the group header Element.</p></li>
78295      * </ul></div></p>
78296      * See {@link Ext.XTemplate} for information on how to format data using a template. Possible usage:<pre><code>
78297 var grid = new Ext.grid.GridPanel({
78298     ...
78299     view: new Ext.grid.GroupingView({
78300         groupTextTpl: '{text} ({[values.rs.length]} {[values.rs.length > 1 ? "Items" : "Item"]})'
78301     }),
78302 });
78303      * </code></pre>
78304      */
78305     groupTextTpl : '{text}',
78306
78307     /**
78308      * @cfg {String} groupMode Indicates how to construct the group identifier. <tt>'value'</tt> constructs the id using
78309      * raw value, <tt>'display'</tt> constructs the id using the rendered value. Defaults to <tt>'value'</tt>.
78310      */
78311     groupMode: 'value',
78312
78313     /**
78314      * @cfg {Function} groupRenderer This property must be configured in the {@link Ext.grid.Column} for
78315      * each column.
78316      */
78317     
78318     /**
78319      * @cfg {Boolean} cancelEditOnToggle True to cancel any editing when the group header is toggled. Defaults to <tt>true</tt>.
78320      */
78321     cancelEditOnToggle: true,
78322
78323     // private
78324     initTemplates : function(){
78325         Ext.grid.GroupingView.superclass.initTemplates.call(this);
78326         this.state = {};
78327
78328         var sm = this.grid.getSelectionModel();
78329         sm.on(sm.selectRow ? 'beforerowselect' : 'beforecellselect',
78330                 this.onBeforeRowSelect, this);
78331
78332         if(!this.startGroup){
78333             this.startGroup = new Ext.XTemplate(
78334                 '<div id="{groupId}" class="x-grid-group {cls}">',
78335                     '<div id="{groupId}-hd" class="x-grid-group-hd" style="{style}"><div class="x-grid-group-title">', this.groupTextTpl ,'</div></div>',
78336                     '<div id="{groupId}-bd" class="x-grid-group-body">'
78337             );
78338         }
78339         this.startGroup.compile();
78340
78341         if (!this.endGroup) {
78342             this.endGroup = '</div></div>';
78343         }
78344     },
78345
78346     // private
78347     findGroup : function(el){
78348         return Ext.fly(el).up('.x-grid-group', this.mainBody.dom);
78349     },
78350
78351     // private
78352     getGroups : function(){
78353         return this.hasRows() ? this.mainBody.dom.childNodes : [];
78354     },
78355
78356     // private
78357     onAdd : function(ds, records, index) {
78358         if (this.canGroup() && !this.ignoreAdd) {
78359             var ss = this.getScrollState();
78360             this.fireEvent('beforerowsinserted', ds, index, index + (records.length-1));
78361             this.refresh();
78362             this.restoreScroll(ss);
78363             this.fireEvent('rowsinserted', ds, index, index + (records.length-1));
78364         } else if (!this.canGroup()) {
78365             Ext.grid.GroupingView.superclass.onAdd.apply(this, arguments);
78366         }
78367     },
78368
78369     // private
78370     onRemove : function(ds, record, index, isUpdate){
78371         Ext.grid.GroupingView.superclass.onRemove.apply(this, arguments);
78372         var g = document.getElementById(record._groupId);
78373         if(g && g.childNodes[1].childNodes.length < 1){
78374             Ext.removeNode(g);
78375         }
78376         this.applyEmptyText();
78377     },
78378
78379     // private
78380     refreshRow : function(record){
78381         if(this.ds.getCount()==1){
78382             this.refresh();
78383         }else{
78384             this.isUpdating = true;
78385             Ext.grid.GroupingView.superclass.refreshRow.apply(this, arguments);
78386             this.isUpdating = false;
78387         }
78388     },
78389
78390     // private
78391     beforeMenuShow : function(){
78392         var item, items = this.hmenu.items, disabled = this.cm.config[this.hdCtxIndex].groupable === false;
78393         if((item = items.get('groupBy'))){
78394             item.setDisabled(disabled);
78395         }
78396         if((item = items.get('showGroups'))){
78397             item.setDisabled(disabled);
78398             item.setChecked(this.canGroup(), true);
78399         }
78400     },
78401
78402     // private
78403     renderUI : function(){
78404         var markup = Ext.grid.GroupingView.superclass.renderUI.call(this);
78405
78406         if(this.enableGroupingMenu && this.hmenu){
78407             this.hmenu.add('-',{
78408                 itemId:'groupBy',
78409                 text: this.groupByText,
78410                 handler: this.onGroupByClick,
78411                 scope: this,
78412                 iconCls:'x-group-by-icon'
78413             });
78414             if(this.enableNoGroups){
78415                 this.hmenu.add({
78416                     itemId:'showGroups',
78417                     text: this.showGroupsText,
78418                     checked: true,
78419                     checkHandler: this.onShowGroupsClick,
78420                     scope: this
78421                 });
78422             }
78423             this.hmenu.on('beforeshow', this.beforeMenuShow, this);
78424         }
78425         return markup;
78426     },
78427
78428     processEvent: function(name, e){
78429         Ext.grid.GroupingView.superclass.processEvent.call(this, name, e);
78430         var hd = e.getTarget('.x-grid-group-hd', this.mainBody);
78431         if(hd){
78432             // group value is at the end of the string
78433             var field = this.getGroupField(),
78434                 prefix = this.getPrefix(field),
78435                 groupValue = hd.id.substring(prefix.length),
78436                 emptyRe = new RegExp('gp-' + Ext.escapeRe(field) + '--hd');
78437
78438             // remove trailing '-hd'
78439             groupValue = groupValue.substr(0, groupValue.length - 3);
78440             
78441             // also need to check for empty groups
78442             if(groupValue || emptyRe.test(hd.id)){
78443                 this.grid.fireEvent('group' + name, this.grid, field, groupValue, e);
78444             }
78445             if(name == 'mousedown' && e.button == 0){
78446                 this.toggleGroup(hd.parentNode);
78447             }
78448         }
78449
78450     },
78451
78452     // private
78453     onGroupByClick : function(){
78454         var grid = this.grid;
78455         this.enableGrouping = true;
78456         grid.store.groupBy(this.cm.getDataIndex(this.hdCtxIndex));
78457         grid.fireEvent('groupchange', grid, grid.store.getGroupState());
78458         this.beforeMenuShow(); // Make sure the checkboxes get properly set when changing groups
78459         this.refresh();
78460     },
78461
78462     // private
78463     onShowGroupsClick : function(mi, checked){
78464         this.enableGrouping = checked;
78465         if(checked){
78466             this.onGroupByClick();
78467         }else{
78468             this.grid.store.clearGrouping();
78469             this.grid.fireEvent('groupchange', this, null);
78470         }
78471     },
78472
78473     /**
78474      * Toggle the group that contains the specific row.
78475      * @param {Number} rowIndex The row inside the group
78476      * @param {Boolean} expanded (optional)
78477      */
78478     toggleRowIndex : function(rowIndex, expanded){
78479         if(!this.canGroup()){
78480             return;
78481         }
78482         var row = this.getRow(rowIndex);
78483         if(row){
78484             this.toggleGroup(this.findGroup(row), expanded);
78485         }
78486     },
78487
78488     /**
78489      * Toggles the specified group if no value is passed, otherwise sets the expanded state of the group to the value passed.
78490      * @param {String} groupId The groupId assigned to the group (see getGroupId)
78491      * @param {Boolean} expanded (optional)
78492      */
78493     toggleGroup : function(group, expanded){
78494         var gel = Ext.get(group);
78495         expanded = Ext.isDefined(expanded) ? expanded : gel.hasClass('x-grid-group-collapsed');
78496         if(this.state[gel.id] !== expanded){
78497             if (this.cancelEditOnToggle !== false) {
78498                 this.grid.stopEditing(true);
78499             }
78500             this.state[gel.id] = expanded;
78501             gel[expanded ? 'removeClass' : 'addClass']('x-grid-group-collapsed');
78502         }
78503     },
78504
78505     /**
78506      * Toggles all groups if no value is passed, otherwise sets the expanded state of all groups to the value passed.
78507      * @param {Boolean} expanded (optional)
78508      */
78509     toggleAllGroups : function(expanded){
78510         var groups = this.getGroups();
78511         for(var i = 0, len = groups.length; i < len; i++){
78512             this.toggleGroup(groups[i], expanded);
78513         }
78514     },
78515
78516     /**
78517      * Expands all grouped rows.
78518      */
78519     expandAllGroups : function(){
78520         this.toggleAllGroups(true);
78521     },
78522
78523     /**
78524      * Collapses all grouped rows.
78525      */
78526     collapseAllGroups : function(){
78527         this.toggleAllGroups(false);
78528     },
78529
78530     // private
78531     getGroup : function(v, r, groupRenderer, rowIndex, colIndex, ds){
78532         var column = this.cm.config[colIndex],
78533             g = groupRenderer ? groupRenderer.call(column.scope, v, {}, r, rowIndex, colIndex, ds) : String(v);
78534         if(g === '' || g === '&#160;'){
78535             g = column.emptyGroupText || this.emptyGroupText;
78536         }
78537         return g;
78538     },
78539
78540     // private
78541     getGroupField : function(){
78542         return this.grid.store.getGroupState();
78543     },
78544
78545     // private
78546     afterRender : function(){
78547         if(!this.ds || !this.cm){
78548             return;
78549         }
78550         Ext.grid.GroupingView.superclass.afterRender.call(this);
78551         if(this.grid.deferRowRender){
78552             this.updateGroupWidths();
78553         }
78554     },
78555     
78556     afterRenderUI: function () {
78557         Ext.grid.GroupingView.superclass.afterRenderUI.call(this);
78558
78559         if (this.enableGroupingMenu && this.hmenu) {
78560             this.hmenu.add('-',{
78561                 itemId:'groupBy',
78562                 text: this.groupByText,
78563                 handler: this.onGroupByClick,
78564                 scope: this,
78565                 iconCls:'x-group-by-icon'
78566             });
78567             
78568             if (this.enableNoGroups) {
78569                 this.hmenu.add({
78570                     itemId:'showGroups',
78571                     text: this.showGroupsText,
78572                     checked: true,
78573                     checkHandler: this.onShowGroupsClick,
78574                     scope: this
78575                 });
78576             }
78577             
78578             this.hmenu.on('beforeshow', this.beforeMenuShow, this);
78579         }
78580     },
78581
78582     // private
78583     renderRows : function(){
78584         var groupField = this.getGroupField();
78585         var eg = !!groupField;
78586         // if they turned off grouping and the last grouped field is hidden
78587         if(this.hideGroupedColumn) {
78588             var colIndex = this.cm.findColumnIndex(groupField),
78589                 hasLastGroupField = Ext.isDefined(this.lastGroupField);
78590             if(!eg && hasLastGroupField){
78591                 this.mainBody.update('');
78592                 this.cm.setHidden(this.cm.findColumnIndex(this.lastGroupField), false);
78593                 delete this.lastGroupField;
78594             }else if (eg && !hasLastGroupField){
78595                 this.lastGroupField = groupField;
78596                 this.cm.setHidden(colIndex, true);
78597             }else if (eg && hasLastGroupField && groupField !== this.lastGroupField) {
78598                 this.mainBody.update('');
78599                 var oldIndex = this.cm.findColumnIndex(this.lastGroupField);
78600                 this.cm.setHidden(oldIndex, false);
78601                 this.lastGroupField = groupField;
78602                 this.cm.setHidden(colIndex, true);
78603             }
78604         }
78605         return Ext.grid.GroupingView.superclass.renderRows.apply(
78606                     this, arguments);
78607     },
78608
78609     // private
78610     doRender : function(cs, rs, ds, startRow, colCount, stripe){
78611         if(rs.length < 1){
78612             return '';
78613         }
78614
78615         if(!this.canGroup() || this.isUpdating){
78616             return Ext.grid.GroupingView.superclass.doRender.apply(this, arguments);
78617         }
78618
78619         var groupField = this.getGroupField(),
78620             colIndex = this.cm.findColumnIndex(groupField),
78621             g,
78622             gstyle = 'width:' + this.getTotalWidth() + ';',
78623             cfg = this.cm.config[colIndex],
78624             groupRenderer = cfg.groupRenderer || cfg.renderer,
78625             prefix = this.showGroupName ? (cfg.groupName || cfg.header)+': ' : '',
78626             groups = [],
78627             curGroup, i, len, gid;
78628
78629         for(i = 0, len = rs.length; i < len; i++){
78630             var rowIndex = startRow + i,
78631                 r = rs[i],
78632                 gvalue = r.data[groupField];
78633
78634                 g = this.getGroup(gvalue, r, groupRenderer, rowIndex, colIndex, ds);
78635             if(!curGroup || curGroup.group != g){
78636                 gid = this.constructId(gvalue, groupField, colIndex);
78637                 // if state is defined use it, however state is in terms of expanded
78638                 // so negate it, otherwise use the default.
78639                 this.state[gid] = !(Ext.isDefined(this.state[gid]) ? !this.state[gid] : this.startCollapsed);
78640                 curGroup = {
78641                     group: g,
78642                     gvalue: gvalue,
78643                     text: prefix + g,
78644                     groupId: gid,
78645                     startRow: rowIndex,
78646                     rs: [r],
78647                     cls: this.state[gid] ? '' : 'x-grid-group-collapsed',
78648                     style: gstyle
78649                 };
78650                 groups.push(curGroup);
78651             }else{
78652                 curGroup.rs.push(r);
78653             }
78654             r._groupId = gid;
78655         }
78656
78657         var buf = [];
78658         for(i = 0, len = groups.length; i < len; i++){
78659             g = groups[i];
78660             this.doGroupStart(buf, g, cs, ds, colCount);
78661             buf[buf.length] = Ext.grid.GroupingView.superclass.doRender.call(
78662                     this, cs, g.rs, ds, g.startRow, colCount, stripe);
78663
78664             this.doGroupEnd(buf, g, cs, ds, colCount);
78665         }
78666         return buf.join('');
78667     },
78668
78669     /**
78670      * Dynamically tries to determine the groupId of a specific value
78671      * @param {String} value
78672      * @return {String} The group id
78673      */
78674     getGroupId : function(value){
78675         var field = this.getGroupField();
78676         return this.constructId(value, field, this.cm.findColumnIndex(field));
78677     },
78678
78679     // private
78680     constructId : function(value, field, idx){
78681         var cfg = this.cm.config[idx],
78682             groupRenderer = cfg.groupRenderer || cfg.renderer,
78683             val = (this.groupMode == 'value') ? value : this.getGroup(value, {data:{}}, groupRenderer, 0, idx, this.ds);
78684
78685         return this.getPrefix(field) + Ext.util.Format.htmlEncode(val);
78686     },
78687
78688     // private
78689     canGroup  : function(){
78690         return this.enableGrouping && !!this.getGroupField();
78691     },
78692
78693     // private
78694     getPrefix: function(field){
78695         return this.grid.getGridEl().id + '-gp-' + field + '-';
78696     },
78697
78698     // private
78699     doGroupStart : function(buf, g, cs, ds, colCount){
78700         buf[buf.length] = this.startGroup.apply(g);
78701     },
78702
78703     // private
78704     doGroupEnd : function(buf, g, cs, ds, colCount){
78705         buf[buf.length] = this.endGroup;
78706     },
78707
78708     // private
78709     getRows : function(){
78710         if(!this.canGroup()){
78711             return Ext.grid.GroupingView.superclass.getRows.call(this);
78712         }
78713         var r = [],
78714             gs = this.getGroups(),
78715             g,
78716             i = 0,
78717             len = gs.length,
78718             j,
78719             jlen;
78720         for(; i < len; ++i){
78721             g = gs[i].childNodes[1];
78722             if(g){
78723                 g = g.childNodes;
78724                 for(j = 0, jlen = g.length; j < jlen; ++j){
78725                     r[r.length] = g[j];
78726                 }
78727             }
78728         }
78729         return r;
78730     },
78731
78732     // private
78733     updateGroupWidths : function(){
78734         if(!this.canGroup() || !this.hasRows()){
78735             return;
78736         }
78737         var tw = Math.max(this.cm.getTotalWidth(), this.el.dom.offsetWidth-this.getScrollOffset()) +'px';
78738         var gs = this.getGroups();
78739         for(var i = 0, len = gs.length; i < len; i++){
78740             gs[i].firstChild.style.width = tw;
78741         }
78742     },
78743
78744     // private
78745     onColumnWidthUpdated : function(col, w, tw){
78746         Ext.grid.GroupingView.superclass.onColumnWidthUpdated.call(this, col, w, tw);
78747         this.updateGroupWidths();
78748     },
78749
78750     // private
78751     onAllColumnWidthsUpdated : function(ws, tw){
78752         Ext.grid.GroupingView.superclass.onAllColumnWidthsUpdated.call(this, ws, tw);
78753         this.updateGroupWidths();
78754     },
78755
78756     // private
78757     onColumnHiddenUpdated : function(col, hidden, tw){
78758         Ext.grid.GroupingView.superclass.onColumnHiddenUpdated.call(this, col, hidden, tw);
78759         this.updateGroupWidths();
78760     },
78761
78762     // private
78763     onLayout : function(){
78764         this.updateGroupWidths();
78765     },
78766
78767     // private
78768     onBeforeRowSelect : function(sm, rowIndex){
78769         this.toggleRowIndex(rowIndex, true);
78770     }
78771 });
78772 // private
78773 Ext.grid.GroupingView.GROUP_ID = 1000;