Roo/Template.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13
14 /*
15  * These classes are derivatives of the similarly named classes in the YUI Library.
16  * The original license:
17  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18  * Code licensed under the BSD License:
19  * http://developer.yahoo.net/yui/license.txt
20  */
21
22 (function() {
23
24 var Event=Roo.EventManager;
25 var Dom=Roo.lib.Dom;
26
27 /**
28  * @class Roo.dd.DragDrop
29  * @extends Roo.util.Observable
30  * Defines the interface and base operation of items that that can be
31  * dragged or can be drop targets.  It was designed to be extended, overriding
32  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
33  * Up to three html elements can be associated with a DragDrop instance:
34  * <ul>
35  * <li>linked element: the element that is passed into the constructor.
36  * This is the element which defines the boundaries for interaction with
37  * other DragDrop objects.</li>
38  * <li>handle element(s): The drag operation only occurs if the element that
39  * was clicked matches a handle element.  By default this is the linked
40  * element, but there are times that you will want only a portion of the
41  * linked element to initiate the drag operation, and the setHandleElId()
42  * method provides a way to define this.</li>
43  * <li>drag element: this represents the element that would be moved along
44  * with the cursor during a drag operation.  By default, this is the linked
45  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
46  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
47  * </li>
48  * </ul>
49  * This class should not be instantiated until the onload event to ensure that
50  * the associated elements are available.
51  * The following would define a DragDrop obj that would interact with any
52  * other DragDrop obj in the "group1" group:
53  * <pre>
54  *  dd = new Roo.dd.DragDrop("div1", "group1");
55  * </pre>
56  * Since none of the event handlers have been implemented, nothing would
57  * actually happen if you were to run the code above.  Normally you would
58  * override this class or one of the default implementations, but you can
59  * also override the methods you want on an instance of the class...
60  * <pre>
61  *  dd.onDragDrop = function(e, id) {
62  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
63  *  }
64  * </pre>
65  * @constructor
66  * @param {String} id of the element that is linked to this instance
67  * @param {String} sGroup the group of related DragDrop objects
68  * @param {object} config an object containing configurable attributes
69  *                Valid properties for DragDrop:
70  *                    padding, isTarget, maintainOffset, primaryButtonOnly
71  */
72 Roo.dd.DragDrop = function(id, sGroup, config) {
73     if (id) {
74         this.init(id, sGroup, config);
75     }
76     
77 };
78
79 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
80
81     /**
82      * The id of the element associated with this object.  This is what we
83      * refer to as the "linked element" because the size and position of
84      * this element is used to determine when the drag and drop objects have
85      * interacted.
86      * @property id
87      * @type String
88      */
89     id: null,
90
91     /**
92      * Configuration attributes passed into the constructor
93      * @property config
94      * @type object
95      */
96     config: null,
97
98     /**
99      * The id of the element that will be dragged.  By default this is same
100      * as the linked element , but could be changed to another element. Ex:
101      * Roo.dd.DDProxy
102      * @property dragElId
103      * @type String
104      * @private
105      */
106     dragElId: null,
107
108     /**
109      * the id of the element that initiates the drag operation.  By default
110      * this is the linked element, but could be changed to be a child of this
111      * element.  This lets us do things like only starting the drag when the
112      * header element within the linked html element is clicked.
113      * @property handleElId
114      * @type String
115      * @private
116      */
117     handleElId: null,
118
119     /**
120      * An associative array of HTML tags that will be ignored if clicked.
121      * @property invalidHandleTypes
122      * @type {string: string}
123      */
124     invalidHandleTypes: null,
125
126     /**
127      * An associative array of ids for elements that will be ignored if clicked
128      * @property invalidHandleIds
129      * @type {string: string}
130      */
131     invalidHandleIds: null,
132
133     /**
134      * An indexted array of css class names for elements that will be ignored
135      * if clicked.
136      * @property invalidHandleClasses
137      * @type string[]
138      */
139     invalidHandleClasses: null,
140
141     /**
142      * The linked element's absolute X position at the time the drag was
143      * started
144      * @property startPageX
145      * @type int
146      * @private
147      */
148     startPageX: 0,
149
150     /**
151      * The linked element's absolute X position at the time the drag was
152      * started
153      * @property startPageY
154      * @type int
155      * @private
156      */
157     startPageY: 0,
158
159     /**
160      * The group defines a logical collection of DragDrop objects that are
161      * related.  Instances only get events when interacting with other
162      * DragDrop object in the same group.  This lets us define multiple
163      * groups using a single DragDrop subclass if we want.
164      * @property groups
165      * @type {string: string}
166      */
167     groups: null,
168
169     /**
170      * Individual drag/drop instances can be locked.  This will prevent
171      * onmousedown start drag.
172      * @property locked
173      * @type boolean
174      * @private
175      */
176     locked: false,
177
178     /**
179      * Lock this instance
180      * @method lock
181      */
182     lock: function() { this.locked = true; },
183
184     /**
185      * Unlock this instace
186      * @method unlock
187      */
188     unlock: function() { this.locked = false; },
189
190     /**
191      * By default, all insances can be a drop target.  This can be disabled by
192      * setting isTarget to false.
193      * @method isTarget
194      * @type boolean
195      */
196     isTarget: true,
197
198     /**
199      * The padding configured for this drag and drop object for calculating
200      * the drop zone intersection with this object.
201      * @method padding
202      * @type int[]
203      */
204     padding: null,
205
206     /**
207      * Cached reference to the linked element
208      * @property _domRef
209      * @private
210      */
211     _domRef: null,
212
213     /**
214      * Internal typeof flag
215      * @property __ygDragDrop
216      * @private
217      */
218     __ygDragDrop: true,
219
220     /**
221      * Set to true when horizontal contraints are applied
222      * @property constrainX
223      * @type boolean
224      * @private
225      */
226     constrainX: false,
227
228     /**
229      * Set to true when vertical contraints are applied
230      * @property constrainY
231      * @type boolean
232      * @private
233      */
234     constrainY: false,
235
236     /**
237      * The left constraint
238      * @property minX
239      * @type int
240      * @private
241      */
242     minX: 0,
243
244     /**
245      * The right constraint
246      * @property maxX
247      * @type int
248      * @private
249      */
250     maxX: 0,
251
252     /**
253      * The up constraint
254      * @property minY
255      * @type int
256      * @type int
257      * @private
258      */
259     minY: 0,
260
261     /**
262      * The down constraint
263      * @property maxY
264      * @type int
265      * @private
266      */
267     maxY: 0,
268
269     /**
270      * Maintain offsets when we resetconstraints.  Set to true when you want
271      * the position of the element relative to its parent to stay the same
272      * when the page changes
273      *
274      * @property maintainOffset
275      * @type boolean
276      */
277     maintainOffset: false,
278
279     /**
280      * Array of pixel locations the element will snap to if we specified a
281      * horizontal graduation/interval.  This array is generated automatically
282      * when you define a tick interval.
283      * @property xTicks
284      * @type int[]
285      */
286     xTicks: null,
287
288     /**
289      * Array of pixel locations the element will snap to if we specified a
290      * vertical graduation/interval.  This array is generated automatically
291      * when you define a tick interval.
292      * @property yTicks
293      * @type int[]
294      */
295     yTicks: null,
296
297     /**
298      * By default the drag and drop instance will only respond to the primary
299      * button click (left button for a right-handed mouse).  Set to true to
300      * allow drag and drop to start with any mouse click that is propogated
301      * by the browser
302      * @property primaryButtonOnly
303      * @type boolean
304      */
305     primaryButtonOnly: true,
306
307     /**
308      * The availabe property is false until the linked dom element is accessible.
309      * @property available
310      * @type boolean
311      */
312     available: false,
313
314     /**
315      * By default, drags can only be initiated if the mousedown occurs in the
316      * region the linked element is.  This is done in part to work around a
317      * bug in some browsers that mis-report the mousedown if the previous
318      * mouseup happened outside of the window.  This property is set to true
319      * if outer handles are defined.
320      *
321      * @property hasOuterHandles
322      * @type boolean
323      * @default false
324      */
325     hasOuterHandles: false,
326
327     /**
328      * Code that executes immediately before the startDrag event
329      * @method b4StartDrag
330      * @private
331      */
332     b4StartDrag: function(x, y) { },
333
334     /**
335      * Abstract method called after a drag/drop object is clicked
336      * and the drag or mousedown time thresholds have beeen met.
337      * @method startDrag
338      * @param {int} X click location
339      * @param {int} Y click location
340      */
341     startDrag: function(x, y) { /* override this */ },
342
343     /**
344      * Code that executes immediately before the onDrag event
345      * @method b4Drag
346      * @private
347      */
348     b4Drag: function(e) { },
349
350     /**
351      * Abstract method called during the onMouseMove event while dragging an
352      * object.
353      * @method onDrag
354      * @param {Event} e the mousemove event
355      */
356     onDrag: function(e) { /* override this */ },
357
358     /**
359      * Abstract method called when this element fist begins hovering over
360      * another DragDrop obj
361      * @method onDragEnter
362      * @param {Event} e the mousemove event
363      * @param {String|DragDrop[]} id In POINT mode, the element
364      * id this is hovering over.  In INTERSECT mode, an array of one or more
365      * dragdrop items being hovered over.
366      */
367     onDragEnter: function(e, id) { /* override this */ },
368
369     /**
370      * Code that executes immediately before the onDragOver event
371      * @method b4DragOver
372      * @private
373      */
374     b4DragOver: function(e) { },
375
376     /**
377      * Abstract method called when this element is hovering over another
378      * DragDrop obj
379      * @method onDragOver
380      * @param {Event} e the mousemove event
381      * @param {String|DragDrop[]} id In POINT mode, the element
382      * id this is hovering over.  In INTERSECT mode, an array of dd items
383      * being hovered over.
384      */
385     onDragOver: function(e, id) { /* override this */ },
386
387     /**
388      * Code that executes immediately before the onDragOut event
389      * @method b4DragOut
390      * @private
391      */
392     b4DragOut: function(e) { },
393
394     /**
395      * Abstract method called when we are no longer hovering over an element
396      * @method onDragOut
397      * @param {Event} e the mousemove event
398      * @param {String|DragDrop[]} id In POINT mode, the element
399      * id this was hovering over.  In INTERSECT mode, an array of dd items
400      * that the mouse is no longer over.
401      */
402     onDragOut: function(e, id) { /* override this */ },
403
404     /**
405      * Code that executes immediately before the onDragDrop event
406      * @method b4DragDrop
407      * @private
408      */
409     b4DragDrop: function(e) { },
410
411     /**
412      * Abstract method called when this item is dropped on another DragDrop
413      * obj
414      * @method onDragDrop
415      * @param {Event} e the mouseup event
416      * @param {String|DragDrop[]} id In POINT mode, the element
417      * id this was dropped on.  In INTERSECT mode, an array of dd items this
418      * was dropped on.
419      */
420     onDragDrop: function(e, id) { /* override this */ },
421
422     /**
423      * Abstract method called when this item is dropped on an area with no
424      * drop target
425      * @method onInvalidDrop
426      * @param {Event} e the mouseup event
427      */
428     onInvalidDrop: function(e) { /* override this */ },
429
430     /**
431      * Code that executes immediately before the endDrag event
432      * @method b4EndDrag
433      * @private
434      */
435     b4EndDrag: function(e) { },
436
437     /**
438      * Fired when we are done dragging the object
439      * @method endDrag
440      * @param {Event} e the mouseup event
441      */
442     endDrag: function(e) { /* override this */ },
443
444     /**
445      * Code executed immediately before the onMouseDown event
446      * @method b4MouseDown
447      * @param {Event} e the mousedown event
448      * @private
449      */
450     b4MouseDown: function(e) {  },
451
452     /**
453      * Event handler that fires when a drag/drop obj gets a mousedown
454      * @method onMouseDown
455      * @param {Event} e the mousedown event
456      */
457     onMouseDown: function(e) { /* override this */ },
458
459     /**
460      * Event handler that fires when a drag/drop obj gets a mouseup
461      * @method onMouseUp
462      * @param {Event} e the mouseup event
463      */
464     onMouseUp: function(e) { /* override this */ },
465
466     /**
467      * Override the onAvailable method to do what is needed after the initial
468      * position was determined.
469      * @method onAvailable
470      */
471     onAvailable: function () {
472     },
473
474     /*
475      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
476      * @type Object
477      */
478     defaultPadding : {left:0, right:0, top:0, bottom:0},
479
480     /*
481      * Initializes the drag drop object's constraints to restrict movement to a certain element.
482  *
483  * Usage:
484  <pre><code>
485  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
486                 { dragElId: "existingProxyDiv" });
487  dd.startDrag = function(){
488      this.constrainTo("parent-id");
489  };
490  </code></pre>
491  * Or you can initalize it using the {@link Roo.Element} object:
492  <pre><code>
493  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
494      startDrag : function(){
495          this.constrainTo("parent-id");
496      }
497  });
498  </code></pre>
499      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
500      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
501      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
502      * an object containing the sides to pad. For example: {right:10, bottom:10}
503      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
504      */
505     constrainTo : function(constrainTo, pad, inContent){
506         if(typeof pad == "number"){
507             pad = {left: pad, right:pad, top:pad, bottom:pad};
508         }
509         pad = pad || this.defaultPadding;
510         var b = Roo.get(this.getEl()).getBox();
511         var ce = Roo.get(constrainTo);
512         var s = ce.getScroll();
513         var c, cd = ce.dom;
514         if(cd == document.body){
515             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
516         }else{
517             xy = ce.getXY();
518             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
519         }
520
521
522         var topSpace = b.y - c.y;
523         var leftSpace = b.x - c.x;
524
525         this.resetConstraints();
526         this.setXConstraint(leftSpace - (pad.left||0), // left
527                 c.width - leftSpace - b.width - (pad.right||0) //right
528         );
529         this.setYConstraint(topSpace - (pad.top||0), //top
530                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
531         );
532     },
533
534     /**
535      * Returns a reference to the linked element
536      * @method getEl
537      * @return {HTMLElement} the html element
538      */
539     getEl: function() {
540         if (!this._domRef) {
541             this._domRef = Roo.getDom(this.id);
542         }
543
544         return this._domRef;
545     },
546
547     /**
548      * Returns a reference to the actual element to drag.  By default this is
549      * the same as the html element, but it can be assigned to another
550      * element. An example of this can be found in Roo.dd.DDProxy
551      * @method getDragEl
552      * @return {HTMLElement} the html element
553      */
554     getDragEl: function() {
555         return Roo.getDom(this.dragElId);
556     },
557
558     /**
559      * Sets up the DragDrop object.  Must be called in the constructor of any
560      * Roo.dd.DragDrop subclass
561      * @method init
562      * @param id the id of the linked element
563      * @param {String} sGroup the group of related items
564      * @param {object} config configuration attributes
565      */
566     init: function(id, sGroup, config) {
567         this.initTarget(id, sGroup, config);
568         Event.on(this.id, "mousedown", this.handleMouseDown, this);
569         // Event.on(this.id, "selectstart", Event.preventDefault);
570     },
571
572     /**
573      * Initializes Targeting functionality only... the object does not
574      * get a mousedown handler.
575      * @method initTarget
576      * @param id the id of the linked element
577      * @param {String} sGroup the group of related items
578      * @param {object} config configuration attributes
579      */
580     initTarget: function(id, sGroup, config) {
581
582         // configuration attributes
583         this.config = config || {};
584
585         // create a local reference to the drag and drop manager
586         this.DDM = Roo.dd.DDM;
587         // initialize the groups array
588         this.groups = {};
589
590         // assume that we have an element reference instead of an id if the
591         // parameter is not a string
592         if (typeof id !== "string") {
593             id = Roo.id(id);
594         }
595
596         // set the id
597         this.id = id;
598
599         // add to an interaction group
600         this.addToGroup((sGroup) ? sGroup : "default");
601
602         // We don't want to register this as the handle with the manager
603         // so we just set the id rather than calling the setter.
604         this.handleElId = id;
605
606         // the linked element is the element that gets dragged by default
607         this.setDragElId(id);
608
609         // by default, clicked anchors will not start drag operations.
610         this.invalidHandleTypes = { A: "A" };
611         this.invalidHandleIds = {};
612         this.invalidHandleClasses = [];
613
614         this.applyConfig();
615
616         this.handleOnAvailable();
617     },
618
619     /**
620      * Applies the configuration parameters that were passed into the constructor.
621      * This is supposed to happen at each level through the inheritance chain.  So
622      * a DDProxy implentation will execute apply config on DDProxy, DD, and
623      * DragDrop in order to get all of the parameters that are available in
624      * each object.
625      * @method applyConfig
626      */
627     applyConfig: function() {
628
629         // configurable properties:
630         //    padding, isTarget, maintainOffset, primaryButtonOnly
631         this.padding           = this.config.padding || [0, 0, 0, 0];
632         this.isTarget          = (this.config.isTarget !== false);
633         this.maintainOffset    = (this.config.maintainOffset);
634         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
635
636     },
637
638     /**
639      * Executed when the linked element is available
640      * @method handleOnAvailable
641      * @private
642      */
643     handleOnAvailable: function() {
644         this.available = true;
645         this.resetConstraints();
646         this.onAvailable();
647     },
648
649      /**
650      * Configures the padding for the target zone in px.  Effectively expands
651      * (or reduces) the virtual object size for targeting calculations.
652      * Supports css-style shorthand; if only one parameter is passed, all sides
653      * will have that padding, and if only two are passed, the top and bottom
654      * will have the first param, the left and right the second.
655      * @method setPadding
656      * @param {int} iTop    Top pad
657      * @param {int} iRight  Right pad
658      * @param {int} iBot    Bot pad
659      * @param {int} iLeft   Left pad
660      */
661     setPadding: function(iTop, iRight, iBot, iLeft) {
662         // this.padding = [iLeft, iRight, iTop, iBot];
663         if (!iRight && 0 !== iRight) {
664             this.padding = [iTop, iTop, iTop, iTop];
665         } else if (!iBot && 0 !== iBot) {
666             this.padding = [iTop, iRight, iTop, iRight];
667         } else {
668             this.padding = [iTop, iRight, iBot, iLeft];
669         }
670     },
671
672     /**
673      * Stores the initial placement of the linked element.
674      * @method setInitialPosition
675      * @param {int} diffX   the X offset, default 0
676      * @param {int} diffY   the Y offset, default 0
677      */
678     setInitPosition: function(diffX, diffY) {
679         var el = this.getEl();
680
681         if (!this.DDM.verifyEl(el)) {
682             return;
683         }
684
685         var dx = diffX || 0;
686         var dy = diffY || 0;
687
688         var p = Dom.getXY( el );
689
690         this.initPageX = p[0] - dx;
691         this.initPageY = p[1] - dy;
692
693         this.lastPageX = p[0];
694         this.lastPageY = p[1];
695
696
697         this.setStartPosition(p);
698     },
699
700     /**
701      * Sets the start position of the element.  This is set when the obj
702      * is initialized, the reset when a drag is started.
703      * @method setStartPosition
704      * @param pos current position (from previous lookup)
705      * @private
706      */
707     setStartPosition: function(pos) {
708         var p = pos || Dom.getXY( this.getEl() );
709         this.deltaSetXY = null;
710
711         this.startPageX = p[0];
712         this.startPageY = p[1];
713     },
714
715     /**
716      * Add this instance to a group of related drag/drop objects.  All
717      * instances belong to at least one group, and can belong to as many
718      * groups as needed.
719      * @method addToGroup
720      * @param sGroup {string} the name of the group
721      */
722     addToGroup: function(sGroup) {
723         this.groups[sGroup] = true;
724         this.DDM.regDragDrop(this, sGroup);
725     },
726
727     /**
728      * Remove's this instance from the supplied interaction group
729      * @method removeFromGroup
730      * @param {string}  sGroup  The group to drop
731      */
732     removeFromGroup: function(sGroup) {
733         if (this.groups[sGroup]) {
734             delete this.groups[sGroup];
735         }
736
737         this.DDM.removeDDFromGroup(this, sGroup);
738     },
739
740     /**
741      * Allows you to specify that an element other than the linked element
742      * will be moved with the cursor during a drag
743      * @method setDragElId
744      * @param id {string} the id of the element that will be used to initiate the drag
745      */
746     setDragElId: function(id) {
747         this.dragElId = id;
748     },
749
750     /**
751      * Allows you to specify a child of the linked element that should be
752      * used to initiate the drag operation.  An example of this would be if
753      * you have a content div with text and links.  Clicking anywhere in the
754      * content area would normally start the drag operation.  Use this method
755      * to specify that an element inside of the content div is the element
756      * that starts the drag operation.
757      * @method setHandleElId
758      * @param id {string} the id of the element that will be used to
759      * initiate the drag.
760      */
761     setHandleElId: function(id) {
762         if (typeof id !== "string") {
763             id = Roo.id(id);
764         }
765         this.handleElId = id;
766         this.DDM.regHandle(this.id, id);
767     },
768
769     /**
770      * Allows you to set an element outside of the linked element as a drag
771      * handle
772      * @method setOuterHandleElId
773      * @param id the id of the element that will be used to initiate the drag
774      */
775     setOuterHandleElId: function(id) {
776         if (typeof id !== "string") {
777             id = Roo.id(id);
778         }
779         Event.on(id, "mousedown",
780                 this.handleMouseDown, this);
781         this.setHandleElId(id);
782
783         this.hasOuterHandles = true;
784     },
785
786     /**
787      * Remove all drag and drop hooks for this element
788      * @method unreg
789      */
790     unreg: function() {
791         Event.un(this.id, "mousedown",
792                 this.handleMouseDown);
793         this._domRef = null;
794         this.DDM._remove(this);
795     },
796
797     destroy : function(){
798         this.unreg();
799     },
800
801     /**
802      * Returns true if this instance is locked, or the drag drop mgr is locked
803      * (meaning that all drag/drop is disabled on the page.)
804      * @method isLocked
805      * @return {boolean} true if this obj or all drag/drop is locked, else
806      * false
807      */
808     isLocked: function() {
809         return (this.DDM.isLocked() || this.locked);
810     },
811
812     /**
813      * Fired when this object is clicked
814      * @method handleMouseDown
815      * @param {Event} e
816      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
817      * @private
818      */
819     handleMouseDown: function(e, oDD){
820         if (this.primaryButtonOnly && e.button != 0) {
821             return;
822         }
823
824         if (this.isLocked()) {
825             return;
826         }
827
828         this.DDM.refreshCache(this.groups);
829
830         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
831         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
832         } else {
833             if (this.clickValidator(e)) {
834
835                 // set the initial element position
836                 this.setStartPosition();
837
838
839                 this.b4MouseDown(e);
840                 this.onMouseDown(e);
841
842                 this.DDM.handleMouseDown(e, this);
843
844                 this.DDM.stopEvent(e);
845             } else {
846
847
848             }
849         }
850     },
851
852     clickValidator: function(e) {
853         var target = e.getTarget();
854         return ( this.isValidHandleChild(target) &&
855                     (this.id == this.handleElId ||
856                         this.DDM.handleWasClicked(target, this.id)) );
857     },
858
859     /**
860      * Allows you to specify a tag name that should not start a drag operation
861      * when clicked.  This is designed to facilitate embedding links within a
862      * drag handle that do something other than start the drag.
863      * @method addInvalidHandleType
864      * @param {string} tagName the type of element to exclude
865      */
866     addInvalidHandleType: function(tagName) {
867         var type = tagName.toUpperCase();
868         this.invalidHandleTypes[type] = type;
869     },
870
871     /**
872      * Lets you to specify an element id for a child of a drag handle
873      * that should not initiate a drag
874      * @method addInvalidHandleId
875      * @param {string} id the element id of the element you wish to ignore
876      */
877     addInvalidHandleId: function(id) {
878         if (typeof id !== "string") {
879             id = Roo.id(id);
880         }
881         this.invalidHandleIds[id] = id;
882     },
883
884     /**
885      * Lets you specify a css class of elements that will not initiate a drag
886      * @method addInvalidHandleClass
887      * @param {string} cssClass the class of the elements you wish to ignore
888      */
889     addInvalidHandleClass: function(cssClass) {
890         this.invalidHandleClasses.push(cssClass);
891     },
892
893     /**
894      * Unsets an excluded tag name set by addInvalidHandleType
895      * @method removeInvalidHandleType
896      * @param {string} tagName the type of element to unexclude
897      */
898     removeInvalidHandleType: function(tagName) {
899         var type = tagName.toUpperCase();
900         // this.invalidHandleTypes[type] = null;
901         delete this.invalidHandleTypes[type];
902     },
903
904     /**
905      * Unsets an invalid handle id
906      * @method removeInvalidHandleId
907      * @param {string} id the id of the element to re-enable
908      */
909     removeInvalidHandleId: function(id) {
910         if (typeof id !== "string") {
911             id = Roo.id(id);
912         }
913         delete this.invalidHandleIds[id];
914     },
915
916     /**
917      * Unsets an invalid css class
918      * @method removeInvalidHandleClass
919      * @param {string} cssClass the class of the element(s) you wish to
920      * re-enable
921      */
922     removeInvalidHandleClass: function(cssClass) {
923         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
924             if (this.invalidHandleClasses[i] == cssClass) {
925                 delete this.invalidHandleClasses[i];
926             }
927         }
928     },
929
930     /**
931      * Checks the tag exclusion list to see if this click should be ignored
932      * @method isValidHandleChild
933      * @param {HTMLElement} node the HTMLElement to evaluate
934      * @return {boolean} true if this is a valid tag type, false if not
935      */
936     isValidHandleChild: function(node) {
937
938         var valid = true;
939         // var n = (node.nodeName == "#text") ? node.parentNode : node;
940         var nodeName;
941         try {
942             nodeName = node.nodeName.toUpperCase();
943         } catch(e) {
944             nodeName = node.nodeName;
945         }
946         valid = valid && !this.invalidHandleTypes[nodeName];
947         valid = valid && !this.invalidHandleIds[node.id];
948
949         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
950             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
951         }
952
953
954         return valid;
955
956     },
957
958     /**
959      * Create the array of horizontal tick marks if an interval was specified
960      * in setXConstraint().
961      * @method setXTicks
962      * @private
963      */
964     setXTicks: function(iStartX, iTickSize) {
965         this.xTicks = [];
966         this.xTickSize = iTickSize;
967
968         var tickMap = {};
969
970         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
971             if (!tickMap[i]) {
972                 this.xTicks[this.xTicks.length] = i;
973                 tickMap[i] = true;
974             }
975         }
976
977         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
978             if (!tickMap[i]) {
979                 this.xTicks[this.xTicks.length] = i;
980                 tickMap[i] = true;
981             }
982         }
983
984         this.xTicks.sort(this.DDM.numericSort) ;
985     },
986
987     /**
988      * Create the array of vertical tick marks if an interval was specified in
989      * setYConstraint().
990      * @method setYTicks
991      * @private
992      */
993     setYTicks: function(iStartY, iTickSize) {
994         this.yTicks = [];
995         this.yTickSize = iTickSize;
996
997         var tickMap = {};
998
999         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
1000             if (!tickMap[i]) {
1001                 this.yTicks[this.yTicks.length] = i;
1002                 tickMap[i] = true;
1003             }
1004         }
1005
1006         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1007             if (!tickMap[i]) {
1008                 this.yTicks[this.yTicks.length] = i;
1009                 tickMap[i] = true;
1010             }
1011         }
1012
1013         this.yTicks.sort(this.DDM.numericSort) ;
1014     },
1015
1016     /**
1017      * By default, the element can be dragged any place on the screen.  Use
1018      * this method to limit the horizontal travel of the element.  Pass in
1019      * 0,0 for the parameters if you want to lock the drag to the y axis.
1020      * @method setXConstraint
1021      * @param {int} iLeft the number of pixels the element can move to the left
1022      * @param {int} iRight the number of pixels the element can move to the
1023      * right
1024      * @param {int} iTickSize optional parameter for specifying that the
1025      * element
1026      * should move iTickSize pixels at a time.
1027      */
1028     setXConstraint: function(iLeft, iRight, iTickSize) {
1029         this.leftConstraint = iLeft;
1030         this.rightConstraint = iRight;
1031
1032         this.minX = this.initPageX - iLeft;
1033         this.maxX = this.initPageX + iRight;
1034         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1035
1036         this.constrainX = true;
1037     },
1038
1039     /**
1040      * Clears any constraints applied to this instance.  Also clears ticks
1041      * since they can't exist independent of a constraint at this time.
1042      * @method clearConstraints
1043      */
1044     clearConstraints: function() {
1045         this.constrainX = false;
1046         this.constrainY = false;
1047         this.clearTicks();
1048     },
1049
1050     /**
1051      * Clears any tick interval defined for this instance
1052      * @method clearTicks
1053      */
1054     clearTicks: function() {
1055         this.xTicks = null;
1056         this.yTicks = null;
1057         this.xTickSize = 0;
1058         this.yTickSize = 0;
1059     },
1060
1061     /**
1062      * By default, the element can be dragged any place on the screen.  Set
1063      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1064      * parameters if you want to lock the drag to the x axis.
1065      * @method setYConstraint
1066      * @param {int} iUp the number of pixels the element can move up
1067      * @param {int} iDown the number of pixels the element can move down
1068      * @param {int} iTickSize optional parameter for specifying that the
1069      * element should move iTickSize pixels at a time.
1070      */
1071     setYConstraint: function(iUp, iDown, iTickSize) {
1072         this.topConstraint = iUp;
1073         this.bottomConstraint = iDown;
1074
1075         this.minY = this.initPageY - iUp;
1076         this.maxY = this.initPageY + iDown;
1077         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1078
1079         this.constrainY = true;
1080
1081     },
1082
1083     /**
1084      * resetConstraints must be called if you manually reposition a dd element.
1085      * @method resetConstraints
1086      * @param {boolean} maintainOffset
1087      */
1088     resetConstraints: function() {
1089
1090
1091         // Maintain offsets if necessary
1092         if (this.initPageX || this.initPageX === 0) {
1093             // figure out how much this thing has moved
1094             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1095             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1096
1097             this.setInitPosition(dx, dy);
1098
1099         // This is the first time we have detected the element's position
1100         } else {
1101             this.setInitPosition();
1102         }
1103
1104         if (this.constrainX) {
1105             this.setXConstraint( this.leftConstraint,
1106                                  this.rightConstraint,
1107                                  this.xTickSize        );
1108         }
1109
1110         if (this.constrainY) {
1111             this.setYConstraint( this.topConstraint,
1112                                  this.bottomConstraint,
1113                                  this.yTickSize         );
1114         }
1115     },
1116
1117     /**
1118      * Normally the drag element is moved pixel by pixel, but we can specify
1119      * that it move a number of pixels at a time.  This method resolves the
1120      * location when we have it set up like this.
1121      * @method getTick
1122      * @param {int} val where we want to place the object
1123      * @param {int[]} tickArray sorted array of valid points
1124      * @return {int} the closest tick
1125      * @private
1126      */
1127     getTick: function(val, tickArray) {
1128
1129         if (!tickArray) {
1130             // If tick interval is not defined, it is effectively 1 pixel,
1131             // so we return the value passed to us.
1132             return val;
1133         } else if (tickArray[0] >= val) {
1134             // The value is lower than the first tick, so we return the first
1135             // tick.
1136             return tickArray[0];
1137         } else {
1138             for (var i=0, len=tickArray.length; i<len; ++i) {
1139                 var next = i + 1;
1140                 if (tickArray[next] && tickArray[next] >= val) {
1141                     var diff1 = val - tickArray[i];
1142                     var diff2 = tickArray[next] - val;
1143                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1144                 }
1145             }
1146
1147             // The value is larger than the last tick, so we return the last
1148             // tick.
1149             return tickArray[tickArray.length - 1];
1150         }
1151     },
1152
1153     /**
1154      * toString method
1155      * @method toString
1156      * @return {string} string representation of the dd obj
1157      */
1158     toString: function() {
1159         return ("DragDrop " + this.id);
1160     }
1161
1162 });
1163
1164 })();
1165 /*
1166  * Based on:
1167  * Ext JS Library 1.1.1
1168  * Copyright(c) 2006-2007, Ext JS, LLC.
1169  *
1170  * Originally Released Under LGPL - original licence link has changed is not relivant.
1171  *
1172  * Fork - LGPL
1173  * <script type="text/javascript">
1174  */
1175
1176
1177 /**
1178  * The drag and drop utility provides a framework for building drag and drop
1179  * applications.  In addition to enabling drag and drop for specific elements,
1180  * the drag and drop elements are tracked by the manager class, and the
1181  * interactions between the various elements are tracked during the drag and
1182  * the implementing code is notified about these important moments.
1183  */
1184
1185 // Only load the library once.  Rewriting the manager class would orphan
1186 // existing drag and drop instances.
1187 if (!Roo.dd.DragDropMgr) {
1188
1189 /**
1190  * @class Roo.dd.DragDropMgr
1191  * DragDropMgr is a singleton that tracks the element interaction for
1192  * all DragDrop items in the window.  Generally, you will not call
1193  * this class directly, but it does have helper methods that could
1194  * be useful in your DragDrop implementations.
1195  * @singleton
1196  */
1197 Roo.dd.DragDropMgr = function() {
1198
1199     var Event = Roo.EventManager;
1200
1201     return {
1202
1203         /**
1204          * Two dimensional Array of registered DragDrop objects.  The first
1205          * dimension is the DragDrop item group, the second the DragDrop
1206          * object.
1207          * @property ids
1208          * @type {string: string}
1209          * @private
1210          * @static
1211          */
1212         ids: {},
1213
1214         /**
1215          * Array of element ids defined as drag handles.  Used to determine
1216          * if the element that generated the mousedown event is actually the
1217          * handle and not the html element itself.
1218          * @property handleIds
1219          * @type {string: string}
1220          * @private
1221          * @static
1222          */
1223         handleIds: {},
1224
1225         /**
1226          * the DragDrop object that is currently being dragged
1227          * @property dragCurrent
1228          * @type DragDrop
1229          * @private
1230          * @static
1231          **/
1232         dragCurrent: null,
1233
1234         /**
1235          * the DragDrop object(s) that are being hovered over
1236          * @property dragOvers
1237          * @type Array
1238          * @private
1239          * @static
1240          */
1241         dragOvers: {},
1242
1243         /**
1244          * the X distance between the cursor and the object being dragged
1245          * @property deltaX
1246          * @type int
1247          * @private
1248          * @static
1249          */
1250         deltaX: 0,
1251
1252         /**
1253          * the Y distance between the cursor and the object being dragged
1254          * @property deltaY
1255          * @type int
1256          * @private
1257          * @static
1258          */
1259         deltaY: 0,
1260
1261         /**
1262          * Flag to determine if we should prevent the default behavior of the
1263          * events we define. By default this is true, but this can be set to
1264          * false if you need the default behavior (not recommended)
1265          * @property preventDefault
1266          * @type boolean
1267          * @static
1268          */
1269         preventDefault: true,
1270
1271         /**
1272          * Flag to determine if we should stop the propagation of the events
1273          * we generate. This is true by default but you may want to set it to
1274          * false if the html element contains other features that require the
1275          * mouse click.
1276          * @property stopPropagation
1277          * @type boolean
1278          * @static
1279          */
1280         stopPropagation: true,
1281
1282         /**
1283          * Internal flag that is set to true when drag and drop has been
1284          * intialized
1285          * @property initialized
1286          * @private
1287          * @static
1288          */
1289         initalized: false,
1290
1291         /**
1292          * All drag and drop can be disabled.
1293          * @property locked
1294          * @private
1295          * @static
1296          */
1297         locked: false,
1298
1299         /**
1300          * Called the first time an element is registered.
1301          * @method init
1302          * @private
1303          * @static
1304          */
1305         init: function() {
1306             this.initialized = true;
1307         },
1308
1309         /**
1310          * In point mode, drag and drop interaction is defined by the
1311          * location of the cursor during the drag/drop
1312          * @property POINT
1313          * @type int
1314          * @static
1315          */
1316         POINT: 0,
1317
1318         /**
1319          * In intersect mode, drag and drop interactio nis defined by the
1320          * overlap of two or more drag and drop objects.
1321          * @property INTERSECT
1322          * @type int
1323          * @static
1324          */
1325         INTERSECT: 1,
1326
1327         /**
1328          * The current drag and drop mode.  Default: POINT
1329          * @property mode
1330          * @type int
1331          * @static
1332          */
1333         mode: 0,
1334
1335         /**
1336          * Runs method on all drag and drop objects
1337          * @method _execOnAll
1338          * @private
1339          * @static
1340          */
1341         _execOnAll: function(sMethod, args) {
1342             for (var i in this.ids) {
1343                 for (var j in this.ids[i]) {
1344                     var oDD = this.ids[i][j];
1345                     if (! this.isTypeOfDD(oDD)) {
1346                         continue;
1347                     }
1348                     oDD[sMethod].apply(oDD, args);
1349                 }
1350             }
1351         },
1352
1353         /**
1354          * Drag and drop initialization.  Sets up the global event handlers
1355          * @method _onLoad
1356          * @private
1357          * @static
1358          */
1359         _onLoad: function() {
1360
1361             this.init();
1362
1363
1364             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1365             Event.on(document, "mousemove", this.handleMouseMove, this, true);
1366             Event.on(window,   "unload",    this._onUnload, this, true);
1367             Event.on(window,   "resize",    this._onResize, this, true);
1368             // Event.on(window,   "mouseout",    this._test);
1369
1370         },
1371
1372         /**
1373          * Reset constraints on all drag and drop objs
1374          * @method _onResize
1375          * @private
1376          * @static
1377          */
1378         _onResize: function(e) {
1379             this._execOnAll("resetConstraints", []);
1380         },
1381
1382         /**
1383          * Lock all drag and drop functionality
1384          * @method lock
1385          * @static
1386          */
1387         lock: function() { this.locked = true; },
1388
1389         /**
1390          * Unlock all drag and drop functionality
1391          * @method unlock
1392          * @static
1393          */
1394         unlock: function() { this.locked = false; },
1395
1396         /**
1397          * Is drag and drop locked?
1398          * @method isLocked
1399          * @return {boolean} True if drag and drop is locked, false otherwise.
1400          * @static
1401          */
1402         isLocked: function() { return this.locked; },
1403
1404         /**
1405          * Location cache that is set for all drag drop objects when a drag is
1406          * initiated, cleared when the drag is finished.
1407          * @property locationCache
1408          * @private
1409          * @static
1410          */
1411         locationCache: {},
1412
1413         /**
1414          * Set useCache to false if you want to force object the lookup of each
1415          * drag and drop linked element constantly during a drag.
1416          * @property useCache
1417          * @type boolean
1418          * @static
1419          */
1420         useCache: true,
1421
1422         /**
1423          * The number of pixels that the mouse needs to move after the
1424          * mousedown before the drag is initiated.  Default=3;
1425          * @property clickPixelThresh
1426          * @type int
1427          * @static
1428          */
1429         clickPixelThresh: 3,
1430
1431         /**
1432          * The number of milliseconds after the mousedown event to initiate the
1433          * drag if we don't get a mouseup event. Default=1000
1434          * @property clickTimeThresh
1435          * @type int
1436          * @static
1437          */
1438         clickTimeThresh: 350,
1439
1440         /**
1441          * Flag that indicates that either the drag pixel threshold or the
1442          * mousdown time threshold has been met
1443          * @property dragThreshMet
1444          * @type boolean
1445          * @private
1446          * @static
1447          */
1448         dragThreshMet: false,
1449
1450         /**
1451          * Timeout used for the click time threshold
1452          * @property clickTimeout
1453          * @type Object
1454          * @private
1455          * @static
1456          */
1457         clickTimeout: null,
1458
1459         /**
1460          * The X position of the mousedown event stored for later use when a
1461          * drag threshold is met.
1462          * @property startX
1463          * @type int
1464          * @private
1465          * @static
1466          */
1467         startX: 0,
1468
1469         /**
1470          * The Y position of the mousedown event stored for later use when a
1471          * drag threshold is met.
1472          * @property startY
1473          * @type int
1474          * @private
1475          * @static
1476          */
1477         startY: 0,
1478
1479         /**
1480          * Each DragDrop instance must be registered with the DragDropMgr.
1481          * This is executed in DragDrop.init()
1482          * @method regDragDrop
1483          * @param {DragDrop} oDD the DragDrop object to register
1484          * @param {String} sGroup the name of the group this element belongs to
1485          * @static
1486          */
1487         regDragDrop: function(oDD, sGroup) {
1488             if (!this.initialized) { this.init(); }
1489
1490             if (!this.ids[sGroup]) {
1491                 this.ids[sGroup] = {};
1492             }
1493             this.ids[sGroup][oDD.id] = oDD;
1494         },
1495
1496         /**
1497          * Removes the supplied dd instance from the supplied group. Executed
1498          * by DragDrop.removeFromGroup, so don't call this function directly.
1499          * @method removeDDFromGroup
1500          * @private
1501          * @static
1502          */
1503         removeDDFromGroup: function(oDD, sGroup) {
1504             if (!this.ids[sGroup]) {
1505                 this.ids[sGroup] = {};
1506             }
1507
1508             var obj = this.ids[sGroup];
1509             if (obj && obj[oDD.id]) {
1510                 delete obj[oDD.id];
1511             }
1512         },
1513
1514         /**
1515          * Unregisters a drag and drop item.  This is executed in
1516          * DragDrop.unreg, use that method instead of calling this directly.
1517          * @method _remove
1518          * @private
1519          * @static
1520          */
1521         _remove: function(oDD) {
1522             for (var g in oDD.groups) {
1523                 if (g && this.ids[g][oDD.id]) {
1524                     delete this.ids[g][oDD.id];
1525                 }
1526             }
1527             delete this.handleIds[oDD.id];
1528         },
1529
1530         /**
1531          * Each DragDrop handle element must be registered.  This is done
1532          * automatically when executing DragDrop.setHandleElId()
1533          * @method regHandle
1534          * @param {String} sDDId the DragDrop id this element is a handle for
1535          * @param {String} sHandleId the id of the element that is the drag
1536          * handle
1537          * @static
1538          */
1539         regHandle: function(sDDId, sHandleId) {
1540             if (!this.handleIds[sDDId]) {
1541                 this.handleIds[sDDId] = {};
1542             }
1543             this.handleIds[sDDId][sHandleId] = sHandleId;
1544         },
1545
1546         /**
1547          * Utility function to determine if a given element has been
1548          * registered as a drag drop item.
1549          * @method isDragDrop
1550          * @param {String} id the element id to check
1551          * @return {boolean} true if this element is a DragDrop item,
1552          * false otherwise
1553          * @static
1554          */
1555         isDragDrop: function(id) {
1556             return ( this.getDDById(id) ) ? true : false;
1557         },
1558
1559         /**
1560          * Returns the drag and drop instances that are in all groups the
1561          * passed in instance belongs to.
1562          * @method getRelated
1563          * @param {DragDrop} p_oDD the obj to get related data for
1564          * @param {boolean} bTargetsOnly if true, only return targetable objs
1565          * @return {DragDrop[]} the related instances
1566          * @static
1567          */
1568         getRelated: function(p_oDD, bTargetsOnly) {
1569             var oDDs = [];
1570             for (var i in p_oDD.groups) {
1571                 for (j in this.ids[i]) {
1572                     var dd = this.ids[i][j];
1573                     if (! this.isTypeOfDD(dd)) {
1574                         continue;
1575                     }
1576                     if (!bTargetsOnly || dd.isTarget) {
1577                         oDDs[oDDs.length] = dd;
1578                     }
1579                 }
1580             }
1581
1582             return oDDs;
1583         },
1584
1585         /**
1586          * Returns true if the specified dd target is a legal target for
1587          * the specifice drag obj
1588          * @method isLegalTarget
1589          * @param {DragDrop} the drag obj
1590          * @param {DragDrop} the target
1591          * @return {boolean} true if the target is a legal target for the
1592          * dd obj
1593          * @static
1594          */
1595         isLegalTarget: function (oDD, oTargetDD) {
1596             var targets = this.getRelated(oDD, true);
1597             for (var i=0, len=targets.length;i<len;++i) {
1598                 if (targets[i].id == oTargetDD.id) {
1599                     return true;
1600                 }
1601             }
1602
1603             return false;
1604         },
1605
1606         /**
1607          * My goal is to be able to transparently determine if an object is
1608          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1609          * returns "object", oDD.constructor.toString() always returns
1610          * "DragDrop" and not the name of the subclass.  So for now it just
1611          * evaluates a well-known variable in DragDrop.
1612          * @method isTypeOfDD
1613          * @param {Object} the object to evaluate
1614          * @return {boolean} true if typeof oDD = DragDrop
1615          * @static
1616          */
1617         isTypeOfDD: function (oDD) {
1618             return (oDD && oDD.__ygDragDrop);
1619         },
1620
1621         /**
1622          * Utility function to determine if a given element has been
1623          * registered as a drag drop handle for the given Drag Drop object.
1624          * @method isHandle
1625          * @param {String} id the element id to check
1626          * @return {boolean} true if this element is a DragDrop handle, false
1627          * otherwise
1628          * @static
1629          */
1630         isHandle: function(sDDId, sHandleId) {
1631             return ( this.handleIds[sDDId] &&
1632                             this.handleIds[sDDId][sHandleId] );
1633         },
1634
1635         /**
1636          * Returns the DragDrop instance for a given id
1637          * @method getDDById
1638          * @param {String} id the id of the DragDrop object
1639          * @return {DragDrop} the drag drop object, null if it is not found
1640          * @static
1641          */
1642         getDDById: function(id) {
1643             for (var i in this.ids) {
1644                 if (this.ids[i][id]) {
1645                     return this.ids[i][id];
1646                 }
1647             }
1648             return null;
1649         },
1650
1651         /**
1652          * Fired after a registered DragDrop object gets the mousedown event.
1653          * Sets up the events required to track the object being dragged
1654          * @method handleMouseDown
1655          * @param {Event} e the event
1656          * @param oDD the DragDrop object being dragged
1657          * @private
1658          * @static
1659          */
1660         handleMouseDown: function(e, oDD) {
1661             if(Roo.QuickTips){
1662                 Roo.QuickTips.disable();
1663             }
1664             this.currentTarget = e.getTarget();
1665
1666             this.dragCurrent = oDD;
1667
1668             var el = oDD.getEl();
1669
1670             // track start position
1671             this.startX = e.getPageX();
1672             this.startY = e.getPageY();
1673
1674             this.deltaX = this.startX - el.offsetLeft;
1675             this.deltaY = this.startY - el.offsetTop;
1676
1677             this.dragThreshMet = false;
1678
1679             this.clickTimeout = setTimeout(
1680                     function() {
1681                         var DDM = Roo.dd.DDM;
1682                         DDM.startDrag(DDM.startX, DDM.startY);
1683                     },
1684                     this.clickTimeThresh );
1685         },
1686
1687         /**
1688          * Fired when either the drag pixel threshol or the mousedown hold
1689          * time threshold has been met.
1690          * @method startDrag
1691          * @param x {int} the X position of the original mousedown
1692          * @param y {int} the Y position of the original mousedown
1693          * @static
1694          */
1695         startDrag: function(x, y) {
1696             clearTimeout(this.clickTimeout);
1697             if (this.dragCurrent) {
1698                 this.dragCurrent.b4StartDrag(x, y);
1699                 this.dragCurrent.startDrag(x, y);
1700             }
1701             this.dragThreshMet = true;
1702         },
1703
1704         /**
1705          * Internal function to handle the mouseup event.  Will be invoked
1706          * from the context of the document.
1707          * @method handleMouseUp
1708          * @param {Event} e the event
1709          * @private
1710          * @static
1711          */
1712         handleMouseUp: function(e) {
1713
1714             if(Roo.QuickTips){
1715                 Roo.QuickTips.enable();
1716             }
1717             if (! this.dragCurrent) {
1718                 return;
1719             }
1720
1721             clearTimeout(this.clickTimeout);
1722
1723             if (this.dragThreshMet) {
1724                 this.fireEvents(e, true);
1725             } else {
1726             }
1727
1728             this.stopDrag(e);
1729
1730             this.stopEvent(e);
1731         },
1732
1733         /**
1734          * Utility to stop event propagation and event default, if these
1735          * features are turned on.
1736          * @method stopEvent
1737          * @param {Event} e the event as returned by this.getEvent()
1738          * @static
1739          */
1740         stopEvent: function(e){
1741             if(this.stopPropagation) {
1742                 e.stopPropagation();
1743             }
1744
1745             if (this.preventDefault) {
1746                 e.preventDefault();
1747             }
1748         },
1749
1750         /**
1751          * Internal function to clean up event handlers after the drag
1752          * operation is complete
1753          * @method stopDrag
1754          * @param {Event} e the event
1755          * @private
1756          * @static
1757          */
1758         stopDrag: function(e) {
1759             // Fire the drag end event for the item that was dragged
1760             if (this.dragCurrent) {
1761                 if (this.dragThreshMet) {
1762                     this.dragCurrent.b4EndDrag(e);
1763                     this.dragCurrent.endDrag(e);
1764                 }
1765
1766                 this.dragCurrent.onMouseUp(e);
1767             }
1768
1769             this.dragCurrent = null;
1770             this.dragOvers = {};
1771         },
1772
1773         /**
1774          * Internal function to handle the mousemove event.  Will be invoked
1775          * from the context of the html element.
1776          *
1777          * @TODO figure out what we can do about mouse events lost when the
1778          * user drags objects beyond the window boundary.  Currently we can
1779          * detect this in internet explorer by verifying that the mouse is
1780          * down during the mousemove event.  Firefox doesn't give us the
1781          * button state on the mousemove event.
1782          * @method handleMouseMove
1783          * @param {Event} e the event
1784          * @private
1785          * @static
1786          */
1787         handleMouseMove: function(e) {
1788             if (! this.dragCurrent) {
1789                 return true;
1790             }
1791
1792             // var button = e.which || e.button;
1793
1794             // check for IE mouseup outside of page boundary
1795             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1796                 this.stopEvent(e);
1797                 return this.handleMouseUp(e);
1798             }
1799
1800             if (!this.dragThreshMet) {
1801                 var diffX = Math.abs(this.startX - e.getPageX());
1802                 var diffY = Math.abs(this.startY - e.getPageY());
1803                 if (diffX > this.clickPixelThresh ||
1804                             diffY > this.clickPixelThresh) {
1805                     this.startDrag(this.startX, this.startY);
1806                 }
1807             }
1808
1809             if (this.dragThreshMet) {
1810                 this.dragCurrent.b4Drag(e);
1811                 this.dragCurrent.onDrag(e);
1812                 if(!this.dragCurrent.moveOnly){
1813                     this.fireEvents(e, false);
1814                 }
1815             }
1816
1817             this.stopEvent(e);
1818
1819             return true;
1820         },
1821
1822         /**
1823          * Iterates over all of the DragDrop elements to find ones we are
1824          * hovering over or dropping on
1825          * @method fireEvents
1826          * @param {Event} e the event
1827          * @param {boolean} isDrop is this a drop op or a mouseover op?
1828          * @private
1829          * @static
1830          */
1831         fireEvents: function(e, isDrop) {
1832             var dc = this.dragCurrent;
1833
1834             // If the user did the mouse up outside of the window, we could
1835             // get here even though we have ended the drag.
1836             if (!dc || dc.isLocked()) {
1837                 return;
1838             }
1839
1840             var pt = e.getPoint();
1841
1842             // cache the previous dragOver array
1843             var oldOvers = [];
1844
1845             var outEvts   = [];
1846             var overEvts  = [];
1847             var dropEvts  = [];
1848             var enterEvts = [];
1849
1850             // Check to see if the object(s) we were hovering over is no longer
1851             // being hovered over so we can fire the onDragOut event
1852             for (var i in this.dragOvers) {
1853
1854                 var ddo = this.dragOvers[i];
1855
1856                 if (! this.isTypeOfDD(ddo)) {
1857                     continue;
1858                 }
1859
1860                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1861                     outEvts.push( ddo );
1862                 }
1863
1864                 oldOvers[i] = true;
1865                 delete this.dragOvers[i];
1866             }
1867
1868             for (var sGroup in dc.groups) {
1869
1870                 if ("string" != typeof sGroup) {
1871                     continue;
1872                 }
1873
1874                 for (i in this.ids[sGroup]) {
1875                     var oDD = this.ids[sGroup][i];
1876                     if (! this.isTypeOfDD(oDD)) {
1877                         continue;
1878                     }
1879
1880                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1881                         if (this.isOverTarget(pt, oDD, this.mode)) {
1882                             // look for drop interactions
1883                             if (isDrop) {
1884                                 dropEvts.push( oDD );
1885                             // look for drag enter and drag over interactions
1886                             } else {
1887
1888                                 // initial drag over: dragEnter fires
1889                                 if (!oldOvers[oDD.id]) {
1890                                     enterEvts.push( oDD );
1891                                 // subsequent drag overs: dragOver fires
1892                                 } else {
1893                                     overEvts.push( oDD );
1894                                 }
1895
1896                                 this.dragOvers[oDD.id] = oDD;
1897                             }
1898                         }
1899                     }
1900                 }
1901             }
1902
1903             if (this.mode) {
1904                 if (outEvts.length) {
1905                     dc.b4DragOut(e, outEvts);
1906                     dc.onDragOut(e, outEvts);
1907                 }
1908
1909                 if (enterEvts.length) {
1910                     dc.onDragEnter(e, enterEvts);
1911                 }
1912
1913                 if (overEvts.length) {
1914                     dc.b4DragOver(e, overEvts);
1915                     dc.onDragOver(e, overEvts);
1916                 }
1917
1918                 if (dropEvts.length) {
1919                     dc.b4DragDrop(e, dropEvts);
1920                     dc.onDragDrop(e, dropEvts);
1921                 }
1922
1923             } else {
1924                 // fire dragout events
1925                 var len = 0;
1926                 for (i=0, len=outEvts.length; i<len; ++i) {
1927                     dc.b4DragOut(e, outEvts[i].id);
1928                     dc.onDragOut(e, outEvts[i].id);
1929                 }
1930
1931                 // fire enter events
1932                 for (i=0,len=enterEvts.length; i<len; ++i) {
1933                     // dc.b4DragEnter(e, oDD.id);
1934                     dc.onDragEnter(e, enterEvts[i].id);
1935                 }
1936
1937                 // fire over events
1938                 for (i=0,len=overEvts.length; i<len; ++i) {
1939                     dc.b4DragOver(e, overEvts[i].id);
1940                     dc.onDragOver(e, overEvts[i].id);
1941                 }
1942
1943                 // fire drop events
1944                 for (i=0, len=dropEvts.length; i<len; ++i) {
1945                     dc.b4DragDrop(e, dropEvts[i].id);
1946                     dc.onDragDrop(e, dropEvts[i].id);
1947                 }
1948
1949             }
1950
1951             // notify about a drop that did not find a target
1952             if (isDrop && !dropEvts.length) {
1953                 dc.onInvalidDrop(e);
1954             }
1955
1956         },
1957
1958         /**
1959          * Helper function for getting the best match from the list of drag
1960          * and drop objects returned by the drag and drop events when we are
1961          * in INTERSECT mode.  It returns either the first object that the
1962          * cursor is over, or the object that has the greatest overlap with
1963          * the dragged element.
1964          * @method getBestMatch
1965          * @param  {DragDrop[]} dds The array of drag and drop objects
1966          * targeted
1967          * @return {DragDrop}       The best single match
1968          * @static
1969          */
1970         getBestMatch: function(dds) {
1971             var winner = null;
1972             // Return null if the input is not what we expect
1973             //if (!dds || !dds.length || dds.length == 0) {
1974                // winner = null;
1975             // If there is only one item, it wins
1976             //} else if (dds.length == 1) {
1977
1978             var len = dds.length;
1979
1980             if (len == 1) {
1981                 winner = dds[0];
1982             } else {
1983                 // Loop through the targeted items
1984                 for (var i=0; i<len; ++i) {
1985                     var dd = dds[i];
1986                     // If the cursor is over the object, it wins.  If the
1987                     // cursor is over multiple matches, the first one we come
1988                     // to wins.
1989                     if (dd.cursorIsOver) {
1990                         winner = dd;
1991                         break;
1992                     // Otherwise the object with the most overlap wins
1993                     } else {
1994                         if (!winner ||
1995                             winner.overlap.getArea() < dd.overlap.getArea()) {
1996                             winner = dd;
1997                         }
1998                     }
1999                 }
2000             }
2001
2002             return winner;
2003         },
2004
2005         /**
2006          * Refreshes the cache of the top-left and bottom-right points of the
2007          * drag and drop objects in the specified group(s).  This is in the
2008          * format that is stored in the drag and drop instance, so typical
2009          * usage is:
2010          * <code>
2011          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2012          * </code>
2013          * Alternatively:
2014          * <code>
2015          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2016          * </code>
2017          * @TODO this really should be an indexed array.  Alternatively this
2018          * method could accept both.
2019          * @method refreshCache
2020          * @param {Object} groups an associative array of groups to refresh
2021          * @static
2022          */
2023         refreshCache: function(groups) {
2024             for (var sGroup in groups) {
2025                 if ("string" != typeof sGroup) {
2026                     continue;
2027                 }
2028                 for (var i in this.ids[sGroup]) {
2029                     var oDD = this.ids[sGroup][i];
2030
2031                     if (this.isTypeOfDD(oDD)) {
2032                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2033                         var loc = this.getLocation(oDD);
2034                         if (loc) {
2035                             this.locationCache[oDD.id] = loc;
2036                         } else {
2037                             delete this.locationCache[oDD.id];
2038                             // this will unregister the drag and drop object if
2039                             // the element is not in a usable state
2040                             // oDD.unreg();
2041                         }
2042                     }
2043                 }
2044             }
2045         },
2046
2047         /**
2048          * This checks to make sure an element exists and is in the DOM.  The
2049          * main purpose is to handle cases where innerHTML is used to remove
2050          * drag and drop objects from the DOM.  IE provides an 'unspecified
2051          * error' when trying to access the offsetParent of such an element
2052          * @method verifyEl
2053          * @param {HTMLElement} el the element to check
2054          * @return {boolean} true if the element looks usable
2055          * @static
2056          */
2057         verifyEl: function(el) {
2058             if (el) {
2059                 var parent;
2060                 if(Roo.isIE){
2061                     try{
2062                         parent = el.offsetParent;
2063                     }catch(e){}
2064                 }else{
2065                     parent = el.offsetParent;
2066                 }
2067                 if (parent) {
2068                     return true;
2069                 }
2070             }
2071
2072             return false;
2073         },
2074
2075         /**
2076          * Returns a Region object containing the drag and drop element's position
2077          * and size, including the padding configured for it
2078          * @method getLocation
2079          * @param {DragDrop} oDD the drag and drop object to get the
2080          *                       location for
2081          * @return {Roo.lib.Region} a Region object representing the total area
2082          *                             the element occupies, including any padding
2083          *                             the instance is configured for.
2084          * @static
2085          */
2086         getLocation: function(oDD) {
2087             if (! this.isTypeOfDD(oDD)) {
2088                 return null;
2089             }
2090
2091             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2092
2093             try {
2094                 pos= Roo.lib.Dom.getXY(el);
2095             } catch (e) { }
2096
2097             if (!pos) {
2098                 return null;
2099             }
2100
2101             x1 = pos[0];
2102             x2 = x1 + el.offsetWidth;
2103             y1 = pos[1];
2104             y2 = y1 + el.offsetHeight;
2105
2106             t = y1 - oDD.padding[0];
2107             r = x2 + oDD.padding[1];
2108             b = y2 + oDD.padding[2];
2109             l = x1 - oDD.padding[3];
2110
2111             return new Roo.lib.Region( t, r, b, l );
2112         },
2113
2114         /**
2115          * Checks the cursor location to see if it over the target
2116          * @method isOverTarget
2117          * @param {Roo.lib.Point} pt The point to evaluate
2118          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2119          * @return {boolean} true if the mouse is over the target
2120          * @private
2121          * @static
2122          */
2123         isOverTarget: function(pt, oTarget, intersect) {
2124             // use cache if available
2125             var loc = this.locationCache[oTarget.id];
2126             if (!loc || !this.useCache) {
2127                 loc = this.getLocation(oTarget);
2128                 this.locationCache[oTarget.id] = loc;
2129
2130             }
2131
2132             if (!loc) {
2133                 return false;
2134             }
2135
2136             oTarget.cursorIsOver = loc.contains( pt );
2137
2138             // DragDrop is using this as a sanity check for the initial mousedown
2139             // in this case we are done.  In POINT mode, if the drag obj has no
2140             // contraints, we are also done. Otherwise we need to evaluate the
2141             // location of the target as related to the actual location of the
2142             // dragged element.
2143             var dc = this.dragCurrent;
2144             if (!dc || !dc.getTargetCoord ||
2145                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2146                 return oTarget.cursorIsOver;
2147             }
2148
2149             oTarget.overlap = null;
2150
2151             // Get the current location of the drag element, this is the
2152             // location of the mouse event less the delta that represents
2153             // where the original mousedown happened on the element.  We
2154             // need to consider constraints and ticks as well.
2155             var pos = dc.getTargetCoord(pt.x, pt.y);
2156
2157             var el = dc.getDragEl();
2158             var curRegion = new Roo.lib.Region( pos.y,
2159                                                    pos.x + el.offsetWidth,
2160                                                    pos.y + el.offsetHeight,
2161                                                    pos.x );
2162
2163             var overlap = curRegion.intersect(loc);
2164
2165             if (overlap) {
2166                 oTarget.overlap = overlap;
2167                 return (intersect) ? true : oTarget.cursorIsOver;
2168             } else {
2169                 return false;
2170             }
2171         },
2172
2173         /**
2174          * unload event handler
2175          * @method _onUnload
2176          * @private
2177          * @static
2178          */
2179         _onUnload: function(e, me) {
2180             Roo.dd.DragDropMgr.unregAll();
2181         },
2182
2183         /**
2184          * Cleans up the drag and drop events and objects.
2185          * @method unregAll
2186          * @private
2187          * @static
2188          */
2189         unregAll: function() {
2190
2191             if (this.dragCurrent) {
2192                 this.stopDrag();
2193                 this.dragCurrent = null;
2194             }
2195
2196             this._execOnAll("unreg", []);
2197
2198             for (i in this.elementCache) {
2199                 delete this.elementCache[i];
2200             }
2201
2202             this.elementCache = {};
2203             this.ids = {};
2204         },
2205
2206         /**
2207          * A cache of DOM elements
2208          * @property elementCache
2209          * @private
2210          * @static
2211          */
2212         elementCache: {},
2213
2214         /**
2215          * Get the wrapper for the DOM element specified
2216          * @method getElWrapper
2217          * @param {String} id the id of the element to get
2218          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2219          * @private
2220          * @deprecated This wrapper isn't that useful
2221          * @static
2222          */
2223         getElWrapper: function(id) {
2224             var oWrapper = this.elementCache[id];
2225             if (!oWrapper || !oWrapper.el) {
2226                 oWrapper = this.elementCache[id] =
2227                     new this.ElementWrapper(Roo.getDom(id));
2228             }
2229             return oWrapper;
2230         },
2231
2232         /**
2233          * Returns the actual DOM element
2234          * @method getElement
2235          * @param {String} id the id of the elment to get
2236          * @return {Object} The element
2237          * @deprecated use Roo.getDom instead
2238          * @static
2239          */
2240         getElement: function(id) {
2241             return Roo.getDom(id);
2242         },
2243
2244         /**
2245          * Returns the style property for the DOM element (i.e.,
2246          * document.getElById(id).style)
2247          * @method getCss
2248          * @param {String} id the id of the elment to get
2249          * @return {Object} The style property of the element
2250          * @deprecated use Roo.getDom instead
2251          * @static
2252          */
2253         getCss: function(id) {
2254             var el = Roo.getDom(id);
2255             return (el) ? el.style : null;
2256         },
2257
2258         /**
2259          * Inner class for cached elements
2260          * @class DragDropMgr.ElementWrapper
2261          * @for DragDropMgr
2262          * @private
2263          * @deprecated
2264          */
2265         ElementWrapper: function(el) {
2266                 /**
2267                  * The element
2268                  * @property el
2269                  */
2270                 this.el = el || null;
2271                 /**
2272                  * The element id
2273                  * @property id
2274                  */
2275                 this.id = this.el && el.id;
2276                 /**
2277                  * A reference to the style property
2278                  * @property css
2279                  */
2280                 this.css = this.el && el.style;
2281             },
2282
2283         /**
2284          * Returns the X position of an html element
2285          * @method getPosX
2286          * @param el the element for which to get the position
2287          * @return {int} the X coordinate
2288          * @for DragDropMgr
2289          * @deprecated use Roo.lib.Dom.getX instead
2290          * @static
2291          */
2292         getPosX: function(el) {
2293             return Roo.lib.Dom.getX(el);
2294         },
2295
2296         /**
2297          * Returns the Y position of an html element
2298          * @method getPosY
2299          * @param el the element for which to get the position
2300          * @return {int} the Y coordinate
2301          * @deprecated use Roo.lib.Dom.getY instead
2302          * @static
2303          */
2304         getPosY: function(el) {
2305             return Roo.lib.Dom.getY(el);
2306         },
2307
2308         /**
2309          * Swap two nodes.  In IE, we use the native method, for others we
2310          * emulate the IE behavior
2311          * @method swapNode
2312          * @param n1 the first node to swap
2313          * @param n2 the other node to swap
2314          * @static
2315          */
2316         swapNode: function(n1, n2) {
2317             if (n1.swapNode) {
2318                 n1.swapNode(n2);
2319             } else {
2320                 var p = n2.parentNode;
2321                 var s = n2.nextSibling;
2322
2323                 if (s == n1) {
2324                     p.insertBefore(n1, n2);
2325                 } else if (n2 == n1.nextSibling) {
2326                     p.insertBefore(n2, n1);
2327                 } else {
2328                     n1.parentNode.replaceChild(n2, n1);
2329                     p.insertBefore(n1, s);
2330                 }
2331             }
2332         },
2333
2334         /**
2335          * Returns the current scroll position
2336          * @method getScroll
2337          * @private
2338          * @static
2339          */
2340         getScroll: function () {
2341             var t, l, dde=document.documentElement, db=document.body;
2342             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2343                 t = dde.scrollTop;
2344                 l = dde.scrollLeft;
2345             } else if (db) {
2346                 t = db.scrollTop;
2347                 l = db.scrollLeft;
2348             } else {
2349
2350             }
2351             return { top: t, left: l };
2352         },
2353
2354         /**
2355          * Returns the specified element style property
2356          * @method getStyle
2357          * @param {HTMLElement} el          the element
2358          * @param {string}      styleProp   the style property
2359          * @return {string} The value of the style property
2360          * @deprecated use Roo.lib.Dom.getStyle
2361          * @static
2362          */
2363         getStyle: function(el, styleProp) {
2364             return Roo.fly(el).getStyle(styleProp);
2365         },
2366
2367         /**
2368          * Gets the scrollTop
2369          * @method getScrollTop
2370          * @return {int} the document's scrollTop
2371          * @static
2372          */
2373         getScrollTop: function () { return this.getScroll().top; },
2374
2375         /**
2376          * Gets the scrollLeft
2377          * @method getScrollLeft
2378          * @return {int} the document's scrollTop
2379          * @static
2380          */
2381         getScrollLeft: function () { return this.getScroll().left; },
2382
2383         /**
2384          * Sets the x/y position of an element to the location of the
2385          * target element.
2386          * @method moveToEl
2387          * @param {HTMLElement} moveEl      The element to move
2388          * @param {HTMLElement} targetEl    The position reference element
2389          * @static
2390          */
2391         moveToEl: function (moveEl, targetEl) {
2392             var aCoord = Roo.lib.Dom.getXY(targetEl);
2393             Roo.lib.Dom.setXY(moveEl, aCoord);
2394         },
2395
2396         /**
2397          * Numeric array sort function
2398          * @method numericSort
2399          * @static
2400          */
2401         numericSort: function(a, b) { return (a - b); },
2402
2403         /**
2404          * Internal counter
2405          * @property _timeoutCount
2406          * @private
2407          * @static
2408          */
2409         _timeoutCount: 0,
2410
2411         /**
2412          * Trying to make the load order less important.  Without this we get
2413          * an error if this file is loaded before the Event Utility.
2414          * @method _addListeners
2415          * @private
2416          * @static
2417          */
2418         _addListeners: function() {
2419             var DDM = Roo.dd.DDM;
2420             if ( Roo.lib.Event && document ) {
2421                 DDM._onLoad();
2422             } else {
2423                 if (DDM._timeoutCount > 2000) {
2424                 } else {
2425                     setTimeout(DDM._addListeners, 10);
2426                     if (document && document.body) {
2427                         DDM._timeoutCount += 1;
2428                     }
2429                 }
2430             }
2431         },
2432
2433         /**
2434          * Recursively searches the immediate parent and all child nodes for
2435          * the handle element in order to determine wheter or not it was
2436          * clicked.
2437          * @method handleWasClicked
2438          * @param node the html element to inspect
2439          * @static
2440          */
2441         handleWasClicked: function(node, id) {
2442             if (this.isHandle(id, node.id)) {
2443                 return true;
2444             } else {
2445                 // check to see if this is a text node child of the one we want
2446                 var p = node.parentNode;
2447
2448                 while (p) {
2449                     if (this.isHandle(id, p.id)) {
2450                         return true;
2451                     } else {
2452                         p = p.parentNode;
2453                     }
2454                 }
2455             }
2456
2457             return false;
2458         }
2459
2460     };
2461
2462 }();
2463
2464 // shorter alias, save a few bytes
2465 Roo.dd.DDM = Roo.dd.DragDropMgr;
2466 Roo.dd.DDM._addListeners();
2467
2468 }/*
2469  * Based on:
2470  * Ext JS Library 1.1.1
2471  * Copyright(c) 2006-2007, Ext JS, LLC.
2472  *
2473  * Originally Released Under LGPL - original licence link has changed is not relivant.
2474  *
2475  * Fork - LGPL
2476  * <script type="text/javascript">
2477  */
2478
2479 /**
2480  * @class Roo.dd.DD
2481  * A DragDrop implementation where the linked element follows the
2482  * mouse cursor during a drag.
2483  * @extends Roo.dd.DragDrop
2484  * @constructor
2485  * @param {String} id the id of the linked element
2486  * @param {String} sGroup the group of related DragDrop items
2487  * @param {object} config an object containing configurable attributes
2488  *                Valid properties for DD:
2489  *                    scroll
2490  */
2491 Roo.dd.DD = function(id, sGroup, config) {
2492     if (id) {
2493         this.init(id, sGroup, config);
2494     }
2495 };
2496
2497 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2498
2499     /**
2500      * When set to true, the utility automatically tries to scroll the browser
2501      * window wehn a drag and drop element is dragged near the viewport boundary.
2502      * Defaults to true.
2503      * @property scroll
2504      * @type boolean
2505      */
2506     scroll: true,
2507
2508     /**
2509      * Sets the pointer offset to the distance between the linked element's top
2510      * left corner and the location the element was clicked
2511      * @method autoOffset
2512      * @param {int} iPageX the X coordinate of the click
2513      * @param {int} iPageY the Y coordinate of the click
2514      */
2515     autoOffset: function(iPageX, iPageY) {
2516         var x = iPageX - this.startPageX;
2517         var y = iPageY - this.startPageY;
2518         this.setDelta(x, y);
2519     },
2520
2521     /**
2522      * Sets the pointer offset.  You can call this directly to force the
2523      * offset to be in a particular location (e.g., pass in 0,0 to set it
2524      * to the center of the object)
2525      * @method setDelta
2526      * @param {int} iDeltaX the distance from the left
2527      * @param {int} iDeltaY the distance from the top
2528      */
2529     setDelta: function(iDeltaX, iDeltaY) {
2530         this.deltaX = iDeltaX;
2531         this.deltaY = iDeltaY;
2532     },
2533
2534     /**
2535      * Sets the drag element to the location of the mousedown or click event,
2536      * maintaining the cursor location relative to the location on the element
2537      * that was clicked.  Override this if you want to place the element in a
2538      * location other than where the cursor is.
2539      * @method setDragElPos
2540      * @param {int} iPageX the X coordinate of the mousedown or drag event
2541      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2542      */
2543     setDragElPos: function(iPageX, iPageY) {
2544         // the first time we do this, we are going to check to make sure
2545         // the element has css positioning
2546
2547         var el = this.getDragEl();
2548         this.alignElWithMouse(el, iPageX, iPageY);
2549     },
2550
2551     /**
2552      * Sets the element to the location of the mousedown or click event,
2553      * maintaining the cursor location relative to the location on the element
2554      * that was clicked.  Override this if you want to place the element in a
2555      * location other than where the cursor is.
2556      * @method alignElWithMouse
2557      * @param {HTMLElement} el the element to move
2558      * @param {int} iPageX the X coordinate of the mousedown or drag event
2559      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2560      */
2561     alignElWithMouse: function(el, iPageX, iPageY) {
2562         var oCoord = this.getTargetCoord(iPageX, iPageY);
2563         var fly = el.dom ? el : Roo.fly(el);
2564         if (!this.deltaSetXY) {
2565             var aCoord = [oCoord.x, oCoord.y];
2566             fly.setXY(aCoord);
2567             var newLeft = fly.getLeft(true);
2568             var newTop  = fly.getTop(true);
2569             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2570         } else {
2571             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2572         }
2573
2574         this.cachePosition(oCoord.x, oCoord.y);
2575         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2576         return oCoord;
2577     },
2578
2579     /**
2580      * Saves the most recent position so that we can reset the constraints and
2581      * tick marks on-demand.  We need to know this so that we can calculate the
2582      * number of pixels the element is offset from its original position.
2583      * @method cachePosition
2584      * @param iPageX the current x position (optional, this just makes it so we
2585      * don't have to look it up again)
2586      * @param iPageY the current y position (optional, this just makes it so we
2587      * don't have to look it up again)
2588      */
2589     cachePosition: function(iPageX, iPageY) {
2590         if (iPageX) {
2591             this.lastPageX = iPageX;
2592             this.lastPageY = iPageY;
2593         } else {
2594             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2595             this.lastPageX = aCoord[0];
2596             this.lastPageY = aCoord[1];
2597         }
2598     },
2599
2600     /**
2601      * Auto-scroll the window if the dragged object has been moved beyond the
2602      * visible window boundary.
2603      * @method autoScroll
2604      * @param {int} x the drag element's x position
2605      * @param {int} y the drag element's y position
2606      * @param {int} h the height of the drag element
2607      * @param {int} w the width of the drag element
2608      * @private
2609      */
2610     autoScroll: function(x, y, h, w) {
2611
2612         if (this.scroll) {
2613             // The client height
2614             var clientH = Roo.lib.Dom.getViewWidth();
2615
2616             // The client width
2617             var clientW = Roo.lib.Dom.getViewHeight();
2618
2619             // The amt scrolled down
2620             var st = this.DDM.getScrollTop();
2621
2622             // The amt scrolled right
2623             var sl = this.DDM.getScrollLeft();
2624
2625             // Location of the bottom of the element
2626             var bot = h + y;
2627
2628             // Location of the right of the element
2629             var right = w + x;
2630
2631             // The distance from the cursor to the bottom of the visible area,
2632             // adjusted so that we don't scroll if the cursor is beyond the
2633             // element drag constraints
2634             var toBot = (clientH + st - y - this.deltaY);
2635
2636             // The distance from the cursor to the right of the visible area
2637             var toRight = (clientW + sl - x - this.deltaX);
2638
2639
2640             // How close to the edge the cursor must be before we scroll
2641             // var thresh = (document.all) ? 100 : 40;
2642             var thresh = 40;
2643
2644             // How many pixels to scroll per autoscroll op.  This helps to reduce
2645             // clunky scrolling. IE is more sensitive about this ... it needs this
2646             // value to be higher.
2647             var scrAmt = (document.all) ? 80 : 30;
2648
2649             // Scroll down if we are near the bottom of the visible page and the
2650             // obj extends below the crease
2651             if ( bot > clientH && toBot < thresh ) {
2652                 window.scrollTo(sl, st + scrAmt);
2653             }
2654
2655             // Scroll up if the window is scrolled down and the top of the object
2656             // goes above the top border
2657             if ( y < st && st > 0 && y - st < thresh ) {
2658                 window.scrollTo(sl, st - scrAmt);
2659             }
2660
2661             // Scroll right if the obj is beyond the right border and the cursor is
2662             // near the border.
2663             if ( right > clientW && toRight < thresh ) {
2664                 window.scrollTo(sl + scrAmt, st);
2665             }
2666
2667             // Scroll left if the window has been scrolled to the right and the obj
2668             // extends past the left border
2669             if ( x < sl && sl > 0 && x - sl < thresh ) {
2670                 window.scrollTo(sl - scrAmt, st);
2671             }
2672         }
2673     },
2674
2675     /**
2676      * Finds the location the element should be placed if we want to move
2677      * it to where the mouse location less the click offset would place us.
2678      * @method getTargetCoord
2679      * @param {int} iPageX the X coordinate of the click
2680      * @param {int} iPageY the Y coordinate of the click
2681      * @return an object that contains the coordinates (Object.x and Object.y)
2682      * @private
2683      */
2684     getTargetCoord: function(iPageX, iPageY) {
2685
2686
2687         var x = iPageX - this.deltaX;
2688         var y = iPageY - this.deltaY;
2689
2690         if (this.constrainX) {
2691             if (x < this.minX) { x = this.minX; }
2692             if (x > this.maxX) { x = this.maxX; }
2693         }
2694
2695         if (this.constrainY) {
2696             if (y < this.minY) { y = this.minY; }
2697             if (y > this.maxY) { y = this.maxY; }
2698         }
2699
2700         x = this.getTick(x, this.xTicks);
2701         y = this.getTick(y, this.yTicks);
2702
2703
2704         return {x:x, y:y};
2705     },
2706
2707     /*
2708      * Sets up config options specific to this class. Overrides
2709      * Roo.dd.DragDrop, but all versions of this method through the
2710      * inheritance chain are called
2711      */
2712     applyConfig: function() {
2713         Roo.dd.DD.superclass.applyConfig.call(this);
2714         this.scroll = (this.config.scroll !== false);
2715     },
2716
2717     /*
2718      * Event that fires prior to the onMouseDown event.  Overrides
2719      * Roo.dd.DragDrop.
2720      */
2721     b4MouseDown: function(e) {
2722         // this.resetConstraints();
2723         this.autoOffset(e.getPageX(),
2724                             e.getPageY());
2725     },
2726
2727     /*
2728      * Event that fires prior to the onDrag event.  Overrides
2729      * Roo.dd.DragDrop.
2730      */
2731     b4Drag: function(e) {
2732         this.setDragElPos(e.getPageX(),
2733                             e.getPageY());
2734     },
2735
2736     toString: function() {
2737         return ("DD " + this.id);
2738     }
2739
2740     //////////////////////////////////////////////////////////////////////////
2741     // Debugging ygDragDrop events that can be overridden
2742     //////////////////////////////////////////////////////////////////////////
2743     /*
2744     startDrag: function(x, y) {
2745     },
2746
2747     onDrag: function(e) {
2748     },
2749
2750     onDragEnter: function(e, id) {
2751     },
2752
2753     onDragOver: function(e, id) {
2754     },
2755
2756     onDragOut: function(e, id) {
2757     },
2758
2759     onDragDrop: function(e, id) {
2760     },
2761
2762     endDrag: function(e) {
2763     }
2764
2765     */
2766
2767 });/*
2768  * Based on:
2769  * Ext JS Library 1.1.1
2770  * Copyright(c) 2006-2007, Ext JS, LLC.
2771  *
2772  * Originally Released Under LGPL - original licence link has changed is not relivant.
2773  *
2774  * Fork - LGPL
2775  * <script type="text/javascript">
2776  */
2777
2778 /**
2779  * @class Roo.dd.DDProxy
2780  * A DragDrop implementation that inserts an empty, bordered div into
2781  * the document that follows the cursor during drag operations.  At the time of
2782  * the click, the frame div is resized to the dimensions of the linked html
2783  * element, and moved to the exact location of the linked element.
2784  *
2785  * References to the "frame" element refer to the single proxy element that
2786  * was created to be dragged in place of all DDProxy elements on the
2787  * page.
2788  *
2789  * @extends Roo.dd.DD
2790  * @constructor
2791  * @param {String} id the id of the linked html element
2792  * @param {String} sGroup the group of related DragDrop objects
2793  * @param {object} config an object containing configurable attributes
2794  *                Valid properties for DDProxy in addition to those in DragDrop:
2795  *                   resizeFrame, centerFrame, dragElId
2796  */
2797 Roo.dd.DDProxy = function(id, sGroup, config) {
2798     if (id) {
2799         this.init(id, sGroup, config);
2800         this.initFrame();
2801     }
2802 };
2803
2804 /**
2805  * The default drag frame div id
2806  * @property Roo.dd.DDProxy.dragElId
2807  * @type String
2808  * @static
2809  */
2810 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2811
2812 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2813
2814     /**
2815      * By default we resize the drag frame to be the same size as the element
2816      * we want to drag (this is to get the frame effect).  We can turn it off
2817      * if we want a different behavior.
2818      * @property resizeFrame
2819      * @type boolean
2820      */
2821     resizeFrame: true,
2822
2823     /**
2824      * By default the frame is positioned exactly where the drag element is, so
2825      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2826      * you do not have constraints on the obj is to have the drag frame centered
2827      * around the cursor.  Set centerFrame to true for this effect.
2828      * @property centerFrame
2829      * @type boolean
2830      */
2831     centerFrame: false,
2832
2833     /**
2834      * Creates the proxy element if it does not yet exist
2835      * @method createFrame
2836      */
2837     createFrame: function() {
2838         var self = this;
2839         var body = document.body;
2840
2841         if (!body || !body.firstChild) {
2842             setTimeout( function() { self.createFrame(); }, 50 );
2843             return;
2844         }
2845
2846         var div = this.getDragEl();
2847
2848         if (!div) {
2849             div    = document.createElement("div");
2850             div.id = this.dragElId;
2851             var s  = div.style;
2852
2853             s.position   = "absolute";
2854             s.visibility = "hidden";
2855             s.cursor     = "move";
2856             s.border     = "2px solid #aaa";
2857             s.zIndex     = 999;
2858
2859             // appendChild can blow up IE if invoked prior to the window load event
2860             // while rendering a table.  It is possible there are other scenarios
2861             // that would cause this to happen as well.
2862             body.insertBefore(div, body.firstChild);
2863         }
2864     },
2865
2866     /**
2867      * Initialization for the drag frame element.  Must be called in the
2868      * constructor of all subclasses
2869      * @method initFrame
2870      */
2871     initFrame: function() {
2872         this.createFrame();
2873     },
2874
2875     applyConfig: function() {
2876         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2877
2878         this.resizeFrame = (this.config.resizeFrame !== false);
2879         this.centerFrame = (this.config.centerFrame);
2880         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2881     },
2882
2883     /**
2884      * Resizes the drag frame to the dimensions of the clicked object, positions
2885      * it over the object, and finally displays it
2886      * @method showFrame
2887      * @param {int} iPageX X click position
2888      * @param {int} iPageY Y click position
2889      * @private
2890      */
2891     showFrame: function(iPageX, iPageY) {
2892         var el = this.getEl();
2893         var dragEl = this.getDragEl();
2894         var s = dragEl.style;
2895
2896         this._resizeProxy();
2897
2898         if (this.centerFrame) {
2899             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2900                            Math.round(parseInt(s.height, 10)/2) );
2901         }
2902
2903         this.setDragElPos(iPageX, iPageY);
2904
2905         Roo.fly(dragEl).show();
2906     },
2907
2908     /**
2909      * The proxy is automatically resized to the dimensions of the linked
2910      * element when a drag is initiated, unless resizeFrame is set to false
2911      * @method _resizeProxy
2912      * @private
2913      */
2914     _resizeProxy: function() {
2915         if (this.resizeFrame) {
2916             var el = this.getEl();
2917             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2918         }
2919     },
2920
2921     // overrides Roo.dd.DragDrop
2922     b4MouseDown: function(e) {
2923         var x = e.getPageX();
2924         var y = e.getPageY();
2925         this.autoOffset(x, y);
2926         this.setDragElPos(x, y);
2927     },
2928
2929     // overrides Roo.dd.DragDrop
2930     b4StartDrag: function(x, y) {
2931         // show the drag frame
2932         this.showFrame(x, y);
2933     },
2934
2935     // overrides Roo.dd.DragDrop
2936     b4EndDrag: function(e) {
2937         Roo.fly(this.getDragEl()).hide();
2938     },
2939
2940     // overrides Roo.dd.DragDrop
2941     // By default we try to move the element to the last location of the frame.
2942     // This is so that the default behavior mirrors that of Roo.dd.DD.
2943     endDrag: function(e) {
2944
2945         var lel = this.getEl();
2946         var del = this.getDragEl();
2947
2948         // Show the drag frame briefly so we can get its position
2949         del.style.visibility = "";
2950
2951         this.beforeMove();
2952         // Hide the linked element before the move to get around a Safari
2953         // rendering bug.
2954         lel.style.visibility = "hidden";
2955         Roo.dd.DDM.moveToEl(lel, del);
2956         del.style.visibility = "hidden";
2957         lel.style.visibility = "";
2958
2959         this.afterDrag();
2960     },
2961
2962     beforeMove : function(){
2963
2964     },
2965
2966     afterDrag : function(){
2967
2968     },
2969
2970     toString: function() {
2971         return ("DDProxy " + this.id);
2972     }
2973
2974 });
2975 /*
2976  * Based on:
2977  * Ext JS Library 1.1.1
2978  * Copyright(c) 2006-2007, Ext JS, LLC.
2979  *
2980  * Originally Released Under LGPL - original licence link has changed is not relivant.
2981  *
2982  * Fork - LGPL
2983  * <script type="text/javascript">
2984  */
2985
2986  /**
2987  * @class Roo.dd.DDTarget
2988  * A DragDrop implementation that does not move, but can be a drop
2989  * target.  You would get the same result by simply omitting implementation
2990  * for the event callbacks, but this way we reduce the processing cost of the
2991  * event listener and the callbacks.
2992  * @extends Roo.dd.DragDrop
2993  * @constructor
2994  * @param {String} id the id of the element that is a drop target
2995  * @param {String} sGroup the group of related DragDrop objects
2996  * @param {object} config an object containing configurable attributes
2997  *                 Valid properties for DDTarget in addition to those in
2998  *                 DragDrop:
2999  *                    none
3000  */
3001 Roo.dd.DDTarget = function(id, sGroup, config) {
3002     if (id) {
3003         this.initTarget(id, sGroup, config);
3004     }
3005     if (config.listeners || config.events) { 
3006        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
3007             listeners : config.listeners || {}, 
3008             events : config.events || {} 
3009         });    
3010     }
3011 };
3012
3013 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3014 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3015     toString: function() {
3016         return ("DDTarget " + this.id);
3017     }
3018 });
3019 /*
3020  * Based on:
3021  * Ext JS Library 1.1.1
3022  * Copyright(c) 2006-2007, Ext JS, LLC.
3023  *
3024  * Originally Released Under LGPL - original licence link has changed is not relivant.
3025  *
3026  * Fork - LGPL
3027  * <script type="text/javascript">
3028  */
3029  
3030
3031 /**
3032  * @class Roo.dd.ScrollManager
3033  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3034  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3035  * @singleton
3036  */
3037 Roo.dd.ScrollManager = function(){
3038     var ddm = Roo.dd.DragDropMgr;
3039     var els = {};
3040     var dragEl = null;
3041     var proc = {};
3042     
3043     var onStop = function(e){
3044         dragEl = null;
3045         clearProc();
3046     };
3047     
3048     var triggerRefresh = function(){
3049         if(ddm.dragCurrent){
3050              ddm.refreshCache(ddm.dragCurrent.groups);
3051         }
3052     };
3053     
3054     var doScroll = function(){
3055         if(ddm.dragCurrent){
3056             var dds = Roo.dd.ScrollManager;
3057             if(!dds.animate){
3058                 if(proc.el.scroll(proc.dir, dds.increment)){
3059                     triggerRefresh();
3060                 }
3061             }else{
3062                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3063             }
3064         }
3065     };
3066     
3067     var clearProc = function(){
3068         if(proc.id){
3069             clearInterval(proc.id);
3070         }
3071         proc.id = 0;
3072         proc.el = null;
3073         proc.dir = "";
3074     };
3075     
3076     var startProc = function(el, dir){
3077         clearProc();
3078         proc.el = el;
3079         proc.dir = dir;
3080         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3081     };
3082     
3083     var onFire = function(e, isDrop){
3084         if(isDrop || !ddm.dragCurrent){ return; }
3085         var dds = Roo.dd.ScrollManager;
3086         if(!dragEl || dragEl != ddm.dragCurrent){
3087             dragEl = ddm.dragCurrent;
3088             // refresh regions on drag start
3089             dds.refreshCache();
3090         }
3091         
3092         var xy = Roo.lib.Event.getXY(e);
3093         var pt = new Roo.lib.Point(xy[0], xy[1]);
3094         for(var id in els){
3095             var el = els[id], r = el._region;
3096             if(r && r.contains(pt) && el.isScrollable()){
3097                 if(r.bottom - pt.y <= dds.thresh){
3098                     if(proc.el != el){
3099                         startProc(el, "down");
3100                     }
3101                     return;
3102                 }else if(r.right - pt.x <= dds.thresh){
3103                     if(proc.el != el){
3104                         startProc(el, "left");
3105                     }
3106                     return;
3107                 }else if(pt.y - r.top <= dds.thresh){
3108                     if(proc.el != el){
3109                         startProc(el, "up");
3110                     }
3111                     return;
3112                 }else if(pt.x - r.left <= dds.thresh){
3113                     if(proc.el != el){
3114                         startProc(el, "right");
3115                     }
3116                     return;
3117                 }
3118             }
3119         }
3120         clearProc();
3121     };
3122     
3123     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3124     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3125     
3126     return {
3127         /**
3128          * Registers new overflow element(s) to auto scroll
3129          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3130          */
3131         register : function(el){
3132             if(el instanceof Array){
3133                 for(var i = 0, len = el.length; i < len; i++) {
3134                         this.register(el[i]);
3135                 }
3136             }else{
3137                 el = Roo.get(el);
3138                 els[el.id] = el;
3139             }
3140         },
3141         
3142         /**
3143          * Unregisters overflow element(s) so they are no longer scrolled
3144          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3145          */
3146         unregister : function(el){
3147             if(el instanceof Array){
3148                 for(var i = 0, len = el.length; i < len; i++) {
3149                         this.unregister(el[i]);
3150                 }
3151             }else{
3152                 el = Roo.get(el);
3153                 delete els[el.id];
3154             }
3155         },
3156         
3157         /**
3158          * The number of pixels from the edge of a container the pointer needs to be to 
3159          * trigger scrolling (defaults to 25)
3160          * @type Number
3161          */
3162         thresh : 25,
3163         
3164         /**
3165          * The number of pixels to scroll in each scroll increment (defaults to 50)
3166          * @type Number
3167          */
3168         increment : 100,
3169         
3170         /**
3171          * The frequency of scrolls in milliseconds (defaults to 500)
3172          * @type Number
3173          */
3174         frequency : 500,
3175         
3176         /**
3177          * True to animate the scroll (defaults to true)
3178          * @type Boolean
3179          */
3180         animate: true,
3181         
3182         /**
3183          * The animation duration in seconds - 
3184          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3185          * @type Number
3186          */
3187         animDuration: .4,
3188         
3189         /**
3190          * Manually trigger a cache refresh.
3191          */
3192         refreshCache : function(){
3193             for(var id in els){
3194                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3195                     els[id]._region = els[id].getRegion();
3196                 }
3197             }
3198         }
3199     };
3200 }();/*
3201  * Based on:
3202  * Ext JS Library 1.1.1
3203  * Copyright(c) 2006-2007, Ext JS, LLC.
3204  *
3205  * Originally Released Under LGPL - original licence link has changed is not relivant.
3206  *
3207  * Fork - LGPL
3208  * <script type="text/javascript">
3209  */
3210  
3211
3212 /**
3213  * @class Roo.dd.Registry
3214  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3215  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3216  * @singleton
3217  */
3218 Roo.dd.Registry = function(){
3219     var elements = {}; 
3220     var handles = {}; 
3221     var autoIdSeed = 0;
3222
3223     var getId = function(el, autogen){
3224         if(typeof el == "string"){
3225             return el;
3226         }
3227         var id = el.id;
3228         if(!id && autogen !== false){
3229             id = "roodd-" + (++autoIdSeed);
3230             el.id = id;
3231         }
3232         return id;
3233     };
3234     
3235     return {
3236     /**
3237      * Register a drag drop element
3238      * @param {String|HTMLElement} element The id or DOM node to register
3239      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3240      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3241      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3242      * populated in the data object (if applicable):
3243      * <pre>
3244 Value      Description<br />
3245 ---------  ------------------------------------------<br />
3246 handles    Array of DOM nodes that trigger dragging<br />
3247            for the element being registered<br />
3248 isHandle   True if the element passed in triggers<br />
3249            dragging itself, else false
3250 </pre>
3251      */
3252         register : function(el, data){
3253             data = data || {};
3254             if(typeof el == "string"){
3255                 el = document.getElementById(el);
3256             }
3257             data.ddel = el;
3258             elements[getId(el)] = data;
3259             if(data.isHandle !== false){
3260                 handles[data.ddel.id] = data;
3261             }
3262             if(data.handles){
3263                 var hs = data.handles;
3264                 for(var i = 0, len = hs.length; i < len; i++){
3265                         handles[getId(hs[i])] = data;
3266                 }
3267             }
3268         },
3269
3270     /**
3271      * Unregister a drag drop element
3272      * @param {String|HTMLElement}  element The id or DOM node to unregister
3273      */
3274         unregister : function(el){
3275             var id = getId(el, false);
3276             var data = elements[id];
3277             if(data){
3278                 delete elements[id];
3279                 if(data.handles){
3280                     var hs = data.handles;
3281                     for(var i = 0, len = hs.length; i < len; i++){
3282                         delete handles[getId(hs[i], false)];
3283                     }
3284                 }
3285             }
3286         },
3287
3288     /**
3289      * Returns the handle registered for a DOM Node by id
3290      * @param {String|HTMLElement} id The DOM node or id to look up
3291      * @return {Object} handle The custom handle data
3292      */
3293         getHandle : function(id){
3294             if(typeof id != "string"){ // must be element?
3295                 id = id.id;
3296             }
3297             return handles[id];
3298         },
3299
3300     /**
3301      * Returns the handle that is registered for the DOM node that is the target of the event
3302      * @param {Event} e The event
3303      * @return {Object} handle The custom handle data
3304      */
3305         getHandleFromEvent : function(e){
3306             var t = Roo.lib.Event.getTarget(e);
3307             return t ? handles[t.id] : null;
3308         },
3309
3310     /**
3311      * Returns a custom data object that is registered for a DOM node by id
3312      * @param {String|HTMLElement} id The DOM node or id to look up
3313      * @return {Object} data The custom data
3314      */
3315         getTarget : function(id){
3316             if(typeof id != "string"){ // must be element?
3317                 id = id.id;
3318             }
3319             return elements[id];
3320         },
3321
3322     /**
3323      * Returns a custom data object that is registered for the DOM node that is the target of the event
3324      * @param {Event} e The event
3325      * @return {Object} data The custom data
3326      */
3327         getTargetFromEvent : function(e){
3328             var t = Roo.lib.Event.getTarget(e);
3329             return t ? elements[t.id] || handles[t.id] : null;
3330         }
3331     };
3332 }();/*
3333  * Based on:
3334  * Ext JS Library 1.1.1
3335  * Copyright(c) 2006-2007, Ext JS, LLC.
3336  *
3337  * Originally Released Under LGPL - original licence link has changed is not relivant.
3338  *
3339  * Fork - LGPL
3340  * <script type="text/javascript">
3341  */
3342  
3343
3344 /**
3345  * @class Roo.dd.StatusProxy
3346  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3347  * default drag proxy used by all Roo.dd components.
3348  * @constructor
3349  * @param {Object} config
3350  */
3351 Roo.dd.StatusProxy = function(config){
3352     Roo.apply(this, config);
3353     this.id = this.id || Roo.id();
3354     this.el = new Roo.Layer({
3355         dh: {
3356             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3357                 {tag: "div", cls: "x-dd-drop-icon"},
3358                 {tag: "div", cls: "x-dd-drag-ghost"}
3359             ]
3360         }, 
3361         shadow: !config || config.shadow !== false
3362     });
3363     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3364     this.dropStatus = this.dropNotAllowed;
3365 };
3366
3367 Roo.dd.StatusProxy.prototype = {
3368     /**
3369      * @cfg {String} dropAllowed
3370      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3371      */
3372     dropAllowed : "x-dd-drop-ok",
3373     /**
3374      * @cfg {String} dropNotAllowed
3375      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3376      */
3377     dropNotAllowed : "x-dd-drop-nodrop",
3378
3379     /**
3380      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3381      * over the current target element.
3382      * @param {String} cssClass The css class for the new drop status indicator image
3383      */
3384     setStatus : function(cssClass){
3385         cssClass = cssClass || this.dropNotAllowed;
3386         if(this.dropStatus != cssClass){
3387             this.el.replaceClass(this.dropStatus, cssClass);
3388             this.dropStatus = cssClass;
3389         }
3390     },
3391
3392     /**
3393      * Resets the status indicator to the default dropNotAllowed value
3394      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3395      */
3396     reset : function(clearGhost){
3397         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3398         this.dropStatus = this.dropNotAllowed;
3399         if(clearGhost){
3400             this.ghost.update("");
3401         }
3402     },
3403
3404     /**
3405      * Updates the contents of the ghost element
3406      * @param {String} html The html that will replace the current innerHTML of the ghost element
3407      */
3408     update : function(html){
3409         if(typeof html == "string"){
3410             this.ghost.update(html);
3411         }else{
3412             this.ghost.update("");
3413             html.style.margin = "0";
3414             this.ghost.dom.appendChild(html);
3415         }
3416         // ensure float = none set?? cant remember why though.
3417         var el = this.ghost.dom.firstChild;
3418                 if(el){
3419                         Roo.fly(el).setStyle('float', 'none');
3420                 }
3421     },
3422     
3423     /**
3424      * Returns the underlying proxy {@link Roo.Layer}
3425      * @return {Roo.Layer} el
3426     */
3427     getEl : function(){
3428         return this.el;
3429     },
3430
3431     /**
3432      * Returns the ghost element
3433      * @return {Roo.Element} el
3434      */
3435     getGhost : function(){
3436         return this.ghost;
3437     },
3438
3439     /**
3440      * Hides the proxy
3441      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3442      */
3443     hide : function(clear){
3444         this.el.hide();
3445         if(clear){
3446             this.reset(true);
3447         }
3448     },
3449
3450     /**
3451      * Stops the repair animation if it's currently running
3452      */
3453     stop : function(){
3454         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3455             this.anim.stop();
3456         }
3457     },
3458
3459     /**
3460      * Displays this proxy
3461      */
3462     show : function(){
3463         this.el.show();
3464     },
3465
3466     /**
3467      * Force the Layer to sync its shadow and shim positions to the element
3468      */
3469     sync : function(){
3470         this.el.sync();
3471     },
3472
3473     /**
3474      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3475      * invalid drop operation by the item being dragged.
3476      * @param {Array} xy The XY position of the element ([x, y])
3477      * @param {Function} callback The function to call after the repair is complete
3478      * @param {Object} scope The scope in which to execute the callback
3479      */
3480     repair : function(xy, callback, scope){
3481         this.callback = callback;
3482         this.scope = scope;
3483         if(xy && this.animRepair !== false){
3484             this.el.addClass("x-dd-drag-repair");
3485             this.el.hideUnders(true);
3486             this.anim = this.el.shift({
3487                 duration: this.repairDuration || .5,
3488                 easing: 'easeOut',
3489                 xy: xy,
3490                 stopFx: true,
3491                 callback: this.afterRepair,
3492                 scope: this
3493             });
3494         }else{
3495             this.afterRepair();
3496         }
3497     },
3498
3499     // private
3500     afterRepair : function(){
3501         this.hide(true);
3502         if(typeof this.callback == "function"){
3503             this.callback.call(this.scope || this);
3504         }
3505         this.callback = null;
3506         this.scope = null;
3507     }
3508 };/*
3509  * Based on:
3510  * Ext JS Library 1.1.1
3511  * Copyright(c) 2006-2007, Ext JS, LLC.
3512  *
3513  * Originally Released Under LGPL - original licence link has changed is not relivant.
3514  *
3515  * Fork - LGPL
3516  * <script type="text/javascript">
3517  */
3518
3519 /**
3520  * @class Roo.dd.DragSource
3521  * @extends Roo.dd.DDProxy
3522  * A simple class that provides the basic implementation needed to make any element draggable.
3523  * @constructor
3524  * @param {String/HTMLElement/Element} el The container element
3525  * @param {Object} config
3526  */
3527 Roo.dd.DragSource = function(el, config){
3528     this.el = Roo.get(el);
3529     this.dragData = {};
3530     
3531     Roo.apply(this, config);
3532     
3533     if(!this.proxy){
3534         this.proxy = new Roo.dd.StatusProxy();
3535     }
3536
3537     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3538           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3539     
3540     this.dragging = false;
3541 };
3542
3543 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3544     /**
3545      * @cfg {String} dropAllowed
3546      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3547      */
3548     dropAllowed : "x-dd-drop-ok",
3549     /**
3550      * @cfg {String} dropNotAllowed
3551      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3552      */
3553     dropNotAllowed : "x-dd-drop-nodrop",
3554
3555     /**
3556      * Returns the data object associated with this drag source
3557      * @return {Object} data An object containing arbitrary data
3558      */
3559     getDragData : function(e){
3560         return this.dragData;
3561     },
3562
3563     // private
3564     onDragEnter : function(e, id){
3565         var target = Roo.dd.DragDropMgr.getDDById(id);
3566         this.cachedTarget = target;
3567         if(this.beforeDragEnter(target, e, id) !== false){
3568             if(target.isNotifyTarget){
3569                 var status = target.notifyEnter(this, e, this.dragData);
3570                 this.proxy.setStatus(status);
3571             }else{
3572                 this.proxy.setStatus(this.dropAllowed);
3573             }
3574             
3575             if(this.afterDragEnter){
3576                 /**
3577                  * An empty function by default, but provided so that you can perform a custom action
3578                  * when the dragged item enters the drop target by providing an implementation.
3579                  * @param {Roo.dd.DragDrop} target The drop target
3580                  * @param {Event} e The event object
3581                  * @param {String} id The id of the dragged element
3582                  * @method afterDragEnter
3583                  */
3584                 this.afterDragEnter(target, e, id);
3585             }
3586         }
3587     },
3588
3589     /**
3590      * An empty function by default, but provided so that you can perform a custom action
3591      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3592      * @param {Roo.dd.DragDrop} target The drop target
3593      * @param {Event} e The event object
3594      * @param {String} id The id of the dragged element
3595      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3596      */
3597     beforeDragEnter : function(target, e, id){
3598         return true;
3599     },
3600
3601     // private
3602     alignElWithMouse: function() {
3603         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3604         this.proxy.sync();
3605     },
3606
3607     // private
3608     onDragOver : function(e, id){
3609         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3610         if(this.beforeDragOver(target, e, id) !== false){
3611             if(target.isNotifyTarget){
3612                 var status = target.notifyOver(this, e, this.dragData);
3613                 this.proxy.setStatus(status);
3614             }
3615
3616             if(this.afterDragOver){
3617                 /**
3618                  * An empty function by default, but provided so that you can perform a custom action
3619                  * while the dragged item is over the drop target by providing an implementation.
3620                  * @param {Roo.dd.DragDrop} target The drop target
3621                  * @param {Event} e The event object
3622                  * @param {String} id The id of the dragged element
3623                  * @method afterDragOver
3624                  */
3625                 this.afterDragOver(target, e, id);
3626             }
3627         }
3628     },
3629
3630     /**
3631      * An empty function by default, but provided so that you can perform a custom action
3632      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3633      * @param {Roo.dd.DragDrop} target The drop target
3634      * @param {Event} e The event object
3635      * @param {String} id The id of the dragged element
3636      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3637      */
3638     beforeDragOver : function(target, e, id){
3639         return true;
3640     },
3641
3642     // private
3643     onDragOut : function(e, id){
3644         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3645         if(this.beforeDragOut(target, e, id) !== false){
3646             if(target.isNotifyTarget){
3647                 target.notifyOut(this, e, this.dragData);
3648             }
3649             this.proxy.reset();
3650             if(this.afterDragOut){
3651                 /**
3652                  * An empty function by default, but provided so that you can perform a custom action
3653                  * after the dragged item is dragged out of the target without dropping.
3654                  * @param {Roo.dd.DragDrop} target The drop target
3655                  * @param {Event} e The event object
3656                  * @param {String} id The id of the dragged element
3657                  * @method afterDragOut
3658                  */
3659                 this.afterDragOut(target, e, id);
3660             }
3661         }
3662         this.cachedTarget = null;
3663     },
3664
3665     /**
3666      * An empty function by default, but provided so that you can perform a custom action before the dragged
3667      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3668      * @param {Roo.dd.DragDrop} target The drop target
3669      * @param {Event} e The event object
3670      * @param {String} id The id of the dragged element
3671      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3672      */
3673     beforeDragOut : function(target, e, id){
3674         return true;
3675     },
3676     
3677     // private
3678     onDragDrop : function(e, id){
3679         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3680         if(this.beforeDragDrop(target, e, id) !== false){
3681             if(target.isNotifyTarget){
3682                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3683                     this.onValidDrop(target, e, id);
3684                 }else{
3685                     this.onInvalidDrop(target, e, id);
3686                 }
3687             }else{
3688                 this.onValidDrop(target, e, id);
3689             }
3690             
3691             if(this.afterDragDrop){
3692                 /**
3693                  * An empty function by default, but provided so that you can perform a custom action
3694                  * after a valid drag drop has occurred by providing an implementation.
3695                  * @param {Roo.dd.DragDrop} target The drop target
3696                  * @param {Event} e The event object
3697                  * @param {String} id The id of the dropped element
3698                  * @method afterDragDrop
3699                  */
3700                 this.afterDragDrop(target, e, id);
3701             }
3702         }
3703         delete this.cachedTarget;
3704     },
3705
3706     /**
3707      * An empty function by default, but provided so that you can perform a custom action before the dragged
3708      * item is dropped onto the target and optionally cancel the onDragDrop.
3709      * @param {Roo.dd.DragDrop} target The drop target
3710      * @param {Event} e The event object
3711      * @param {String} id The id of the dragged element
3712      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3713      */
3714     beforeDragDrop : function(target, e, id){
3715         return true;
3716     },
3717
3718     // private
3719     onValidDrop : function(target, e, id){
3720         this.hideProxy();
3721         if(this.afterValidDrop){
3722             /**
3723              * An empty function by default, but provided so that you can perform a custom action
3724              * after a valid drop has occurred by providing an implementation.
3725              * @param {Object} target The target DD 
3726              * @param {Event} e The event object
3727              * @param {String} id The id of the dropped element
3728              * @method afterInvalidDrop
3729              */
3730             this.afterValidDrop(target, e, id);
3731         }
3732     },
3733
3734     // private
3735     getRepairXY : function(e, data){
3736         return this.el.getXY();  
3737     },
3738
3739     // private
3740     onInvalidDrop : function(target, e, id){
3741         this.beforeInvalidDrop(target, e, id);
3742         if(this.cachedTarget){
3743             if(this.cachedTarget.isNotifyTarget){
3744                 this.cachedTarget.notifyOut(this, e, this.dragData);
3745             }
3746             this.cacheTarget = null;
3747         }
3748         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3749
3750         if(this.afterInvalidDrop){
3751             /**
3752              * An empty function by default, but provided so that you can perform a custom action
3753              * after an invalid drop has occurred by providing an implementation.
3754              * @param {Event} e The event object
3755              * @param {String} id The id of the dropped element
3756              * @method afterInvalidDrop
3757              */
3758             this.afterInvalidDrop(e, id);
3759         }
3760     },
3761
3762     // private
3763     afterRepair : function(){
3764         if(Roo.enableFx){
3765             this.el.highlight(this.hlColor || "c3daf9");
3766         }
3767         this.dragging = false;
3768     },
3769
3770     /**
3771      * An empty function by default, but provided so that you can perform a custom action after an invalid
3772      * drop has occurred.
3773      * @param {Roo.dd.DragDrop} target The drop target
3774      * @param {Event} e The event object
3775      * @param {String} id The id of the dragged element
3776      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3777      */
3778     beforeInvalidDrop : function(target, e, id){
3779         return true;
3780     },
3781
3782     // private
3783     handleMouseDown : function(e){
3784         if(this.dragging) {
3785             return;
3786         }
3787         var data = this.getDragData(e);
3788         if(data && this.onBeforeDrag(data, e) !== false){
3789             this.dragData = data;
3790             this.proxy.stop();
3791             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3792         } 
3793     },
3794
3795     /**
3796      * An empty function by default, but provided so that you can perform a custom action before the initial
3797      * drag event begins and optionally cancel it.
3798      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3799      * @param {Event} e The event object
3800      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3801      */
3802     onBeforeDrag : function(data, e){
3803         return true;
3804     },
3805
3806     /**
3807      * An empty function by default, but provided so that you can perform a custom action once the initial
3808      * drag event has begun.  The drag cannot be canceled from this function.
3809      * @param {Number} x The x position of the click on the dragged object
3810      * @param {Number} y The y position of the click on the dragged object
3811      */
3812     onStartDrag : Roo.emptyFn,
3813
3814     // private - YUI override
3815     startDrag : function(x, y){
3816         this.proxy.reset();
3817         this.dragging = true;
3818         this.proxy.update("");
3819         this.onInitDrag(x, y);
3820         this.proxy.show();
3821     },
3822
3823     // private
3824     onInitDrag : function(x, y){
3825         var clone = this.el.dom.cloneNode(true);
3826         clone.id = Roo.id(); // prevent duplicate ids
3827         this.proxy.update(clone);
3828         this.onStartDrag(x, y);
3829         return true;
3830     },
3831
3832     /**
3833      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3834      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3835      */
3836     getProxy : function(){
3837         return this.proxy;  
3838     },
3839
3840     /**
3841      * Hides the drag source's {@link Roo.dd.StatusProxy}
3842      */
3843     hideProxy : function(){
3844         this.proxy.hide();  
3845         this.proxy.reset(true);
3846         this.dragging = false;
3847     },
3848
3849     // private
3850     triggerCacheRefresh : function(){
3851         Roo.dd.DDM.refreshCache(this.groups);
3852     },
3853
3854     // private - override to prevent hiding
3855     b4EndDrag: function(e) {
3856     },
3857
3858     // private - override to prevent moving
3859     endDrag : function(e){
3860         this.onEndDrag(this.dragData, e);
3861     },
3862
3863     // private
3864     onEndDrag : function(data, e){
3865     },
3866     
3867     // private - pin to cursor
3868     autoOffset : function(x, y) {
3869         this.setDelta(-12, -20);
3870     }    
3871 });/*
3872  * Based on:
3873  * Ext JS Library 1.1.1
3874  * Copyright(c) 2006-2007, Ext JS, LLC.
3875  *
3876  * Originally Released Under LGPL - original licence link has changed is not relivant.
3877  *
3878  * Fork - LGPL
3879  * <script type="text/javascript">
3880  */
3881
3882
3883 /**
3884  * @class Roo.dd.DropTarget
3885  * @extends Roo.dd.DDTarget
3886  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3887  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3888  * @constructor
3889  * @param {String/HTMLElement/Element} el The container element
3890  * @param {Object} config
3891  */
3892 Roo.dd.DropTarget = function(el, config){
3893     this.el = Roo.get(el);
3894     
3895     var listeners = false; ;
3896     if (config && config.listeners) {
3897         listeners= config.listeners;
3898         delete config.listeners;
3899     }
3900     Roo.apply(this, config);
3901     
3902     if(this.containerScroll){
3903         Roo.dd.ScrollManager.register(this.el);
3904     }
3905     this.addEvents( {
3906          /**
3907          * @scope Roo.dd.DropTarget
3908          */
3909          
3910          /**
3911          * @event enter
3912          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3913          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3914          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3915          * 
3916          * IMPORTANT : it should set this.overClass and this.dropAllowed
3917          * 
3918          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3919          * @param {Event} e The event
3920          * @param {Object} data An object containing arbitrary data supplied by the drag source
3921          */
3922         "enter" : true,
3923         
3924          /**
3925          * @event over
3926          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3927          * This method will be called on every mouse movement while the drag source is over the drop target.
3928          * This default implementation simply returns the dropAllowed config value.
3929          * 
3930          * IMPORTANT : it should set this.dropAllowed
3931          * 
3932          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3933          * @param {Event} e The event
3934          * @param {Object} data An object containing arbitrary data supplied by the drag source
3935          
3936          */
3937         "over" : true,
3938         /**
3939          * @event out
3940          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3941          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3942          * overClass (if any) from the drop element.
3943          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3944          * @param {Event} e The event
3945          * @param {Object} data An object containing arbitrary data supplied by the drag source
3946          */
3947          "out" : true,
3948          
3949         /**
3950          * @event drop
3951          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3952          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3953          * implementation that does something to process the drop event and returns true so that the drag source's
3954          * repair action does not run.
3955          * 
3956          * IMPORTANT : it should set this.success
3957          * 
3958          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3959          * @param {Event} e The event
3960          * @param {Object} data An object containing arbitrary data supplied by the drag source
3961         */
3962          "drop" : true
3963     });
3964             
3965      
3966     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3967         this.el.dom, 
3968         this.ddGroup || this.group,
3969         {
3970             isTarget: true,
3971             listeners : listeners || {} 
3972            
3973         
3974         }
3975     );
3976
3977 };
3978
3979 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
3980     /**
3981      * @cfg {String} overClass
3982      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
3983      */
3984      /**
3985      * @cfg {String} ddGroup
3986      * The drag drop group to handle drop events for
3987      */
3988      
3989     /**
3990      * @cfg {String} dropAllowed
3991      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3992      */
3993     dropAllowed : "x-dd-drop-ok",
3994     /**
3995      * @cfg {String} dropNotAllowed
3996      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3997      */
3998     dropNotAllowed : "x-dd-drop-nodrop",
3999     /**
4000      * @cfg {boolean} success
4001      * set this after drop listener.. 
4002      */
4003     success : false,
4004     /**
4005      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
4006      * if the drop point is valid for over/enter..
4007      */
4008     valid : false,
4009     // private
4010     isTarget : true,
4011
4012     // private
4013     isNotifyTarget : true,
4014     
4015     /**
4016      * @hide
4017      */
4018     notifyEnter : function(dd, e, data)
4019     {
4020         this.valid = true;
4021         this.fireEvent('enter', dd, e, data);
4022         if(this.overClass){
4023             this.el.addClass(this.overClass);
4024         }
4025         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4026             this.valid ? this.dropAllowed : this.dropNotAllowed
4027         );
4028     },
4029
4030     /**
4031      * @hide
4032      */
4033     notifyOver : function(dd, e, data)
4034     {
4035         this.valid = true;
4036         this.fireEvent('over', dd, e, data);
4037         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4038             this.valid ? this.dropAllowed : this.dropNotAllowed
4039         );
4040     },
4041
4042     /**
4043      * @hide
4044      */
4045     notifyOut : function(dd, e, data)
4046     {
4047         this.fireEvent('out', dd, e, data);
4048         if(this.overClass){
4049             this.el.removeClass(this.overClass);
4050         }
4051     },
4052
4053     /**
4054      * @hide
4055      */
4056     notifyDrop : function(dd, e, data)
4057     {
4058         this.success = false;
4059         this.fireEvent('drop', dd, e, data);
4060         return this.success;
4061     }
4062 });/*
4063  * Based on:
4064  * Ext JS Library 1.1.1
4065  * Copyright(c) 2006-2007, Ext JS, LLC.
4066  *
4067  * Originally Released Under LGPL - original licence link has changed is not relivant.
4068  *
4069  * Fork - LGPL
4070  * <script type="text/javascript">
4071  */
4072
4073
4074 /**
4075  * @class Roo.dd.DragZone
4076  * @extends Roo.dd.DragSource
4077  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4078  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4079  * @constructor
4080  * @param {String/HTMLElement/Element} el The container element
4081  * @param {Object} config
4082  */
4083 Roo.dd.DragZone = function(el, config){
4084     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4085     if(this.containerScroll){
4086         Roo.dd.ScrollManager.register(this.el);
4087     }
4088 };
4089
4090 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4091     /**
4092      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4093      * for auto scrolling during drag operations.
4094      */
4095     /**
4096      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4097      * method after a failed drop (defaults to "c3daf9" - light blue)
4098      */
4099
4100     /**
4101      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4102      * for a valid target to drag based on the mouse down. Override this method
4103      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4104      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4105      * @param {EventObject} e The mouse down event
4106      * @return {Object} The dragData
4107      */
4108     getDragData : function(e){
4109         return Roo.dd.Registry.getHandleFromEvent(e);
4110     },
4111     
4112     /**
4113      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4114      * this.dragData.ddel
4115      * @param {Number} x The x position of the click on the dragged object
4116      * @param {Number} y The y position of the click on the dragged object
4117      * @return {Boolean} true to continue the drag, false to cancel
4118      */
4119     onInitDrag : function(x, y){
4120         this.proxy.update(this.dragData.ddel.cloneNode(true));
4121         this.onStartDrag(x, y);
4122         return true;
4123     },
4124     
4125     /**
4126      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4127      */
4128     afterRepair : function(){
4129         if(Roo.enableFx){
4130             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4131         }
4132         this.dragging = false;
4133     },
4134
4135     /**
4136      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4137      * the XY of this.dragData.ddel
4138      * @param {EventObject} e The mouse up event
4139      * @return {Array} The xy location (e.g. [100, 200])
4140      */
4141     getRepairXY : function(e){
4142         return Roo.Element.fly(this.dragData.ddel).getXY();  
4143     }
4144 });/*
4145  * Based on:
4146  * Ext JS Library 1.1.1
4147  * Copyright(c) 2006-2007, Ext JS, LLC.
4148  *
4149  * Originally Released Under LGPL - original licence link has changed is not relivant.
4150  *
4151  * Fork - LGPL
4152  * <script type="text/javascript">
4153  */
4154 /**
4155  * @class Roo.dd.DropZone
4156  * @extends Roo.dd.DropTarget
4157  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4158  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4159  * @constructor
4160  * @param {String/HTMLElement/Element} el The container element
4161  * @param {Object} config
4162  */
4163 Roo.dd.DropZone = function(el, config){
4164     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4165 };
4166
4167 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4168     /**
4169      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4170      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4171      * provide your own custom lookup.
4172      * @param {Event} e The event
4173      * @return {Object} data The custom data
4174      */
4175     getTargetFromEvent : function(e){
4176         return Roo.dd.Registry.getTargetFromEvent(e);
4177     },
4178
4179     /**
4180      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4181      * that it has registered.  This method has no default implementation and should be overridden to provide
4182      * node-specific processing if necessary.
4183      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4184      * {@link #getTargetFromEvent} for this node)
4185      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4186      * @param {Event} e The event
4187      * @param {Object} data An object containing arbitrary data supplied by the drag source
4188      */
4189     onNodeEnter : function(n, dd, e, data){
4190         
4191     },
4192
4193     /**
4194      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4195      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4196      * overridden to provide the proper feedback.
4197      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4198      * {@link #getTargetFromEvent} for this node)
4199      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4200      * @param {Event} e The event
4201      * @param {Object} data An object containing arbitrary data supplied by the drag source
4202      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4203      * underlying {@link Roo.dd.StatusProxy} can be updated
4204      */
4205     onNodeOver : function(n, dd, e, data){
4206         return this.dropAllowed;
4207     },
4208
4209     /**
4210      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4211      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4212      * node-specific processing if necessary.
4213      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4214      * {@link #getTargetFromEvent} for this node)
4215      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4216      * @param {Event} e The event
4217      * @param {Object} data An object containing arbitrary data supplied by the drag source
4218      */
4219     onNodeOut : function(n, dd, e, data){
4220         
4221     },
4222
4223     /**
4224      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4225      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4226      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4227      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4228      * {@link #getTargetFromEvent} for this node)
4229      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4230      * @param {Event} e The event
4231      * @param {Object} data An object containing arbitrary data supplied by the drag source
4232      * @return {Boolean} True if the drop was valid, else false
4233      */
4234     onNodeDrop : function(n, dd, e, data){
4235         return false;
4236     },
4237
4238     /**
4239      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4240      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4241      * it should be overridden to provide the proper feedback if necessary.
4242      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4243      * @param {Event} e The event
4244      * @param {Object} data An object containing arbitrary data supplied by the drag source
4245      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4246      * underlying {@link Roo.dd.StatusProxy} can be updated
4247      */
4248     onContainerOver : function(dd, e, data){
4249         return this.dropNotAllowed;
4250     },
4251
4252     /**
4253      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4254      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4255      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4256      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4257      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4258      * @param {Event} e The event
4259      * @param {Object} data An object containing arbitrary data supplied by the drag source
4260      * @return {Boolean} True if the drop was valid, else false
4261      */
4262     onContainerDrop : function(dd, e, data){
4263         return false;
4264     },
4265
4266     /**
4267      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4268      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4269      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4270      * you should override this method and provide a custom implementation.
4271      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4272      * @param {Event} e The event
4273      * @param {Object} data An object containing arbitrary data supplied by the drag source
4274      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4275      * underlying {@link Roo.dd.StatusProxy} can be updated
4276      */
4277     notifyEnter : function(dd, e, data){
4278         return this.dropNotAllowed;
4279     },
4280
4281     /**
4282      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4283      * This method will be called on every mouse movement while the drag source is over the drop zone.
4284      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4285      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4286      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4287      * registered node, it will call {@link #onContainerOver}.
4288      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4289      * @param {Event} e The event
4290      * @param {Object} data An object containing arbitrary data supplied by the drag source
4291      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4292      * underlying {@link Roo.dd.StatusProxy} can be updated
4293      */
4294     notifyOver : function(dd, e, data){
4295         var n = this.getTargetFromEvent(e);
4296         if(!n){ // not over valid drop target
4297             if(this.lastOverNode){
4298                 this.onNodeOut(this.lastOverNode, dd, e, data);
4299                 this.lastOverNode = null;
4300             }
4301             return this.onContainerOver(dd, e, data);
4302         }
4303         if(this.lastOverNode != n){
4304             if(this.lastOverNode){
4305                 this.onNodeOut(this.lastOverNode, dd, e, data);
4306             }
4307             this.onNodeEnter(n, dd, e, data);
4308             this.lastOverNode = n;
4309         }
4310         return this.onNodeOver(n, dd, e, data);
4311     },
4312
4313     /**
4314      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4315      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4316      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4317      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4318      * @param {Event} e The event
4319      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4320      */
4321     notifyOut : function(dd, e, data){
4322         if(this.lastOverNode){
4323             this.onNodeOut(this.lastOverNode, dd, e, data);
4324             this.lastOverNode = null;
4325         }
4326     },
4327
4328     /**
4329      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4330      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4331      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4332      * otherwise it will call {@link #onContainerDrop}.
4333      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4334      * @param {Event} e The event
4335      * @param {Object} data An object containing arbitrary data supplied by the drag source
4336      * @return {Boolean} True if the drop was valid, else false
4337      */
4338     notifyDrop : function(dd, e, data){
4339         if(this.lastOverNode){
4340             this.onNodeOut(this.lastOverNode, dd, e, data);
4341             this.lastOverNode = null;
4342         }
4343         var n = this.getTargetFromEvent(e);
4344         return n ?
4345             this.onNodeDrop(n, dd, e, data) :
4346             this.onContainerDrop(dd, e, data);
4347     },
4348
4349     // private
4350     triggerCacheRefresh : function(){
4351         Roo.dd.DDM.refreshCache(this.groups);
4352     }  
4353 });/*
4354  * Based on:
4355  * Ext JS Library 1.1.1
4356  * Copyright(c) 2006-2007, Ext JS, LLC.
4357  *
4358  * Originally Released Under LGPL - original licence link has changed is not relivant.
4359  *
4360  * Fork - LGPL
4361  * <script type="text/javascript">
4362  */
4363
4364
4365 /**
4366  * @class Roo.data.SortTypes
4367  * @singleton
4368  * Defines the default sorting (casting?) comparison functions used when sorting data.
4369  */
4370 Roo.data.SortTypes = {
4371     /**
4372      * Default sort that does nothing
4373      * @param {Mixed} s The value being converted
4374      * @return {Mixed} The comparison value
4375      */
4376     none : function(s){
4377         return s;
4378     },
4379     
4380     /**
4381      * The regular expression used to strip tags
4382      * @type {RegExp}
4383      * @property
4384      */
4385     stripTagsRE : /<\/?[^>]+>/gi,
4386     
4387     /**
4388      * Strips all HTML tags to sort on text only
4389      * @param {Mixed} s The value being converted
4390      * @return {String} The comparison value
4391      */
4392     asText : function(s){
4393         return String(s).replace(this.stripTagsRE, "");
4394     },
4395     
4396     /**
4397      * Strips all HTML tags to sort on text only - Case insensitive
4398      * @param {Mixed} s The value being converted
4399      * @return {String} The comparison value
4400      */
4401     asUCText : function(s){
4402         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4403     },
4404     
4405     /**
4406      * Case insensitive string
4407      * @param {Mixed} s The value being converted
4408      * @return {String} The comparison value
4409      */
4410     asUCString : function(s) {
4411         return String(s).toUpperCase();
4412     },
4413     
4414     /**
4415      * Date sorting
4416      * @param {Mixed} s The value being converted
4417      * @return {Number} The comparison value
4418      */
4419     asDate : function(s) {
4420         if(!s){
4421             return 0;
4422         }
4423         if(s instanceof Date){
4424             return s.getTime();
4425         }
4426         return Date.parse(String(s));
4427     },
4428     
4429     /**
4430      * Float sorting
4431      * @param {Mixed} s The value being converted
4432      * @return {Float} The comparison value
4433      */
4434     asFloat : function(s) {
4435         var val = parseFloat(String(s).replace(/,/g, ""));
4436         if(isNaN(val)) val = 0;
4437         return val;
4438     },
4439     
4440     /**
4441      * Integer sorting
4442      * @param {Mixed} s The value being converted
4443      * @return {Number} The comparison value
4444      */
4445     asInt : function(s) {
4446         var val = parseInt(String(s).replace(/,/g, ""));
4447         if(isNaN(val)) val = 0;
4448         return val;
4449     }
4450 };/*
4451  * Based on:
4452  * Ext JS Library 1.1.1
4453  * Copyright(c) 2006-2007, Ext JS, LLC.
4454  *
4455  * Originally Released Under LGPL - original licence link has changed is not relivant.
4456  *
4457  * Fork - LGPL
4458  * <script type="text/javascript">
4459  */
4460
4461 /**
4462 * @class Roo.data.Record
4463  * Instances of this class encapsulate both record <em>definition</em> information, and record
4464  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4465  * to access Records cached in an {@link Roo.data.Store} object.<br>
4466  * <p>
4467  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4468  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4469  * objects.<br>
4470  * <p>
4471  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4472  * @constructor
4473  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4474  * {@link #create}. The parameters are the same.
4475  * @param {Array} data An associative Array of data values keyed by the field name.
4476  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4477  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4478  * not specified an integer id is generated.
4479  */
4480 Roo.data.Record = function(data, id){
4481     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4482     this.data = data;
4483 };
4484
4485 /**
4486  * Generate a constructor for a specific record layout.
4487  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4488  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4489  * Each field definition object may contain the following properties: <ul>
4490  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
4491  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4492  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4493  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4494  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4495  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4496  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4497  * this may be omitted.</p></li>
4498  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4499  * <ul><li>auto (Default, implies no conversion)</li>
4500  * <li>string</li>
4501  * <li>int</li>
4502  * <li>float</li>
4503  * <li>boolean</li>
4504  * <li>date</li></ul></p></li>
4505  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4506  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4507  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4508  * by the Reader into an object that will be stored in the Record. It is passed the
4509  * following parameters:<ul>
4510  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4511  * </ul></p></li>
4512  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4513  * </ul>
4514  * <br>usage:<br><pre><code>
4515 var TopicRecord = Roo.data.Record.create(
4516     {name: 'title', mapping: 'topic_title'},
4517     {name: 'author', mapping: 'username'},
4518     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4519     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4520     {name: 'lastPoster', mapping: 'user2'},
4521     {name: 'excerpt', mapping: 'post_text'}
4522 );
4523
4524 var myNewRecord = new TopicRecord({
4525     title: 'Do my job please',
4526     author: 'noobie',
4527     totalPosts: 1,
4528     lastPost: new Date(),
4529     lastPoster: 'Animal',
4530     excerpt: 'No way dude!'
4531 });
4532 myStore.add(myNewRecord);
4533 </code></pre>
4534  * @method create
4535  * @static
4536  */
4537 Roo.data.Record.create = function(o){
4538     var f = function(){
4539         f.superclass.constructor.apply(this, arguments);
4540     };
4541     Roo.extend(f, Roo.data.Record);
4542     var p = f.prototype;
4543     p.fields = new Roo.util.MixedCollection(false, function(field){
4544         return field.name;
4545     });
4546     for(var i = 0, len = o.length; i < len; i++){
4547         p.fields.add(new Roo.data.Field(o[i]));
4548     }
4549     f.getField = function(name){
4550         return p.fields.get(name);  
4551     };
4552     return f;
4553 };
4554
4555 Roo.data.Record.AUTO_ID = 1000;
4556 Roo.data.Record.EDIT = 'edit';
4557 Roo.data.Record.REJECT = 'reject';
4558 Roo.data.Record.COMMIT = 'commit';
4559
4560 Roo.data.Record.prototype = {
4561     /**
4562      * Readonly flag - true if this record has been modified.
4563      * @type Boolean
4564      */
4565     dirty : false,
4566     editing : false,
4567     error: null,
4568     modified: null,
4569
4570     // private
4571     join : function(store){
4572         this.store = store;
4573     },
4574
4575     /**
4576      * Set the named field to the specified value.
4577      * @param {String} name The name of the field to set.
4578      * @param {Object} value The value to set the field to.
4579      */
4580     set : function(name, value){
4581         if(this.data[name] == value){
4582             return;
4583         }
4584         this.dirty = true;
4585         if(!this.modified){
4586             this.modified = {};
4587         }
4588         if(typeof this.modified[name] == 'undefined'){
4589             this.modified[name] = this.data[name];
4590         }
4591         this.data[name] = value;
4592         if(!this.editing && this.store){
4593             this.store.afterEdit(this);
4594         }       
4595     },
4596
4597     /**
4598      * Get the value of the named field.
4599      * @param {String} name The name of the field to get the value of.
4600      * @return {Object} The value of the field.
4601      */
4602     get : function(name){
4603         return this.data[name]; 
4604     },
4605
4606     // private
4607     beginEdit : function(){
4608         this.editing = true;
4609         this.modified = {}; 
4610     },
4611
4612     // private
4613     cancelEdit : function(){
4614         this.editing = false;
4615         delete this.modified;
4616     },
4617
4618     // private
4619     endEdit : function(){
4620         this.editing = false;
4621         if(this.dirty && this.store){
4622             this.store.afterEdit(this);
4623         }
4624     },
4625
4626     /**
4627      * Usually called by the {@link Roo.data.Store} which owns the Record.
4628      * Rejects all changes made to the Record since either creation, or the last commit operation.
4629      * Modified fields are reverted to their original values.
4630      * <p>
4631      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4632      * of reject operations.
4633      */
4634     reject : function(){
4635         var m = this.modified;
4636         for(var n in m){
4637             if(typeof m[n] != "function"){
4638                 this.data[n] = m[n];
4639             }
4640         }
4641         this.dirty = false;
4642         delete this.modified;
4643         this.editing = false;
4644         if(this.store){
4645             this.store.afterReject(this);
4646         }
4647     },
4648
4649     /**
4650      * Usually called by the {@link Roo.data.Store} which owns the Record.
4651      * Commits all changes made to the Record since either creation, or the last commit operation.
4652      * <p>
4653      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4654      * of commit operations.
4655      */
4656     commit : function(){
4657         this.dirty = false;
4658         delete this.modified;
4659         this.editing = false;
4660         if(this.store){
4661             this.store.afterCommit(this);
4662         }
4663     },
4664
4665     // private
4666     hasError : function(){
4667         return this.error != null;
4668     },
4669
4670     // private
4671     clearError : function(){
4672         this.error = null;
4673     },
4674
4675     /**
4676      * Creates a copy of this record.
4677      * @param {String} id (optional) A new record id if you don't want to use this record's id
4678      * @return {Record}
4679      */
4680     copy : function(newId) {
4681         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4682     }
4683 };/*
4684  * Based on:
4685  * Ext JS Library 1.1.1
4686  * Copyright(c) 2006-2007, Ext JS, LLC.
4687  *
4688  * Originally Released Under LGPL - original licence link has changed is not relivant.
4689  *
4690  * Fork - LGPL
4691  * <script type="text/javascript">
4692  */
4693
4694
4695
4696 /**
4697  * @class Roo.data.Store
4698  * @extends Roo.util.Observable
4699  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4700  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4701  * <p>
4702  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
4703  * has no knowledge of the format of the data returned by the Proxy.<br>
4704  * <p>
4705  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4706  * instances from the data object. These records are cached and made available through accessor functions.
4707  * @constructor
4708  * Creates a new Store.
4709  * @param {Object} config A config object containing the objects needed for the Store to access data,
4710  * and read the data into Records.
4711  */
4712 Roo.data.Store = function(config){
4713     this.data = new Roo.util.MixedCollection(false);
4714     this.data.getKey = function(o){
4715         return o.id;
4716     };
4717     this.baseParams = {};
4718     // private
4719     this.paramNames = {
4720         "start" : "start",
4721         "limit" : "limit",
4722         "sort" : "sort",
4723         "dir" : "dir",
4724         "multisort" : "_multisort"
4725     };
4726
4727     if(config && config.data){
4728         this.inlineData = config.data;
4729         delete config.data;
4730     }
4731
4732     Roo.apply(this, config);
4733     
4734     if(this.reader){ // reader passed
4735         this.reader = Roo.factory(this.reader, Roo.data);
4736         this.reader.xmodule = this.xmodule || false;
4737         if(!this.recordType){
4738             this.recordType = this.reader.recordType;
4739         }
4740         if(this.reader.onMetaChange){
4741             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4742         }
4743     }
4744
4745     if(this.recordType){
4746         this.fields = this.recordType.prototype.fields;
4747     }
4748     this.modified = [];
4749
4750     this.addEvents({
4751         /**
4752          * @event datachanged
4753          * Fires when the data cache has changed, and a widget which is using this Store
4754          * as a Record cache should refresh its view.
4755          * @param {Store} this
4756          */
4757         datachanged : true,
4758         /**
4759          * @event metachange
4760          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4761          * @param {Store} this
4762          * @param {Object} meta The JSON metadata
4763          */
4764         metachange : true,
4765         /**
4766          * @event add
4767          * Fires when Records have been added to the Store
4768          * @param {Store} this
4769          * @param {Roo.data.Record[]} records The array of Records added
4770          * @param {Number} index The index at which the record(s) were added
4771          */
4772         add : true,
4773         /**
4774          * @event remove
4775          * Fires when a Record has been removed from the Store
4776          * @param {Store} this
4777          * @param {Roo.data.Record} record The Record that was removed
4778          * @param {Number} index The index at which the record was removed
4779          */
4780         remove : true,
4781         /**
4782          * @event update
4783          * Fires when a Record has been updated
4784          * @param {Store} this
4785          * @param {Roo.data.Record} record The Record that was updated
4786          * @param {String} operation The update operation being performed.  Value may be one of:
4787          * <pre><code>
4788  Roo.data.Record.EDIT
4789  Roo.data.Record.REJECT
4790  Roo.data.Record.COMMIT
4791          * </code></pre>
4792          */
4793         update : true,
4794         /**
4795          * @event clear
4796          * Fires when the data cache has been cleared.
4797          * @param {Store} this
4798          */
4799         clear : true,
4800         /**
4801          * @event beforeload
4802          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4803          * the load action will be canceled.
4804          * @param {Store} this
4805          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4806          */
4807         beforeload : true,
4808         /**
4809          * @event load
4810          * Fires after a new set of Records has been loaded.
4811          * @param {Store} this
4812          * @param {Roo.data.Record[]} records The Records that were loaded
4813          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4814          */
4815         load : true,
4816         /**
4817          * @event loadexception
4818          * Fires if an exception occurs in the Proxy during loading.
4819          * Called with the signature of the Proxy's "loadexception" event.
4820          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4821          * 
4822          * @param {Proxy} 
4823          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4824          * @param {Object} load options 
4825          * @param {Object} jsonData from your request (normally this contains the Exception)
4826          */
4827         loadexception : true
4828     });
4829     
4830     if(this.proxy){
4831         this.proxy = Roo.factory(this.proxy, Roo.data);
4832         this.proxy.xmodule = this.xmodule || false;
4833         this.relayEvents(this.proxy,  ["loadexception"]);
4834     }
4835     this.sortToggle = {};
4836     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
4837
4838     Roo.data.Store.superclass.constructor.call(this);
4839
4840     if(this.inlineData){
4841         this.loadData(this.inlineData);
4842         delete this.inlineData;
4843     }
4844 };
4845 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4846      /**
4847     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4848     * without a remote query - used by combo/forms at present.
4849     */
4850     
4851     /**
4852     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4853     */
4854     /**
4855     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4856     */
4857     /**
4858     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4859     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4860     */
4861     /**
4862     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4863     * on any HTTP request
4864     */
4865     /**
4866     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4867     */
4868     /**
4869     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
4870     */
4871     multiSort: false,
4872     /**
4873     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4874     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4875     */
4876     remoteSort : false,
4877
4878     /**
4879     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4880      * loaded or when a record is removed. (defaults to false).
4881     */
4882     pruneModifiedRecords : false,
4883
4884     // private
4885     lastOptions : null,
4886
4887     /**
4888      * Add Records to the Store and fires the add event.
4889      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4890      */
4891     add : function(records){
4892         records = [].concat(records);
4893         for(var i = 0, len = records.length; i < len; i++){
4894             records[i].join(this);
4895         }
4896         var index = this.data.length;
4897         this.data.addAll(records);
4898         this.fireEvent("add", this, records, index);
4899     },
4900
4901     /**
4902      * Remove a Record from the Store and fires the remove event.
4903      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4904      */
4905     remove : function(record){
4906         var index = this.data.indexOf(record);
4907         this.data.removeAt(index);
4908         if(this.pruneModifiedRecords){
4909             this.modified.remove(record);
4910         }
4911         this.fireEvent("remove", this, record, index);
4912     },
4913
4914     /**
4915      * Remove all Records from the Store and fires the clear event.
4916      */
4917     removeAll : function(){
4918         this.data.clear();
4919         if(this.pruneModifiedRecords){
4920             this.modified = [];
4921         }
4922         this.fireEvent("clear", this);
4923     },
4924
4925     /**
4926      * Inserts Records to the Store at the given index and fires the add event.
4927      * @param {Number} index The start index at which to insert the passed Records.
4928      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4929      */
4930     insert : function(index, records){
4931         records = [].concat(records);
4932         for(var i = 0, len = records.length; i < len; i++){
4933             this.data.insert(index, records[i]);
4934             records[i].join(this);
4935         }
4936         this.fireEvent("add", this, records, index);
4937     },
4938
4939     /**
4940      * Get the index within the cache of the passed Record.
4941      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4942      * @return {Number} The index of the passed Record. Returns -1 if not found.
4943      */
4944     indexOf : function(record){
4945         return this.data.indexOf(record);
4946     },
4947
4948     /**
4949      * Get the index within the cache of the Record with the passed id.
4950      * @param {String} id The id of the Record to find.
4951      * @return {Number} The index of the Record. Returns -1 if not found.
4952      */
4953     indexOfId : function(id){
4954         return this.data.indexOfKey(id);
4955     },
4956
4957     /**
4958      * Get the Record with the specified id.
4959      * @param {String} id The id of the Record to find.
4960      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4961      */
4962     getById : function(id){
4963         return this.data.key(id);
4964     },
4965
4966     /**
4967      * Get the Record at the specified index.
4968      * @param {Number} index The index of the Record to find.
4969      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4970      */
4971     getAt : function(index){
4972         return this.data.itemAt(index);
4973     },
4974
4975     /**
4976      * Returns a range of Records between specified indices.
4977      * @param {Number} startIndex (optional) The starting index (defaults to 0)
4978      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
4979      * @return {Roo.data.Record[]} An array of Records
4980      */
4981     getRange : function(start, end){
4982         return this.data.getRange(start, end);
4983     },
4984
4985     // private
4986     storeOptions : function(o){
4987         o = Roo.apply({}, o);
4988         delete o.callback;
4989         delete o.scope;
4990         this.lastOptions = o;
4991     },
4992
4993     /**
4994      * Loads the Record cache from the configured Proxy using the configured Reader.
4995      * <p>
4996      * If using remote paging, then the first load call must specify the <em>start</em>
4997      * and <em>limit</em> properties in the options.params property to establish the initial
4998      * position within the dataset, and the number of Records to cache on each read from the Proxy.
4999      * <p>
5000      * <strong>It is important to note that for remote data sources, loading is asynchronous,
5001      * and this call will return before the new data has been loaded. Perform any post-processing
5002      * in a callback function, or in a "load" event handler.</strong>
5003      * <p>
5004      * @param {Object} options An object containing properties which control loading options:<ul>
5005      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5006      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5007      * passed the following arguments:<ul>
5008      * <li>r : Roo.data.Record[]</li>
5009      * <li>options: Options object from the load call</li>
5010      * <li>success: Boolean success indicator</li></ul></li>
5011      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5012      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5013      * </ul>
5014      */
5015     load : function(options){
5016         options = options || {};
5017         if(this.fireEvent("beforeload", this, options) !== false){
5018             this.storeOptions(options);
5019             var p = Roo.apply(options.params || {}, this.baseParams);
5020             // if meta was not loaded from remote source.. try requesting it.
5021             if (!this.reader.metaFromRemote) {
5022                 p._requestMeta = 1;
5023             }
5024             if(this.sortInfo && this.remoteSort){
5025                 var pn = this.paramNames;
5026                 p[pn["sort"]] = this.sortInfo.field;
5027                 p[pn["dir"]] = this.sortInfo.direction;
5028             }
5029             if (this.multiSort) {
5030                 var pn = this.paramNames;
5031                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5032             }
5033             
5034             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5035         }
5036     },
5037
5038     /**
5039      * Reloads the Record cache from the configured Proxy using the configured Reader and
5040      * the options from the last load operation performed.
5041      * @param {Object} options (optional) An object containing properties which may override the options
5042      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5043      * the most recently used options are reused).
5044      */
5045     reload : function(options){
5046         this.load(Roo.applyIf(options||{}, this.lastOptions));
5047     },
5048
5049     // private
5050     // Called as a callback by the Reader during a load operation.
5051     loadRecords : function(o, options, success){
5052         if(!o || success === false){
5053             if(success !== false){
5054                 this.fireEvent("load", this, [], options);
5055             }
5056             if(options.callback){
5057                 options.callback.call(options.scope || this, [], options, false);
5058             }
5059             return;
5060         }
5061         // if data returned failure - throw an exception.
5062         if (o.success === false) {
5063             // show a message if no listener is registered.
5064             if (!this.hasListener('loadexception') && typeof(this.reader.jsonData.errorMsg) != 'undefined') {
5065                     Roo.MessageBox.alert("Error loading",this.reader.jsonData.errorMsg);
5066             }
5067             // loadmask wil be hooked into this..
5068             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
5069             return;
5070         }
5071         var r = o.records, t = o.totalRecords || r.length;
5072         if(!options || options.add !== true){
5073             if(this.pruneModifiedRecords){
5074                 this.modified = [];
5075             }
5076             for(var i = 0, len = r.length; i < len; i++){
5077                 r[i].join(this);
5078             }
5079             if(this.snapshot){
5080                 this.data = this.snapshot;
5081                 delete this.snapshot;
5082             }
5083             this.data.clear();
5084             this.data.addAll(r);
5085             this.totalLength = t;
5086             this.applySort();
5087             this.fireEvent("datachanged", this);
5088         }else{
5089             this.totalLength = Math.max(t, this.data.length+r.length);
5090             this.add(r);
5091         }
5092         this.fireEvent("load", this, r, options);
5093         if(options.callback){
5094             options.callback.call(options.scope || this, r, options, true);
5095         }
5096     },
5097
5098
5099     /**
5100      * Loads data from a passed data block. A Reader which understands the format of the data
5101      * must have been configured in the constructor.
5102      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5103      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5104      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5105      */
5106     loadData : function(o, append){
5107         var r = this.reader.readRecords(o);
5108         this.loadRecords(r, {add: append}, true);
5109     },
5110
5111     /**
5112      * Gets the number of cached records.
5113      * <p>
5114      * <em>If using paging, this may not be the total size of the dataset. If the data object
5115      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5116      * the data set size</em>
5117      */
5118     getCount : function(){
5119         return this.data.length || 0;
5120     },
5121
5122     /**
5123      * Gets the total number of records in the dataset as returned by the server.
5124      * <p>
5125      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5126      * the dataset size</em>
5127      */
5128     getTotalCount : function(){
5129         return this.totalLength || 0;
5130     },
5131
5132     /**
5133      * Returns the sort state of the Store as an object with two properties:
5134      * <pre><code>
5135  field {String} The name of the field by which the Records are sorted
5136  direction {String} The sort order, "ASC" or "DESC"
5137      * </code></pre>
5138      */
5139     getSortState : function(){
5140         return this.sortInfo;
5141     },
5142
5143     // private
5144     applySort : function(){
5145         if(this.sortInfo && !this.remoteSort){
5146             var s = this.sortInfo, f = s.field;
5147             var st = this.fields.get(f).sortType;
5148             var fn = function(r1, r2){
5149                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5150                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5151             };
5152             this.data.sort(s.direction, fn);
5153             if(this.snapshot && this.snapshot != this.data){
5154                 this.snapshot.sort(s.direction, fn);
5155             }
5156         }
5157     },
5158
5159     /**
5160      * Sets the default sort column and order to be used by the next load operation.
5161      * @param {String} fieldName The name of the field to sort by.
5162      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5163      */
5164     setDefaultSort : function(field, dir){
5165         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5166     },
5167
5168     /**
5169      * Sort the Records.
5170      * If remote sorting is used, the sort is performed on the server, and the cache is
5171      * reloaded. If local sorting is used, the cache is sorted internally.
5172      * @param {String} fieldName The name of the field to sort by.
5173      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5174      */
5175     sort : function(fieldName, dir){
5176         var f = this.fields.get(fieldName);
5177         if(!dir){
5178             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5179             
5180             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5181                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5182             }else{
5183                 dir = f.sortDir;
5184             }
5185         }
5186         this.sortToggle[f.name] = dir;
5187         this.sortInfo = {field: f.name, direction: dir};
5188         if(!this.remoteSort){
5189             this.applySort();
5190             this.fireEvent("datachanged", this);
5191         }else{
5192             this.load(this.lastOptions);
5193         }
5194     },
5195
5196     /**
5197      * Calls the specified function for each of the Records in the cache.
5198      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5199      * Returning <em>false</em> aborts and exits the iteration.
5200      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5201      */
5202     each : function(fn, scope){
5203         this.data.each(fn, scope);
5204     },
5205
5206     /**
5207      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5208      * (e.g., during paging).
5209      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5210      */
5211     getModifiedRecords : function(){
5212         return this.modified;
5213     },
5214
5215     // private
5216     createFilterFn : function(property, value, anyMatch){
5217         if(!value.exec){ // not a regex
5218             value = String(value);
5219             if(value.length == 0){
5220                 return false;
5221             }
5222             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5223         }
5224         return function(r){
5225             return value.test(r.data[property]);
5226         };
5227     },
5228
5229     /**
5230      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5231      * @param {String} property A field on your records
5232      * @param {Number} start The record index to start at (defaults to 0)
5233      * @param {Number} end The last record index to include (defaults to length - 1)
5234      * @return {Number} The sum
5235      */
5236     sum : function(property, start, end){
5237         var rs = this.data.items, v = 0;
5238         start = start || 0;
5239         end = (end || end === 0) ? end : rs.length-1;
5240
5241         for(var i = start; i <= end; i++){
5242             v += (rs[i].data[property] || 0);
5243         }
5244         return v;
5245     },
5246
5247     /**
5248      * Filter the records by a specified property.
5249      * @param {String} field A field on your records
5250      * @param {String/RegExp} value Either a string that the field
5251      * should start with or a RegExp to test against the field
5252      * @param {Boolean} anyMatch True to match any part not just the beginning
5253      */
5254     filter : function(property, value, anyMatch){
5255         var fn = this.createFilterFn(property, value, anyMatch);
5256         return fn ? this.filterBy(fn) : this.clearFilter();
5257     },
5258
5259     /**
5260      * Filter by a function. The specified function will be called with each
5261      * record in this data source. If the function returns true the record is included,
5262      * otherwise it is filtered.
5263      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5264      * @param {Object} scope (optional) The scope of the function (defaults to this)
5265      */
5266     filterBy : function(fn, scope){
5267         this.snapshot = this.snapshot || this.data;
5268         this.data = this.queryBy(fn, scope||this);
5269         this.fireEvent("datachanged", this);
5270     },
5271
5272     /**
5273      * Query the records by a specified property.
5274      * @param {String} field A field on your records
5275      * @param {String/RegExp} value Either a string that the field
5276      * should start with or a RegExp to test against the field
5277      * @param {Boolean} anyMatch True to match any part not just the beginning
5278      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5279      */
5280     query : function(property, value, anyMatch){
5281         var fn = this.createFilterFn(property, value, anyMatch);
5282         return fn ? this.queryBy(fn) : this.data.clone();
5283     },
5284
5285     /**
5286      * Query by a function. The specified function will be called with each
5287      * record in this data source. If the function returns true the record is included
5288      * in the results.
5289      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5290      * @param {Object} scope (optional) The scope of the function (defaults to this)
5291       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5292      **/
5293     queryBy : function(fn, scope){
5294         var data = this.snapshot || this.data;
5295         return data.filterBy(fn, scope||this);
5296     },
5297
5298     /**
5299      * Collects unique values for a particular dataIndex from this store.
5300      * @param {String} dataIndex The property to collect
5301      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5302      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5303      * @return {Array} An array of the unique values
5304      **/
5305     collect : function(dataIndex, allowNull, bypassFilter){
5306         var d = (bypassFilter === true && this.snapshot) ?
5307                 this.snapshot.items : this.data.items;
5308         var v, sv, r = [], l = {};
5309         for(var i = 0, len = d.length; i < len; i++){
5310             v = d[i].data[dataIndex];
5311             sv = String(v);
5312             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5313                 l[sv] = true;
5314                 r[r.length] = v;
5315             }
5316         }
5317         return r;
5318     },
5319
5320     /**
5321      * Revert to a view of the Record cache with no filtering applied.
5322      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5323      */
5324     clearFilter : function(suppressEvent){
5325         if(this.snapshot && this.snapshot != this.data){
5326             this.data = this.snapshot;
5327             delete this.snapshot;
5328             if(suppressEvent !== true){
5329                 this.fireEvent("datachanged", this);
5330             }
5331         }
5332     },
5333
5334     // private
5335     afterEdit : function(record){
5336         if(this.modified.indexOf(record) == -1){
5337             this.modified.push(record);
5338         }
5339         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5340     },
5341     
5342     // private
5343     afterReject : function(record){
5344         this.modified.remove(record);
5345         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5346     },
5347
5348     // private
5349     afterCommit : function(record){
5350         this.modified.remove(record);
5351         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5352     },
5353
5354     /**
5355      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5356      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5357      */
5358     commitChanges : function(){
5359         var m = this.modified.slice(0);
5360         this.modified = [];
5361         for(var i = 0, len = m.length; i < len; i++){
5362             m[i].commit();
5363         }
5364     },
5365
5366     /**
5367      * Cancel outstanding changes on all changed records.
5368      */
5369     rejectChanges : function(){
5370         var m = this.modified.slice(0);
5371         this.modified = [];
5372         for(var i = 0, len = m.length; i < len; i++){
5373             m[i].reject();
5374         }
5375     },
5376
5377     onMetaChange : function(meta, rtype, o){
5378         this.recordType = rtype;
5379         this.fields = rtype.prototype.fields;
5380         delete this.snapshot;
5381         this.sortInfo = meta.sortInfo || this.sortInfo;
5382         this.modified = [];
5383         this.fireEvent('metachange', this, this.reader.meta);
5384     }
5385 });/*
5386  * Based on:
5387  * Ext JS Library 1.1.1
5388  * Copyright(c) 2006-2007, Ext JS, LLC.
5389  *
5390  * Originally Released Under LGPL - original licence link has changed is not relivant.
5391  *
5392  * Fork - LGPL
5393  * <script type="text/javascript">
5394  */
5395
5396 /**
5397  * @class Roo.data.SimpleStore
5398  * @extends Roo.data.Store
5399  * Small helper class to make creating Stores from Array data easier.
5400  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5401  * @cfg {Array} fields An array of field definition objects, or field name strings.
5402  * @cfg {Array} data The multi-dimensional array of data
5403  * @constructor
5404  * @param {Object} config
5405  */
5406 Roo.data.SimpleStore = function(config){
5407     Roo.data.SimpleStore.superclass.constructor.call(this, {
5408         isLocal : true,
5409         reader: new Roo.data.ArrayReader({
5410                 id: config.id
5411             },
5412             Roo.data.Record.create(config.fields)
5413         ),
5414         proxy : new Roo.data.MemoryProxy(config.data)
5415     });
5416     this.load();
5417 };
5418 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5419  * Based on:
5420  * Ext JS Library 1.1.1
5421  * Copyright(c) 2006-2007, Ext JS, LLC.
5422  *
5423  * Originally Released Under LGPL - original licence link has changed is not relivant.
5424  *
5425  * Fork - LGPL
5426  * <script type="text/javascript">
5427  */
5428
5429 /**
5430 /**
5431  * @extends Roo.data.Store
5432  * @class Roo.data.JsonStore
5433  * Small helper class to make creating Stores for JSON data easier. <br/>
5434 <pre><code>
5435 var store = new Roo.data.JsonStore({
5436     url: 'get-images.php',
5437     root: 'images',
5438     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5439 });
5440 </code></pre>
5441  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5442  * JsonReader and HttpProxy (unless inline data is provided).</b>
5443  * @cfg {Array} fields An array of field definition objects, or field name strings.
5444  * @constructor
5445  * @param {Object} config
5446  */
5447 Roo.data.JsonStore = function(c){
5448     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5449         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5450         reader: new Roo.data.JsonReader(c, c.fields)
5451     }));
5452 };
5453 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5454  * Based on:
5455  * Ext JS Library 1.1.1
5456  * Copyright(c) 2006-2007, Ext JS, LLC.
5457  *
5458  * Originally Released Under LGPL - original licence link has changed is not relivant.
5459  *
5460  * Fork - LGPL
5461  * <script type="text/javascript">
5462  */
5463
5464  
5465 Roo.data.Field = function(config){
5466     if(typeof config == "string"){
5467         config = {name: config};
5468     }
5469     Roo.apply(this, config);
5470     
5471     if(!this.type){
5472         this.type = "auto";
5473     }
5474     
5475     var st = Roo.data.SortTypes;
5476     // named sortTypes are supported, here we look them up
5477     if(typeof this.sortType == "string"){
5478         this.sortType = st[this.sortType];
5479     }
5480     
5481     // set default sortType for strings and dates
5482     if(!this.sortType){
5483         switch(this.type){
5484             case "string":
5485                 this.sortType = st.asUCString;
5486                 break;
5487             case "date":
5488                 this.sortType = st.asDate;
5489                 break;
5490             default:
5491                 this.sortType = st.none;
5492         }
5493     }
5494
5495     // define once
5496     var stripRe = /[\$,%]/g;
5497
5498     // prebuilt conversion function for this field, instead of
5499     // switching every time we're reading a value
5500     if(!this.convert){
5501         var cv, dateFormat = this.dateFormat;
5502         switch(this.type){
5503             case "":
5504             case "auto":
5505             case undefined:
5506                 cv = function(v){ return v; };
5507                 break;
5508             case "string":
5509                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5510                 break;
5511             case "int":
5512                 cv = function(v){
5513                     return v !== undefined && v !== null && v !== '' ?
5514                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5515                     };
5516                 break;
5517             case "float":
5518                 cv = function(v){
5519                     return v !== undefined && v !== null && v !== '' ?
5520                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5521                     };
5522                 break;
5523             case "bool":
5524             case "boolean":
5525                 cv = function(v){ return v === true || v === "true" || v == 1; };
5526                 break;
5527             case "date":
5528                 cv = function(v){
5529                     if(!v){
5530                         return '';
5531                     }
5532                     if(v instanceof Date){
5533                         return v;
5534                     }
5535                     if(dateFormat){
5536                         if(dateFormat == "timestamp"){
5537                             return new Date(v*1000);
5538                         }
5539                         return Date.parseDate(v, dateFormat);
5540                     }
5541                     var parsed = Date.parse(v);
5542                     return parsed ? new Date(parsed) : null;
5543                 };
5544              break;
5545             
5546         }
5547         this.convert = cv;
5548     }
5549 };
5550
5551 Roo.data.Field.prototype = {
5552     dateFormat: null,
5553     defaultValue: "",
5554     mapping: null,
5555     sortType : null,
5556     sortDir : "ASC"
5557 };/*
5558  * Based on:
5559  * Ext JS Library 1.1.1
5560  * Copyright(c) 2006-2007, Ext JS, LLC.
5561  *
5562  * Originally Released Under LGPL - original licence link has changed is not relivant.
5563  *
5564  * Fork - LGPL
5565  * <script type="text/javascript">
5566  */
5567  
5568 // Base class for reading structured data from a data source.  This class is intended to be
5569 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5570
5571 /**
5572  * @class Roo.data.DataReader
5573  * Base class for reading structured data from a data source.  This class is intended to be
5574  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5575  */
5576
5577 Roo.data.DataReader = function(meta, recordType){
5578     
5579     this.meta = meta;
5580     
5581     this.recordType = recordType instanceof Array ? 
5582         Roo.data.Record.create(recordType) : recordType;
5583 };
5584
5585 Roo.data.DataReader.prototype = {
5586      /**
5587      * Create an empty record
5588      * @param {Object} data (optional) - overlay some values
5589      * @return {Roo.data.Record} record created.
5590      */
5591     newRow :  function(d) {
5592         var da =  {};
5593         this.recordType.prototype.fields.each(function(c) {
5594             switch( c.type) {
5595                 case 'int' : da[c.name] = 0; break;
5596                 case 'date' : da[c.name] = new Date(); break;
5597                 case 'float' : da[c.name] = 0.0; break;
5598                 case 'boolean' : da[c.name] = false; break;
5599                 default : da[c.name] = ""; break;
5600             }
5601             
5602         });
5603         return new this.recordType(Roo.apply(da, d));
5604     }
5605     
5606 };/*
5607  * Based on:
5608  * Ext JS Library 1.1.1
5609  * Copyright(c) 2006-2007, Ext JS, LLC.
5610  *
5611  * Originally Released Under LGPL - original licence link has changed is not relivant.
5612  *
5613  * Fork - LGPL
5614  * <script type="text/javascript">
5615  */
5616
5617 /**
5618  * @class Roo.data.DataProxy
5619  * @extends Roo.data.Observable
5620  * This class is an abstract base class for implementations which provide retrieval of
5621  * unformatted data objects.<br>
5622  * <p>
5623  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5624  * (of the appropriate type which knows how to parse the data object) to provide a block of
5625  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5626  * <p>
5627  * Custom implementations must implement the load method as described in
5628  * {@link Roo.data.HttpProxy#load}.
5629  */
5630 Roo.data.DataProxy = function(){
5631     this.addEvents({
5632         /**
5633          * @event beforeload
5634          * Fires before a network request is made to retrieve a data object.
5635          * @param {Object} This DataProxy object.
5636          * @param {Object} params The params parameter to the load function.
5637          */
5638         beforeload : true,
5639         /**
5640          * @event load
5641          * Fires before the load method's callback is called.
5642          * @param {Object} This DataProxy object.
5643          * @param {Object} o The data object.
5644          * @param {Object} arg The callback argument object passed to the load function.
5645          */
5646         load : true,
5647         /**
5648          * @event loadexception
5649          * Fires if an Exception occurs during data retrieval.
5650          * @param {Object} This DataProxy object.
5651          * @param {Object} o The data object.
5652          * @param {Object} arg The callback argument object passed to the load function.
5653          * @param {Object} e The Exception.
5654          */
5655         loadexception : true
5656     });
5657     Roo.data.DataProxy.superclass.constructor.call(this);
5658 };
5659
5660 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5661
5662     /**
5663      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5664      */
5665 /*
5666  * Based on:
5667  * Ext JS Library 1.1.1
5668  * Copyright(c) 2006-2007, Ext JS, LLC.
5669  *
5670  * Originally Released Under LGPL - original licence link has changed is not relivant.
5671  *
5672  * Fork - LGPL
5673  * <script type="text/javascript">
5674  */
5675 /**
5676  * @class Roo.data.MemoryProxy
5677  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5678  * to the Reader when its load method is called.
5679  * @constructor
5680  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5681  */
5682 Roo.data.MemoryProxy = function(data){
5683     if (data.data) {
5684         data = data.data;
5685     }
5686     Roo.data.MemoryProxy.superclass.constructor.call(this);
5687     this.data = data;
5688 };
5689
5690 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5691     /**
5692      * Load data from the requested source (in this case an in-memory
5693      * data object passed to the constructor), read the data object into
5694      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5695      * process that block using the passed callback.
5696      * @param {Object} params This parameter is not used by the MemoryProxy class.
5697      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5698      * object into a block of Roo.data.Records.
5699      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5700      * The function must be passed <ul>
5701      * <li>The Record block object</li>
5702      * <li>The "arg" argument from the load function</li>
5703      * <li>A boolean success indicator</li>
5704      * </ul>
5705      * @param {Object} scope The scope in which to call the callback
5706      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5707      */
5708     load : function(params, reader, callback, scope, arg){
5709         params = params || {};
5710         var result;
5711         try {
5712             result = reader.readRecords(this.data);
5713         }catch(e){
5714             this.fireEvent("loadexception", this, arg, null, e);
5715             callback.call(scope, null, arg, false);
5716             return;
5717         }
5718         callback.call(scope, result, arg, true);
5719     },
5720     
5721     // private
5722     update : function(params, records){
5723         
5724     }
5725 });/*
5726  * Based on:
5727  * Ext JS Library 1.1.1
5728  * Copyright(c) 2006-2007, Ext JS, LLC.
5729  *
5730  * Originally Released Under LGPL - original licence link has changed is not relivant.
5731  *
5732  * Fork - LGPL
5733  * <script type="text/javascript">
5734  */
5735 /**
5736  * @class Roo.data.HttpProxy
5737  * @extends Roo.data.DataProxy
5738  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5739  * configured to reference a certain URL.<br><br>
5740  * <p>
5741  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5742  * from which the running page was served.<br><br>
5743  * <p>
5744  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5745  * <p>
5746  * Be aware that to enable the browser to parse an XML document, the server must set
5747  * the Content-Type header in the HTTP response to "text/xml".
5748  * @constructor
5749  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5750  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5751  * will be used to make the request.
5752  */
5753 Roo.data.HttpProxy = function(conn){
5754     Roo.data.HttpProxy.superclass.constructor.call(this);
5755     // is conn a conn config or a real conn?
5756     this.conn = conn;
5757     this.useAjax = !conn || !conn.events;
5758   
5759 };
5760
5761 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5762     // thse are take from connection...
5763     
5764     /**
5765      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5766      */
5767     /**
5768      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5769      * extra parameters to each request made by this object. (defaults to undefined)
5770      */
5771     /**
5772      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5773      *  to each request made by this object. (defaults to undefined)
5774      */
5775     /**
5776      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
5777      */
5778     /**
5779      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5780      */
5781      /**
5782      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5783      * @type Boolean
5784      */
5785   
5786
5787     /**
5788      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5789      * @type Boolean
5790      */
5791     /**
5792      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5793      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5794      * a finer-grained basis than the DataProxy events.
5795      */
5796     getConnection : function(){
5797         return this.useAjax ? Roo.Ajax : this.conn;
5798     },
5799
5800     /**
5801      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5802      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5803      * process that block using the passed callback.
5804      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5805      * for the request to the remote server.
5806      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5807      * object into a block of Roo.data.Records.
5808      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5809      * The function must be passed <ul>
5810      * <li>The Record block object</li>
5811      * <li>The "arg" argument from the load function</li>
5812      * <li>A boolean success indicator</li>
5813      * </ul>
5814      * @param {Object} scope The scope in which to call the callback
5815      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5816      */
5817     load : function(params, reader, callback, scope, arg){
5818         if(this.fireEvent("beforeload", this, params) !== false){
5819             var  o = {
5820                 params : params || {},
5821                 request: {
5822                     callback : callback,
5823                     scope : scope,
5824                     arg : arg
5825                 },
5826                 reader: reader,
5827                 callback : this.loadResponse,
5828                 scope: this
5829             };
5830             if(this.useAjax){
5831                 Roo.applyIf(o, this.conn);
5832                 if(this.activeRequest){
5833                     Roo.Ajax.abort(this.activeRequest);
5834                 }
5835                 this.activeRequest = Roo.Ajax.request(o);
5836             }else{
5837                 this.conn.request(o);
5838             }
5839         }else{
5840             callback.call(scope||this, null, arg, false);
5841         }
5842     },
5843
5844     // private
5845     loadResponse : function(o, success, response){
5846         delete this.activeRequest;
5847         if(!success){
5848             this.fireEvent("loadexception", this, o, response);
5849             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5850             return;
5851         }
5852         var result;
5853         try {
5854             result = o.reader.read(response);
5855         }catch(e){
5856             this.fireEvent("loadexception", this, o, response, e);
5857             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5858             return;
5859         }
5860         
5861         this.fireEvent("load", this, o, o.request.arg);
5862         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5863     },
5864
5865     // private
5866     update : function(dataSet){
5867
5868     },
5869
5870     // private
5871     updateResponse : function(dataSet){
5872
5873     }
5874 });/*
5875  * Based on:
5876  * Ext JS Library 1.1.1
5877  * Copyright(c) 2006-2007, Ext JS, LLC.
5878  *
5879  * Originally Released Under LGPL - original licence link has changed is not relivant.
5880  *
5881  * Fork - LGPL
5882  * <script type="text/javascript">
5883  */
5884
5885 /**
5886  * @class Roo.data.ScriptTagProxy
5887  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5888  * other than the originating domain of the running page.<br><br>
5889  * <p>
5890  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
5891  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5892  * <p>
5893  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5894  * source code that is used as the source inside a &lt;script> tag.<br><br>
5895  * <p>
5896  * In order for the browser to process the returned data, the server must wrap the data object
5897  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5898  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5899  * depending on whether the callback name was passed:
5900  * <p>
5901  * <pre><code>
5902 boolean scriptTag = false;
5903 String cb = request.getParameter("callback");
5904 if (cb != null) {
5905     scriptTag = true;
5906     response.setContentType("text/javascript");
5907 } else {
5908     response.setContentType("application/x-json");
5909 }
5910 Writer out = response.getWriter();
5911 if (scriptTag) {
5912     out.write(cb + "(");
5913 }
5914 out.print(dataBlock.toJsonString());
5915 if (scriptTag) {
5916     out.write(");");
5917 }
5918 </pre></code>
5919  *
5920  * @constructor
5921  * @param {Object} config A configuration object.
5922  */
5923 Roo.data.ScriptTagProxy = function(config){
5924     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5925     Roo.apply(this, config);
5926     this.head = document.getElementsByTagName("head")[0];
5927 };
5928
5929 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5930
5931 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5932     /**
5933      * @cfg {String} url The URL from which to request the data object.
5934      */
5935     /**
5936      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5937      */
5938     timeout : 30000,
5939     /**
5940      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5941      * the server the name of the callback function set up by the load call to process the returned data object.
5942      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5943      * javascript output which calls this named function passing the data object as its only parameter.
5944      */
5945     callbackParam : "callback",
5946     /**
5947      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5948      * name to the request.
5949      */
5950     nocache : true,
5951
5952     /**
5953      * Load data from the configured URL, read the data object into
5954      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5955      * process that block using the passed callback.
5956      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5957      * for the request to the remote server.
5958      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5959      * object into a block of Roo.data.Records.
5960      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5961      * The function must be passed <ul>
5962      * <li>The Record block object</li>
5963      * <li>The "arg" argument from the load function</li>
5964      * <li>A boolean success indicator</li>
5965      * </ul>
5966      * @param {Object} scope The scope in which to call the callback
5967      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5968      */
5969     load : function(params, reader, callback, scope, arg){
5970         if(this.fireEvent("beforeload", this, params) !== false){
5971
5972             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5973
5974             var url = this.url;
5975             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5976             if(this.nocache){
5977                 url += "&_dc=" + (new Date().getTime());
5978             }
5979             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5980             var trans = {
5981                 id : transId,
5982                 cb : "stcCallback"+transId,
5983                 scriptId : "stcScript"+transId,
5984                 params : params,
5985                 arg : arg,
5986                 url : url,
5987                 callback : callback,
5988                 scope : scope,
5989                 reader : reader
5990             };
5991             var conn = this;
5992
5993             window[trans.cb] = function(o){
5994                 conn.handleResponse(o, trans);
5995             };
5996
5997             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
5998
5999             if(this.autoAbort !== false){
6000                 this.abort();
6001             }
6002
6003             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
6004
6005             var script = document.createElement("script");
6006             script.setAttribute("src", url);
6007             script.setAttribute("type", "text/javascript");
6008             script.setAttribute("id", trans.scriptId);
6009             this.head.appendChild(script);
6010
6011             this.trans = trans;
6012         }else{
6013             callback.call(scope||this, null, arg, false);
6014         }
6015     },
6016
6017     // private
6018     isLoading : function(){
6019         return this.trans ? true : false;
6020     },
6021
6022     /**
6023      * Abort the current server request.
6024      */
6025     abort : function(){
6026         if(this.isLoading()){
6027             this.destroyTrans(this.trans);
6028         }
6029     },
6030
6031     // private
6032     destroyTrans : function(trans, isLoaded){
6033         this.head.removeChild(document.getElementById(trans.scriptId));
6034         clearTimeout(trans.timeoutId);
6035         if(isLoaded){
6036             window[trans.cb] = undefined;
6037             try{
6038                 delete window[trans.cb];
6039             }catch(e){}
6040         }else{
6041             // if hasn't been loaded, wait for load to remove it to prevent script error
6042             window[trans.cb] = function(){
6043                 window[trans.cb] = undefined;
6044                 try{
6045                     delete window[trans.cb];
6046                 }catch(e){}
6047             };
6048         }
6049     },
6050
6051     // private
6052     handleResponse : function(o, trans){
6053         this.trans = false;
6054         this.destroyTrans(trans, true);
6055         var result;
6056         try {
6057             result = trans.reader.readRecords(o);
6058         }catch(e){
6059             this.fireEvent("loadexception", this, o, trans.arg, e);
6060             trans.callback.call(trans.scope||window, null, trans.arg, false);
6061             return;
6062         }
6063         this.fireEvent("load", this, o, trans.arg);
6064         trans.callback.call(trans.scope||window, result, trans.arg, true);
6065     },
6066
6067     // private
6068     handleFailure : function(trans){
6069         this.trans = false;
6070         this.destroyTrans(trans, false);
6071         this.fireEvent("loadexception", this, null, trans.arg);
6072         trans.callback.call(trans.scope||window, null, trans.arg, false);
6073     }
6074 });/*
6075  * Based on:
6076  * Ext JS Library 1.1.1
6077  * Copyright(c) 2006-2007, Ext JS, LLC.
6078  *
6079  * Originally Released Under LGPL - original licence link has changed is not relivant.
6080  *
6081  * Fork - LGPL
6082  * <script type="text/javascript">
6083  */
6084
6085 /**
6086  * @class Roo.data.JsonReader
6087  * @extends Roo.data.DataReader
6088  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6089  * based on mappings in a provided Roo.data.Record constructor.
6090  * 
6091  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6092  * in the reply previously. 
6093  * 
6094  * <p>
6095  * Example code:
6096  * <pre><code>
6097 var RecordDef = Roo.data.Record.create([
6098     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6099     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6100 ]);
6101 var myReader = new Roo.data.JsonReader({
6102     totalProperty: "results",    // The property which contains the total dataset size (optional)
6103     root: "rows",                // The property which contains an Array of row objects
6104     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6105 }, RecordDef);
6106 </code></pre>
6107  * <p>
6108  * This would consume a JSON file like this:
6109  * <pre><code>
6110 { 'results': 2, 'rows': [
6111     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6112     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6113 }
6114 </code></pre>
6115  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6116  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6117  * paged from the remote server.
6118  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6119  * @cfg {String} root name of the property which contains the Array of row objects.
6120  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6121  * @constructor
6122  * Create a new JsonReader
6123  * @param {Object} meta Metadata configuration options
6124  * @param {Object} recordType Either an Array of field definition objects,
6125  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6126  */
6127 Roo.data.JsonReader = function(meta, recordType){
6128     
6129     meta = meta || {};
6130     // set some defaults:
6131     Roo.applyIf(meta, {
6132         totalProperty: 'total',
6133         successProperty : 'success',
6134         root : 'data',
6135         id : 'id'
6136     });
6137     
6138     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6139 };
6140 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6141     
6142     /**
6143      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6144      * Used by Store query builder to append _requestMeta to params.
6145      * 
6146      */
6147     metaFromRemote : false,
6148     /**
6149      * This method is only used by a DataProxy which has retrieved data from a remote server.
6150      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6151      * @return {Object} data A data block which is used by an Roo.data.Store object as
6152      * a cache of Roo.data.Records.
6153      */
6154     read : function(response){
6155         var json = response.responseText;
6156        
6157         var o = /* eval:var:o */ eval("("+json+")");
6158         if(!o) {
6159             throw {message: "JsonReader.read: Json object not found"};
6160         }
6161         
6162         if(o.metaData){
6163             
6164             delete this.ef;
6165             this.metaFromRemote = true;
6166             this.meta = o.metaData;
6167             this.recordType = Roo.data.Record.create(o.metaData.fields);
6168             this.onMetaChange(this.meta, this.recordType, o);
6169         }
6170         return this.readRecords(o);
6171     },
6172
6173     // private function a store will implement
6174     onMetaChange : function(meta, recordType, o){
6175
6176     },
6177
6178     /**
6179          * @ignore
6180          */
6181     simpleAccess: function(obj, subsc) {
6182         return obj[subsc];
6183     },
6184
6185         /**
6186          * @ignore
6187          */
6188     getJsonAccessor: function(){
6189         var re = /[\[\.]/;
6190         return function(expr) {
6191             try {
6192                 return(re.test(expr))
6193                     ? new Function("obj", "return obj." + expr)
6194                     : function(obj){
6195                         return obj[expr];
6196                     };
6197             } catch(e){}
6198             return Roo.emptyFn;
6199         };
6200     }(),
6201
6202     /**
6203      * Create a data block containing Roo.data.Records from an XML document.
6204      * @param {Object} o An object which contains an Array of row objects in the property specified
6205      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6206      * which contains the total size of the dataset.
6207      * @return {Object} data A data block which is used by an Roo.data.Store object as
6208      * a cache of Roo.data.Records.
6209      */
6210     readRecords : function(o){
6211         /**
6212          * After any data loads, the raw JSON data is available for further custom processing.
6213          * @type Object
6214          */
6215         this.jsonData = o;
6216         var s = this.meta, Record = this.recordType,
6217             f = Record.prototype.fields, fi = f.items, fl = f.length;
6218
6219 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6220         if (!this.ef) {
6221             if(s.totalProperty) {
6222                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6223                 }
6224                 if(s.successProperty) {
6225                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6226                 }
6227                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6228                 if (s.id) {
6229                         var g = this.getJsonAccessor(s.id);
6230                         this.getId = function(rec) {
6231                                 var r = g(rec);
6232                                 return (r === undefined || r === "") ? null : r;
6233                         };
6234                 } else {
6235                         this.getId = function(){return null;};
6236                 }
6237             this.ef = [];
6238             for(var jj = 0; jj < fl; jj++){
6239                 f = fi[jj];
6240                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6241                 this.ef[jj] = this.getJsonAccessor(map);
6242             }
6243         }
6244
6245         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6246         if(s.totalProperty){
6247             var vt = parseInt(this.getTotal(o), 10);
6248             if(!isNaN(vt)){
6249                 totalRecords = vt;
6250             }
6251         }
6252         if(s.successProperty){
6253             var vs = this.getSuccess(o);
6254             if(vs === false || vs === 'false'){
6255                 success = false;
6256             }
6257         }
6258         var records = [];
6259             for(var i = 0; i < c; i++){
6260                     var n = root[i];
6261                 var values = {};
6262                 var id = this.getId(n);
6263                 for(var j = 0; j < fl; j++){
6264                     f = fi[j];
6265                 var v = this.ef[j](n);
6266                 if (!f.convert) {
6267                     Roo.log('missing convert for ' + f.name);
6268                     Roo.log(f);
6269                     continue;
6270                 }
6271                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6272                 }
6273                 var record = new Record(values, id);
6274                 record.json = n;
6275                 records[i] = record;
6276             }
6277             return {
6278                 success : success,
6279                 records : records,
6280                 totalRecords : totalRecords
6281             };
6282     }
6283 });/*
6284  * Based on:
6285  * Ext JS Library 1.1.1
6286  * Copyright(c) 2006-2007, Ext JS, LLC.
6287  *
6288  * Originally Released Under LGPL - original licence link has changed is not relivant.
6289  *
6290  * Fork - LGPL
6291  * <script type="text/javascript">
6292  */
6293
6294 /**
6295  * @class Roo.data.XmlReader
6296  * @extends Roo.data.DataReader
6297  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6298  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6299  * <p>
6300  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6301  * header in the HTTP response must be set to "text/xml".</em>
6302  * <p>
6303  * Example code:
6304  * <pre><code>
6305 var RecordDef = Roo.data.Record.create([
6306    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6307    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6308 ]);
6309 var myReader = new Roo.data.XmlReader({
6310    totalRecords: "results", // The element which contains the total dataset size (optional)
6311    record: "row",           // The repeated element which contains row information
6312    id: "id"                 // The element within the row that provides an ID for the record (optional)
6313 }, RecordDef);
6314 </code></pre>
6315  * <p>
6316  * This would consume an XML file like this:
6317  * <pre><code>
6318 &lt;?xml?>
6319 &lt;dataset>
6320  &lt;results>2&lt;/results>
6321  &lt;row>
6322    &lt;id>1&lt;/id>
6323    &lt;name>Bill&lt;/name>
6324    &lt;occupation>Gardener&lt;/occupation>
6325  &lt;/row>
6326  &lt;row>
6327    &lt;id>2&lt;/id>
6328    &lt;name>Ben&lt;/name>
6329    &lt;occupation>Horticulturalist&lt;/occupation>
6330  &lt;/row>
6331 &lt;/dataset>
6332 </code></pre>
6333  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6334  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6335  * paged from the remote server.
6336  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6337  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6338  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6339  * a record identifier value.
6340  * @constructor
6341  * Create a new XmlReader
6342  * @param {Object} meta Metadata configuration options
6343  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6344  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6345  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6346  */
6347 Roo.data.XmlReader = function(meta, recordType){
6348     meta = meta || {};
6349     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6350 };
6351 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6352     /**
6353      * This method is only used by a DataProxy which has retrieved data from a remote server.
6354          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6355          * to contain a method called 'responseXML' that returns an XML document object.
6356      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6357      * a cache of Roo.data.Records.
6358      */
6359     read : function(response){
6360         var doc = response.responseXML;
6361         if(!doc) {
6362             throw {message: "XmlReader.read: XML Document not available"};
6363         }
6364         return this.readRecords(doc);
6365     },
6366
6367     /**
6368      * Create a data block containing Roo.data.Records from an XML document.
6369          * @param {Object} doc A parsed XML document.
6370      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6371      * a cache of Roo.data.Records.
6372      */
6373     readRecords : function(doc){
6374         /**
6375          * After any data loads/reads, the raw XML Document is available for further custom processing.
6376          * @type XMLDocument
6377          */
6378         this.xmlData = doc;
6379         var root = doc.documentElement || doc;
6380         var q = Roo.DomQuery;
6381         var recordType = this.recordType, fields = recordType.prototype.fields;
6382         var sid = this.meta.id;
6383         var totalRecords = 0, success = true;
6384         if(this.meta.totalRecords){
6385             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6386         }
6387         
6388         if(this.meta.success){
6389             var sv = q.selectValue(this.meta.success, root, true);
6390             success = sv !== false && sv !== 'false';
6391         }
6392         var records = [];
6393         var ns = q.select(this.meta.record, root);
6394         for(var i = 0, len = ns.length; i < len; i++) {
6395                 var n = ns[i];
6396                 var values = {};
6397                 var id = sid ? q.selectValue(sid, n) : undefined;
6398                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6399                     var f = fields.items[j];
6400                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6401                     v = f.convert(v);
6402                     values[f.name] = v;
6403                 }
6404                 var record = new recordType(values, id);
6405                 record.node = n;
6406                 records[records.length] = record;
6407             }
6408
6409             return {
6410                 success : success,
6411                 records : records,
6412                 totalRecords : totalRecords || records.length
6413             };
6414     }
6415 });/*
6416  * Based on:
6417  * Ext JS Library 1.1.1
6418  * Copyright(c) 2006-2007, Ext JS, LLC.
6419  *
6420  * Originally Released Under LGPL - original licence link has changed is not relivant.
6421  *
6422  * Fork - LGPL
6423  * <script type="text/javascript">
6424  */
6425
6426 /**
6427  * @class Roo.data.ArrayReader
6428  * @extends Roo.data.DataReader
6429  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6430  * Each element of that Array represents a row of data fields. The
6431  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6432  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6433  * <p>
6434  * Example code:.
6435  * <pre><code>
6436 var RecordDef = Roo.data.Record.create([
6437     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6438     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6439 ]);
6440 var myReader = new Roo.data.ArrayReader({
6441     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6442 }, RecordDef);
6443 </code></pre>
6444  * <p>
6445  * This would consume an Array like this:
6446  * <pre><code>
6447 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6448   </code></pre>
6449  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6450  * @constructor
6451  * Create a new JsonReader
6452  * @param {Object} meta Metadata configuration options.
6453  * @param {Object} recordType Either an Array of field definition objects
6454  * as specified to {@link Roo.data.Record#create},
6455  * or an {@link Roo.data.Record} object
6456  * created using {@link Roo.data.Record#create}.
6457  */
6458 Roo.data.ArrayReader = function(meta, recordType){
6459     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6460 };
6461
6462 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6463     /**
6464      * Create a data block containing Roo.data.Records from an XML document.
6465      * @param {Object} o An Array of row objects which represents the dataset.
6466      * @return {Object} data A data block which is used by an Roo.data.Store object as
6467      * a cache of Roo.data.Records.
6468      */
6469     readRecords : function(o){
6470         var sid = this.meta ? this.meta.id : null;
6471         var recordType = this.recordType, fields = recordType.prototype.fields;
6472         var records = [];
6473         var root = o;
6474             for(var i = 0; i < root.length; i++){
6475                     var n = root[i];
6476                 var values = {};
6477                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6478                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6479                 var f = fields.items[j];
6480                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6481                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6482                 v = f.convert(v);
6483                 values[f.name] = v;
6484             }
6485                 var record = new recordType(values, id);
6486                 record.json = n;
6487                 records[records.length] = record;
6488             }
6489             return {
6490                 records : records,
6491                 totalRecords : records.length
6492             };
6493     }
6494 });/*
6495  * Based on:
6496  * Ext JS Library 1.1.1
6497  * Copyright(c) 2006-2007, Ext JS, LLC.
6498  *
6499  * Originally Released Under LGPL - original licence link has changed is not relivant.
6500  *
6501  * Fork - LGPL
6502  * <script type="text/javascript">
6503  */
6504
6505
6506 /**
6507  * @class Roo.data.Tree
6508  * @extends Roo.util.Observable
6509  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6510  * in the tree have most standard DOM functionality.
6511  * @constructor
6512  * @param {Node} root (optional) The root node
6513  */
6514 Roo.data.Tree = function(root){
6515    this.nodeHash = {};
6516    /**
6517     * The root node for this tree
6518     * @type Node
6519     */
6520    this.root = null;
6521    if(root){
6522        this.setRootNode(root);
6523    }
6524    this.addEvents({
6525        /**
6526         * @event append
6527         * Fires when a new child node is appended to a node in this tree.
6528         * @param {Tree} tree The owner tree
6529         * @param {Node} parent The parent node
6530         * @param {Node} node The newly appended node
6531         * @param {Number} index The index of the newly appended node
6532         */
6533        "append" : true,
6534        /**
6535         * @event remove
6536         * Fires when a child node is removed from a node in this tree.
6537         * @param {Tree} tree The owner tree
6538         * @param {Node} parent The parent node
6539         * @param {Node} node The child node removed
6540         */
6541        "remove" : true,
6542        /**
6543         * @event move
6544         * Fires when a node is moved to a new location in the tree
6545         * @param {Tree} tree The owner tree
6546         * @param {Node} node The node moved
6547         * @param {Node} oldParent The old parent of this node
6548         * @param {Node} newParent The new parent of this node
6549         * @param {Number} index The index it was moved to
6550         */
6551        "move" : true,
6552        /**
6553         * @event insert
6554         * Fires when a new child node is inserted in a node in this tree.
6555         * @param {Tree} tree The owner tree
6556         * @param {Node} parent The parent node
6557         * @param {Node} node The child node inserted
6558         * @param {Node} refNode The child node the node was inserted before
6559         */
6560        "insert" : true,
6561        /**
6562         * @event beforeappend
6563         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6564         * @param {Tree} tree The owner tree
6565         * @param {Node} parent The parent node
6566         * @param {Node} node The child node to be appended
6567         */
6568        "beforeappend" : true,
6569        /**
6570         * @event beforeremove
6571         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6572         * @param {Tree} tree The owner tree
6573         * @param {Node} parent The parent node
6574         * @param {Node} node The child node to be removed
6575         */
6576        "beforeremove" : true,
6577        /**
6578         * @event beforemove
6579         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6580         * @param {Tree} tree The owner tree
6581         * @param {Node} node The node being moved
6582         * @param {Node} oldParent The parent of the node
6583         * @param {Node} newParent The new parent the node is moving to
6584         * @param {Number} index The index it is being moved to
6585         */
6586        "beforemove" : true,
6587        /**
6588         * @event beforeinsert
6589         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6590         * @param {Tree} tree The owner tree
6591         * @param {Node} parent The parent node
6592         * @param {Node} node The child node to be inserted
6593         * @param {Node} refNode The child node the node is being inserted before
6594         */
6595        "beforeinsert" : true
6596    });
6597
6598     Roo.data.Tree.superclass.constructor.call(this);
6599 };
6600
6601 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6602     pathSeparator: "/",
6603
6604     proxyNodeEvent : function(){
6605         return this.fireEvent.apply(this, arguments);
6606     },
6607
6608     /**
6609      * Returns the root node for this tree.
6610      * @return {Node}
6611      */
6612     getRootNode : function(){
6613         return this.root;
6614     },
6615
6616     /**
6617      * Sets the root node for this tree.
6618      * @param {Node} node
6619      * @return {Node}
6620      */
6621     setRootNode : function(node){
6622         this.root = node;
6623         node.ownerTree = this;
6624         node.isRoot = true;
6625         this.registerNode(node);
6626         return node;
6627     },
6628
6629     /**
6630      * Gets a node in this tree by its id.
6631      * @param {String} id
6632      * @return {Node}
6633      */
6634     getNodeById : function(id){
6635         return this.nodeHash[id];
6636     },
6637
6638     registerNode : function(node){
6639         this.nodeHash[node.id] = node;
6640     },
6641
6642     unregisterNode : function(node){
6643         delete this.nodeHash[node.id];
6644     },
6645
6646     toString : function(){
6647         return "[Tree"+(this.id?" "+this.id:"")+"]";
6648     }
6649 });
6650
6651 /**
6652  * @class Roo.data.Node
6653  * @extends Roo.util.Observable
6654  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6655  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6656  * @constructor
6657  * @param {Object} attributes The attributes/config for the node
6658  */
6659 Roo.data.Node = function(attributes){
6660     /**
6661      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6662      * @type {Object}
6663      */
6664     this.attributes = attributes || {};
6665     this.leaf = this.attributes.leaf;
6666     /**
6667      * The node id. @type String
6668      */
6669     this.id = this.attributes.id;
6670     if(!this.id){
6671         this.id = Roo.id(null, "ynode-");
6672         this.attributes.id = this.id;
6673     }
6674     /**
6675      * All child nodes of this node. @type Array
6676      */
6677     this.childNodes = [];
6678     if(!this.childNodes.indexOf){ // indexOf is a must
6679         this.childNodes.indexOf = function(o){
6680             for(var i = 0, len = this.length; i < len; i++){
6681                 if(this[i] == o) {
6682                     return i;
6683                 }
6684             }
6685             return -1;
6686         };
6687     }
6688     /**
6689      * The parent node for this node. @type Node
6690      */
6691     this.parentNode = null;
6692     /**
6693      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6694      */
6695     this.firstChild = null;
6696     /**
6697      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6698      */
6699     this.lastChild = null;
6700     /**
6701      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6702      */
6703     this.previousSibling = null;
6704     /**
6705      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6706      */
6707     this.nextSibling = null;
6708
6709     this.addEvents({
6710        /**
6711         * @event append
6712         * Fires when a new child node is appended
6713         * @param {Tree} tree The owner tree
6714         * @param {Node} this This node
6715         * @param {Node} node The newly appended node
6716         * @param {Number} index The index of the newly appended node
6717         */
6718        "append" : true,
6719        /**
6720         * @event remove
6721         * Fires when a child node is removed
6722         * @param {Tree} tree The owner tree
6723         * @param {Node} this This node
6724         * @param {Node} node The removed node
6725         */
6726        "remove" : true,
6727        /**
6728         * @event move
6729         * Fires when this node is moved to a new location in the tree
6730         * @param {Tree} tree The owner tree
6731         * @param {Node} this This node
6732         * @param {Node} oldParent The old parent of this node
6733         * @param {Node} newParent The new parent of this node
6734         * @param {Number} index The index it was moved to
6735         */
6736        "move" : true,
6737        /**
6738         * @event insert
6739         * Fires when a new child node is inserted.
6740         * @param {Tree} tree The owner tree
6741         * @param {Node} this This node
6742         * @param {Node} node The child node inserted
6743         * @param {Node} refNode The child node the node was inserted before
6744         */
6745        "insert" : true,
6746        /**
6747         * @event beforeappend
6748         * Fires before a new child is appended, return false to cancel the append.
6749         * @param {Tree} tree The owner tree
6750         * @param {Node} this This node
6751         * @param {Node} node The child node to be appended
6752         */
6753        "beforeappend" : true,
6754        /**
6755         * @event beforeremove
6756         * Fires before a child is removed, return false to cancel the remove.
6757         * @param {Tree} tree The owner tree
6758         * @param {Node} this This node
6759         * @param {Node} node The child node to be removed
6760         */
6761        "beforeremove" : true,
6762        /**
6763         * @event beforemove
6764         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6765         * @param {Tree} tree The owner tree
6766         * @param {Node} this This node
6767         * @param {Node} oldParent The parent of this node
6768         * @param {Node} newParent The new parent this node is moving to
6769         * @param {Number} index The index it is being moved to
6770         */
6771        "beforemove" : true,
6772        /**
6773         * @event beforeinsert
6774         * Fires before a new child is inserted, return false to cancel the insert.
6775         * @param {Tree} tree The owner tree
6776         * @param {Node} this This node
6777         * @param {Node} node The child node to be inserted
6778         * @param {Node} refNode The child node the node is being inserted before
6779         */
6780        "beforeinsert" : true
6781    });
6782     this.listeners = this.attributes.listeners;
6783     Roo.data.Node.superclass.constructor.call(this);
6784 };
6785
6786 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6787     fireEvent : function(evtName){
6788         // first do standard event for this node
6789         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6790             return false;
6791         }
6792         // then bubble it up to the tree if the event wasn't cancelled
6793         var ot = this.getOwnerTree();
6794         if(ot){
6795             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6796                 return false;
6797             }
6798         }
6799         return true;
6800     },
6801
6802     /**
6803      * Returns true if this node is a leaf
6804      * @return {Boolean}
6805      */
6806     isLeaf : function(){
6807         return this.leaf === true;
6808     },
6809
6810     // private
6811     setFirstChild : function(node){
6812         this.firstChild = node;
6813     },
6814
6815     //private
6816     setLastChild : function(node){
6817         this.lastChild = node;
6818     },
6819
6820
6821     /**
6822      * Returns true if this node is the last child of its parent
6823      * @return {Boolean}
6824      */
6825     isLast : function(){
6826        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6827     },
6828
6829     /**
6830      * Returns true if this node is the first child of its parent
6831      * @return {Boolean}
6832      */
6833     isFirst : function(){
6834        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6835     },
6836
6837     hasChildNodes : function(){
6838         return !this.isLeaf() && this.childNodes.length > 0;
6839     },
6840
6841     /**
6842      * Insert node(s) as the last child node of this node.
6843      * @param {Node/Array} node The node or Array of nodes to append
6844      * @return {Node} The appended node if single append, or null if an array was passed
6845      */
6846     appendChild : function(node){
6847         var multi = false;
6848         if(node instanceof Array){
6849             multi = node;
6850         }else if(arguments.length > 1){
6851             multi = arguments;
6852         }
6853         // if passed an array or multiple args do them one by one
6854         if(multi){
6855             for(var i = 0, len = multi.length; i < len; i++) {
6856                 this.appendChild(multi[i]);
6857             }
6858         }else{
6859             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6860                 return false;
6861             }
6862             var index = this.childNodes.length;
6863             var oldParent = node.parentNode;
6864             // it's a move, make sure we move it cleanly
6865             if(oldParent){
6866                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6867                     return false;
6868                 }
6869                 oldParent.removeChild(node);
6870             }
6871             index = this.childNodes.length;
6872             if(index == 0){
6873                 this.setFirstChild(node);
6874             }
6875             this.childNodes.push(node);
6876             node.parentNode = this;
6877             var ps = this.childNodes[index-1];
6878             if(ps){
6879                 node.previousSibling = ps;
6880                 ps.nextSibling = node;
6881             }else{
6882                 node.previousSibling = null;
6883             }
6884             node.nextSibling = null;
6885             this.setLastChild(node);
6886             node.setOwnerTree(this.getOwnerTree());
6887             this.fireEvent("append", this.ownerTree, this, node, index);
6888             if(oldParent){
6889                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6890             }
6891             return node;
6892         }
6893     },
6894
6895     /**
6896      * Removes a child node from this node.
6897      * @param {Node} node The node to remove
6898      * @return {Node} The removed node
6899      */
6900     removeChild : function(node){
6901         var index = this.childNodes.indexOf(node);
6902         if(index == -1){
6903             return false;
6904         }
6905         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6906             return false;
6907         }
6908
6909         // remove it from childNodes collection
6910         this.childNodes.splice(index, 1);
6911
6912         // update siblings
6913         if(node.previousSibling){
6914             node.previousSibling.nextSibling = node.nextSibling;
6915         }
6916         if(node.nextSibling){
6917             node.nextSibling.previousSibling = node.previousSibling;
6918         }
6919
6920         // update child refs
6921         if(this.firstChild == node){
6922             this.setFirstChild(node.nextSibling);
6923         }
6924         if(this.lastChild == node){
6925             this.setLastChild(node.previousSibling);
6926         }
6927
6928         node.setOwnerTree(null);
6929         // clear any references from the node
6930         node.parentNode = null;
6931         node.previousSibling = null;
6932         node.nextSibling = null;
6933         this.fireEvent("remove", this.ownerTree, this, node);
6934         return node;
6935     },
6936
6937     /**
6938      * Inserts the first node before the second node in this nodes childNodes collection.
6939      * @param {Node} node The node to insert
6940      * @param {Node} refNode The node to insert before (if null the node is appended)
6941      * @return {Node} The inserted node
6942      */
6943     insertBefore : function(node, refNode){
6944         if(!refNode){ // like standard Dom, refNode can be null for append
6945             return this.appendChild(node);
6946         }
6947         // nothing to do
6948         if(node == refNode){
6949             return false;
6950         }
6951
6952         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6953             return false;
6954         }
6955         var index = this.childNodes.indexOf(refNode);
6956         var oldParent = node.parentNode;
6957         var refIndex = index;
6958
6959         // when moving internally, indexes will change after remove
6960         if(oldParent == this && this.childNodes.indexOf(node) < index){
6961             refIndex--;
6962         }
6963
6964         // it's a move, make sure we move it cleanly
6965         if(oldParent){
6966             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6967                 return false;
6968             }
6969             oldParent.removeChild(node);
6970         }
6971         if(refIndex == 0){
6972             this.setFirstChild(node);
6973         }
6974         this.childNodes.splice(refIndex, 0, node);
6975         node.parentNode = this;
6976         var ps = this.childNodes[refIndex-1];
6977         if(ps){
6978             node.previousSibling = ps;
6979             ps.nextSibling = node;
6980         }else{
6981             node.previousSibling = null;
6982         }
6983         node.nextSibling = refNode;
6984         refNode.previousSibling = node;
6985         node.setOwnerTree(this.getOwnerTree());
6986         this.fireEvent("insert", this.ownerTree, this, node, refNode);
6987         if(oldParent){
6988             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
6989         }
6990         return node;
6991     },
6992
6993     /**
6994      * Returns the child node at the specified index.
6995      * @param {Number} index
6996      * @return {Node}
6997      */
6998     item : function(index){
6999         return this.childNodes[index];
7000     },
7001
7002     /**
7003      * Replaces one child node in this node with another.
7004      * @param {Node} newChild The replacement node
7005      * @param {Node} oldChild The node to replace
7006      * @return {Node} The replaced node
7007      */
7008     replaceChild : function(newChild, oldChild){
7009         this.insertBefore(newChild, oldChild);
7010         this.removeChild(oldChild);
7011         return oldChild;
7012     },
7013
7014     /**
7015      * Returns the index of a child node
7016      * @param {Node} node
7017      * @return {Number} The index of the node or -1 if it was not found
7018      */
7019     indexOf : function(child){
7020         return this.childNodes.indexOf(child);
7021     },
7022
7023     /**
7024      * Returns the tree this node is in.
7025      * @return {Tree}
7026      */
7027     getOwnerTree : function(){
7028         // if it doesn't have one, look for one
7029         if(!this.ownerTree){
7030             var p = this;
7031             while(p){
7032                 if(p.ownerTree){
7033                     this.ownerTree = p.ownerTree;
7034                     break;
7035                 }
7036                 p = p.parentNode;
7037             }
7038         }
7039         return this.ownerTree;
7040     },
7041
7042     /**
7043      * Returns depth of this node (the root node has a depth of 0)
7044      * @return {Number}
7045      */
7046     getDepth : function(){
7047         var depth = 0;
7048         var p = this;
7049         while(p.parentNode){
7050             ++depth;
7051             p = p.parentNode;
7052         }
7053         return depth;
7054     },
7055
7056     // private
7057     setOwnerTree : function(tree){
7058         // if it's move, we need to update everyone
7059         if(tree != this.ownerTree){
7060             if(this.ownerTree){
7061                 this.ownerTree.unregisterNode(this);
7062             }
7063             this.ownerTree = tree;
7064             var cs = this.childNodes;
7065             for(var i = 0, len = cs.length; i < len; i++) {
7066                 cs[i].setOwnerTree(tree);
7067             }
7068             if(tree){
7069                 tree.registerNode(this);
7070             }
7071         }
7072     },
7073
7074     /**
7075      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7076      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7077      * @return {String} The path
7078      */
7079     getPath : function(attr){
7080         attr = attr || "id";
7081         var p = this.parentNode;
7082         var b = [this.attributes[attr]];
7083         while(p){
7084             b.unshift(p.attributes[attr]);
7085             p = p.parentNode;
7086         }
7087         var sep = this.getOwnerTree().pathSeparator;
7088         return sep + b.join(sep);
7089     },
7090
7091     /**
7092      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7093      * function call will be the scope provided or the current node. The arguments to the function
7094      * will be the args provided or the current node. If the function returns false at any point,
7095      * the bubble is stopped.
7096      * @param {Function} fn The function to call
7097      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7098      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7099      */
7100     bubble : function(fn, scope, args){
7101         var p = this;
7102         while(p){
7103             if(fn.call(scope || p, args || p) === false){
7104                 break;
7105             }
7106             p = p.parentNode;
7107         }
7108     },
7109
7110     /**
7111      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7112      * function call will be the scope provided or the current node. The arguments to the function
7113      * will be the args provided or the current node. If the function returns false at any point,
7114      * the cascade is stopped on that branch.
7115      * @param {Function} fn The function to call
7116      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7117      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7118      */
7119     cascade : function(fn, scope, args){
7120         if(fn.call(scope || this, args || this) !== false){
7121             var cs = this.childNodes;
7122             for(var i = 0, len = cs.length; i < len; i++) {
7123                 cs[i].cascade(fn, scope, args);
7124             }
7125         }
7126     },
7127
7128     /**
7129      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7130      * function call will be the scope provided or the current node. The arguments to the function
7131      * will be the args provided or the current node. If the function returns false at any point,
7132      * the iteration stops.
7133      * @param {Function} fn The function to call
7134      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7135      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7136      */
7137     eachChild : function(fn, scope, args){
7138         var cs = this.childNodes;
7139         for(var i = 0, len = cs.length; i < len; i++) {
7140                 if(fn.call(scope || this, args || cs[i]) === false){
7141                     break;
7142                 }
7143         }
7144     },
7145
7146     /**
7147      * Finds the first child that has the attribute with the specified value.
7148      * @param {String} attribute The attribute name
7149      * @param {Mixed} value The value to search for
7150      * @return {Node} The found child or null if none was found
7151      */
7152     findChild : function(attribute, value){
7153         var cs = this.childNodes;
7154         for(var i = 0, len = cs.length; i < len; i++) {
7155                 if(cs[i].attributes[attribute] == value){
7156                     return cs[i];
7157                 }
7158         }
7159         return null;
7160     },
7161
7162     /**
7163      * Finds the first child by a custom function. The child matches if the function passed
7164      * returns true.
7165      * @param {Function} fn
7166      * @param {Object} scope (optional)
7167      * @return {Node} The found child or null if none was found
7168      */
7169     findChildBy : function(fn, scope){
7170         var cs = this.childNodes;
7171         for(var i = 0, len = cs.length; i < len; i++) {
7172                 if(fn.call(scope||cs[i], cs[i]) === true){
7173                     return cs[i];
7174                 }
7175         }
7176         return null;
7177     },
7178
7179     /**
7180      * Sorts this nodes children using the supplied sort function
7181      * @param {Function} fn
7182      * @param {Object} scope (optional)
7183      */
7184     sort : function(fn, scope){
7185         var cs = this.childNodes;
7186         var len = cs.length;
7187         if(len > 0){
7188             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7189             cs.sort(sortFn);
7190             for(var i = 0; i < len; i++){
7191                 var n = cs[i];
7192                 n.previousSibling = cs[i-1];
7193                 n.nextSibling = cs[i+1];
7194                 if(i == 0){
7195                     this.setFirstChild(n);
7196                 }
7197                 if(i == len-1){
7198                     this.setLastChild(n);
7199                 }
7200             }
7201         }
7202     },
7203
7204     /**
7205      * Returns true if this node is an ancestor (at any point) of the passed node.
7206      * @param {Node} node
7207      * @return {Boolean}
7208      */
7209     contains : function(node){
7210         return node.isAncestor(this);
7211     },
7212
7213     /**
7214      * Returns true if the passed node is an ancestor (at any point) of this node.
7215      * @param {Node} node
7216      * @return {Boolean}
7217      */
7218     isAncestor : function(node){
7219         var p = this.parentNode;
7220         while(p){
7221             if(p == node){
7222                 return true;
7223             }
7224             p = p.parentNode;
7225         }
7226         return false;
7227     },
7228
7229     toString : function(){
7230         return "[Node"+(this.id?" "+this.id:"")+"]";
7231     }
7232 });/*
7233  * Based on:
7234  * Ext JS Library 1.1.1
7235  * Copyright(c) 2006-2007, Ext JS, LLC.
7236  *
7237  * Originally Released Under LGPL - original licence link has changed is not relivant.
7238  *
7239  * Fork - LGPL
7240  * <script type="text/javascript">
7241  */
7242  
7243
7244 /**
7245  * @class Roo.ComponentMgr
7246  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7247  * @singleton
7248  */
7249 Roo.ComponentMgr = function(){
7250     var all = new Roo.util.MixedCollection();
7251
7252     return {
7253         /**
7254          * Registers a component.
7255          * @param {Roo.Component} c The component
7256          */
7257         register : function(c){
7258             all.add(c);
7259         },
7260
7261         /**
7262          * Unregisters a component.
7263          * @param {Roo.Component} c The component
7264          */
7265         unregister : function(c){
7266             all.remove(c);
7267         },
7268
7269         /**
7270          * Returns a component by id
7271          * @param {String} id The component id
7272          */
7273         get : function(id){
7274             return all.get(id);
7275         },
7276
7277         /**
7278          * Registers a function that will be called when a specified component is added to ComponentMgr
7279          * @param {String} id The component id
7280          * @param {Funtction} fn The callback function
7281          * @param {Object} scope The scope of the callback
7282          */
7283         onAvailable : function(id, fn, scope){
7284             all.on("add", function(index, o){
7285                 if(o.id == id){
7286                     fn.call(scope || o, o);
7287                     all.un("add", fn, scope);
7288                 }
7289             });
7290         }
7291     };
7292 }();/*
7293  * Based on:
7294  * Ext JS Library 1.1.1
7295  * Copyright(c) 2006-2007, Ext JS, LLC.
7296  *
7297  * Originally Released Under LGPL - original licence link has changed is not relivant.
7298  *
7299  * Fork - LGPL
7300  * <script type="text/javascript">
7301  */
7302  
7303 /**
7304  * @class Roo.Component
7305  * @extends Roo.util.Observable
7306  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7307  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7308  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7309  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7310  * All visual components (widgets) that require rendering into a layout should subclass Component.
7311  * @constructor
7312  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7313  * 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
7314  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7315  */
7316 Roo.Component = function(config){
7317     config = config || {};
7318     if(config.tagName || config.dom || typeof config == "string"){ // element object
7319         config = {el: config, id: config.id || config};
7320     }
7321     this.initialConfig = config;
7322
7323     Roo.apply(this, config);
7324     this.addEvents({
7325         /**
7326          * @event disable
7327          * Fires after the component is disabled.
7328              * @param {Roo.Component} this
7329              */
7330         disable : true,
7331         /**
7332          * @event enable
7333          * Fires after the component is enabled.
7334              * @param {Roo.Component} this
7335              */
7336         enable : true,
7337         /**
7338          * @event beforeshow
7339          * Fires before the component is shown.  Return false to stop the show.
7340              * @param {Roo.Component} this
7341              */
7342         beforeshow : true,
7343         /**
7344          * @event show
7345          * Fires after the component is shown.
7346              * @param {Roo.Component} this
7347              */
7348         show : true,
7349         /**
7350          * @event beforehide
7351          * Fires before the component is hidden. Return false to stop the hide.
7352              * @param {Roo.Component} this
7353              */
7354         beforehide : true,
7355         /**
7356          * @event hide
7357          * Fires after the component is hidden.
7358              * @param {Roo.Component} this
7359              */
7360         hide : true,
7361         /**
7362          * @event beforerender
7363          * Fires before the component is rendered. Return false to stop the render.
7364              * @param {Roo.Component} this
7365              */
7366         beforerender : true,
7367         /**
7368          * @event render
7369          * Fires after the component is rendered.
7370              * @param {Roo.Component} this
7371              */
7372         render : true,
7373         /**
7374          * @event beforedestroy
7375          * Fires before the component is destroyed. Return false to stop the destroy.
7376              * @param {Roo.Component} this
7377              */
7378         beforedestroy : true,
7379         /**
7380          * @event destroy
7381          * Fires after the component is destroyed.
7382              * @param {Roo.Component} this
7383              */
7384         destroy : true
7385     });
7386     if(!this.id){
7387         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7388     }
7389     Roo.ComponentMgr.register(this);
7390     Roo.Component.superclass.constructor.call(this);
7391     this.initComponent();
7392     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7393         this.render(this.renderTo);
7394         delete this.renderTo;
7395     }
7396 };
7397
7398 /** @private */
7399 Roo.Component.AUTO_ID = 1000;
7400
7401 Roo.extend(Roo.Component, Roo.util.Observable, {
7402     /**
7403      * @scope Roo.Component.prototype
7404      * @type {Boolean}
7405      * true if this component is hidden. Read-only.
7406      */
7407     hidden : false,
7408     /**
7409      * @type {Boolean}
7410      * true if this component is disabled. Read-only.
7411      */
7412     disabled : false,
7413     /**
7414      * @type {Boolean}
7415      * true if this component has been rendered. Read-only.
7416      */
7417     rendered : false,
7418     
7419     /** @cfg {String} disableClass
7420      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7421      */
7422     disabledClass : "x-item-disabled",
7423         /** @cfg {Boolean} allowDomMove
7424          * Whether the component can move the Dom node when rendering (defaults to true).
7425          */
7426     allowDomMove : true,
7427     /** @cfg {String} hideMode
7428      * How this component should hidden. Supported values are
7429      * "visibility" (css visibility), "offsets" (negative offset position) and
7430      * "display" (css display) - defaults to "display".
7431      */
7432     hideMode: 'display',
7433
7434     /** @private */
7435     ctype : "Roo.Component",
7436
7437     /**
7438      * @cfg {String} actionMode 
7439      * which property holds the element that used for  hide() / show() / disable() / enable()
7440      * default is 'el' 
7441      */
7442     actionMode : "el",
7443
7444     /** @private */
7445     getActionEl : function(){
7446         return this[this.actionMode];
7447     },
7448
7449     initComponent : Roo.emptyFn,
7450     /**
7451      * If this is a lazy rendering component, render it to its container element.
7452      * @param {String/HTMLElement/Element} container (optional) The element this component should be rendered into. If it is being applied to existing markup, this should be left off.
7453      */
7454     render : function(container, position){
7455         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7456             if(!container && this.el){
7457                 this.el = Roo.get(this.el);
7458                 container = this.el.dom.parentNode;
7459                 this.allowDomMove = false;
7460             }
7461             this.container = Roo.get(container);
7462             this.rendered = true;
7463             if(position !== undefined){
7464                 if(typeof position == 'number'){
7465                     position = this.container.dom.childNodes[position];
7466                 }else{
7467                     position = Roo.getDom(position);
7468                 }
7469             }
7470             this.onRender(this.container, position || null);
7471             if(this.cls){
7472                 this.el.addClass(this.cls);
7473                 delete this.cls;
7474             }
7475             if(this.style){
7476                 this.el.applyStyles(this.style);
7477                 delete this.style;
7478             }
7479             this.fireEvent("render", this);
7480             this.afterRender(this.container);
7481             if(this.hidden){
7482                 this.hide();
7483             }
7484             if(this.disabled){
7485                 this.disable();
7486             }
7487         }
7488         return this;
7489     },
7490
7491     /** @private */
7492     // default function is not really useful
7493     onRender : function(ct, position){
7494         if(this.el){
7495             this.el = Roo.get(this.el);
7496             if(this.allowDomMove !== false){
7497                 ct.dom.insertBefore(this.el.dom, position);
7498             }
7499         }
7500     },
7501
7502     /** @private */
7503     getAutoCreate : function(){
7504         var cfg = typeof this.autoCreate == "object" ?
7505                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7506         if(this.id && !cfg.id){
7507             cfg.id = this.id;
7508         }
7509         return cfg;
7510     },
7511
7512     /** @private */
7513     afterRender : Roo.emptyFn,
7514
7515     /**
7516      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7517      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7518      */
7519     destroy : function(){
7520         if(this.fireEvent("beforedestroy", this) !== false){
7521             this.purgeListeners();
7522             this.beforeDestroy();
7523             if(this.rendered){
7524                 this.el.removeAllListeners();
7525                 this.el.remove();
7526                 if(this.actionMode == "container"){
7527                     this.container.remove();
7528                 }
7529             }
7530             this.onDestroy();
7531             Roo.ComponentMgr.unregister(this);
7532             this.fireEvent("destroy", this);
7533         }
7534     },
7535
7536         /** @private */
7537     beforeDestroy : function(){
7538
7539     },
7540
7541         /** @private */
7542         onDestroy : function(){
7543
7544     },
7545
7546     /**
7547      * Returns the underlying {@link Roo.Element}.
7548      * @return {Roo.Element} The element
7549      */
7550     getEl : function(){
7551         return this.el;
7552     },
7553
7554     /**
7555      * Returns the id of this component.
7556      * @return {String}
7557      */
7558     getId : function(){
7559         return this.id;
7560     },
7561
7562     /**
7563      * Try to focus this component.
7564      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7565      * @return {Roo.Component} this
7566      */
7567     focus : function(selectText){
7568         if(this.rendered){
7569             this.el.focus();
7570             if(selectText === true){
7571                 this.el.dom.select();
7572             }
7573         }
7574         return this;
7575     },
7576
7577     /** @private */
7578     blur : function(){
7579         if(this.rendered){
7580             this.el.blur();
7581         }
7582         return this;
7583     },
7584
7585     /**
7586      * Disable this component.
7587      * @return {Roo.Component} this
7588      */
7589     disable : function(){
7590         if(this.rendered){
7591             this.onDisable();
7592         }
7593         this.disabled = true;
7594         this.fireEvent("disable", this);
7595         return this;
7596     },
7597
7598         // private
7599     onDisable : function(){
7600         this.getActionEl().addClass(this.disabledClass);
7601         this.el.dom.disabled = true;
7602     },
7603
7604     /**
7605      * Enable this component.
7606      * @return {Roo.Component} this
7607      */
7608     enable : function(){
7609         if(this.rendered){
7610             this.onEnable();
7611         }
7612         this.disabled = false;
7613         this.fireEvent("enable", this);
7614         return this;
7615     },
7616
7617         // private
7618     onEnable : function(){
7619         this.getActionEl().removeClass(this.disabledClass);
7620         this.el.dom.disabled = false;
7621     },
7622
7623     /**
7624      * Convenience function for setting disabled/enabled by boolean.
7625      * @param {Boolean} disabled
7626      */
7627     setDisabled : function(disabled){
7628         this[disabled ? "disable" : "enable"]();
7629     },
7630
7631     /**
7632      * Show this component.
7633      * @return {Roo.Component} this
7634      */
7635     show: function(){
7636         if(this.fireEvent("beforeshow", this) !== false){
7637             this.hidden = false;
7638             if(this.rendered){
7639                 this.onShow();
7640             }
7641             this.fireEvent("show", this);
7642         }
7643         return this;
7644     },
7645
7646     // private
7647     onShow : function(){
7648         var ae = this.getActionEl();
7649         if(this.hideMode == 'visibility'){
7650             ae.dom.style.visibility = "visible";
7651         }else if(this.hideMode == 'offsets'){
7652             ae.removeClass('x-hidden');
7653         }else{
7654             ae.dom.style.display = "";
7655         }
7656     },
7657
7658     /**
7659      * Hide this component.
7660      * @return {Roo.Component} this
7661      */
7662     hide: function(){
7663         if(this.fireEvent("beforehide", this) !== false){
7664             this.hidden = true;
7665             if(this.rendered){
7666                 this.onHide();
7667             }
7668             this.fireEvent("hide", this);
7669         }
7670         return this;
7671     },
7672
7673     // private
7674     onHide : function(){
7675         var ae = this.getActionEl();
7676         if(this.hideMode == 'visibility'){
7677             ae.dom.style.visibility = "hidden";
7678         }else if(this.hideMode == 'offsets'){
7679             ae.addClass('x-hidden');
7680         }else{
7681             ae.dom.style.display = "none";
7682         }
7683     },
7684
7685     /**
7686      * Convenience function to hide or show this component by boolean.
7687      * @param {Boolean} visible True to show, false to hide
7688      * @return {Roo.Component} this
7689      */
7690     setVisible: function(visible){
7691         if(visible) {
7692             this.show();
7693         }else{
7694             this.hide();
7695         }
7696         return this;
7697     },
7698
7699     /**
7700      * Returns true if this component is visible.
7701      */
7702     isVisible : function(){
7703         return this.getActionEl().isVisible();
7704     },
7705
7706     cloneConfig : function(overrides){
7707         overrides = overrides || {};
7708         var id = overrides.id || Roo.id();
7709         var cfg = Roo.applyIf(overrides, this.initialConfig);
7710         cfg.id = id; // prevent dup id
7711         return new this.constructor(cfg);
7712     }
7713 });/*
7714  * Based on:
7715  * Ext JS Library 1.1.1
7716  * Copyright(c) 2006-2007, Ext JS, LLC.
7717  *
7718  * Originally Released Under LGPL - original licence link has changed is not relivant.
7719  *
7720  * Fork - LGPL
7721  * <script type="text/javascript">
7722  */
7723  (function(){ 
7724 /**
7725  * @class Roo.Layer
7726  * @extends Roo.Element
7727  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7728  * automatic maintaining of shadow/shim positions.
7729  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7730  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7731  * you can pass a string with a CSS class name. False turns off the shadow.
7732  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7733  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7734  * @cfg {String} cls CSS class to add to the element
7735  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7736  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7737  * @constructor
7738  * @param {Object} config An object with config options.
7739  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7740  */
7741
7742 Roo.Layer = function(config, existingEl){
7743     config = config || {};
7744     var dh = Roo.DomHelper;
7745     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7746     if(existingEl){
7747         this.dom = Roo.getDom(existingEl);
7748     }
7749     if(!this.dom){
7750         var o = config.dh || {tag: "div", cls: "x-layer"};
7751         this.dom = dh.append(pel, o);
7752     }
7753     if(config.cls){
7754         this.addClass(config.cls);
7755     }
7756     this.constrain = config.constrain !== false;
7757     this.visibilityMode = Roo.Element.VISIBILITY;
7758     if(config.id){
7759         this.id = this.dom.id = config.id;
7760     }else{
7761         this.id = Roo.id(this.dom);
7762     }
7763     this.zindex = config.zindex || this.getZIndex();
7764     this.position("absolute", this.zindex);
7765     if(config.shadow){
7766         this.shadowOffset = config.shadowOffset || 4;
7767         this.shadow = new Roo.Shadow({
7768             offset : this.shadowOffset,
7769             mode : config.shadow
7770         });
7771     }else{
7772         this.shadowOffset = 0;
7773     }
7774     this.useShim = config.shim !== false && Roo.useShims;
7775     this.useDisplay = config.useDisplay;
7776     this.hide();
7777 };
7778
7779 var supr = Roo.Element.prototype;
7780
7781 // shims are shared among layer to keep from having 100 iframes
7782 var shims = [];
7783
7784 Roo.extend(Roo.Layer, Roo.Element, {
7785
7786     getZIndex : function(){
7787         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7788     },
7789
7790     getShim : function(){
7791         if(!this.useShim){
7792             return null;
7793         }
7794         if(this.shim){
7795             return this.shim;
7796         }
7797         var shim = shims.shift();
7798         if(!shim){
7799             shim = this.createShim();
7800             shim.enableDisplayMode('block');
7801             shim.dom.style.display = 'none';
7802             shim.dom.style.visibility = 'visible';
7803         }
7804         var pn = this.dom.parentNode;
7805         if(shim.dom.parentNode != pn){
7806             pn.insertBefore(shim.dom, this.dom);
7807         }
7808         shim.setStyle('z-index', this.getZIndex()-2);
7809         this.shim = shim;
7810         return shim;
7811     },
7812
7813     hideShim : function(){
7814         if(this.shim){
7815             this.shim.setDisplayed(false);
7816             shims.push(this.shim);
7817             delete this.shim;
7818         }
7819     },
7820
7821     disableShadow : function(){
7822         if(this.shadow){
7823             this.shadowDisabled = true;
7824             this.shadow.hide();
7825             this.lastShadowOffset = this.shadowOffset;
7826             this.shadowOffset = 0;
7827         }
7828     },
7829
7830     enableShadow : function(show){
7831         if(this.shadow){
7832             this.shadowDisabled = false;
7833             this.shadowOffset = this.lastShadowOffset;
7834             delete this.lastShadowOffset;
7835             if(show){
7836                 this.sync(true);
7837             }
7838         }
7839     },
7840
7841     // private
7842     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7843     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7844     sync : function(doShow){
7845         var sw = this.shadow;
7846         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7847             var sh = this.getShim();
7848
7849             var w = this.getWidth(),
7850                 h = this.getHeight();
7851
7852             var l = this.getLeft(true),
7853                 t = this.getTop(true);
7854
7855             if(sw && !this.shadowDisabled){
7856                 if(doShow && !sw.isVisible()){
7857                     sw.show(this);
7858                 }else{
7859                     sw.realign(l, t, w, h);
7860                 }
7861                 if(sh){
7862                     if(doShow){
7863                        sh.show();
7864                     }
7865                     // fit the shim behind the shadow, so it is shimmed too
7866                     var a = sw.adjusts, s = sh.dom.style;
7867                     s.left = (Math.min(l, l+a.l))+"px";
7868                     s.top = (Math.min(t, t+a.t))+"px";
7869                     s.width = (w+a.w)+"px";
7870                     s.height = (h+a.h)+"px";
7871                 }
7872             }else if(sh){
7873                 if(doShow){
7874                    sh.show();
7875                 }
7876                 sh.setSize(w, h);
7877                 sh.setLeftTop(l, t);
7878             }
7879             
7880         }
7881     },
7882
7883     // private
7884     destroy : function(){
7885         this.hideShim();
7886         if(this.shadow){
7887             this.shadow.hide();
7888         }
7889         this.removeAllListeners();
7890         var pn = this.dom.parentNode;
7891         if(pn){
7892             pn.removeChild(this.dom);
7893         }
7894         Roo.Element.uncache(this.id);
7895     },
7896
7897     remove : function(){
7898         this.destroy();
7899     },
7900
7901     // private
7902     beginUpdate : function(){
7903         this.updating = true;
7904     },
7905
7906     // private
7907     endUpdate : function(){
7908         this.updating = false;
7909         this.sync(true);
7910     },
7911
7912     // private
7913     hideUnders : function(negOffset){
7914         if(this.shadow){
7915             this.shadow.hide();
7916         }
7917         this.hideShim();
7918     },
7919
7920     // private
7921     constrainXY : function(){
7922         if(this.constrain){
7923             var vw = Roo.lib.Dom.getViewWidth(),
7924                 vh = Roo.lib.Dom.getViewHeight();
7925             var s = Roo.get(document).getScroll();
7926
7927             var xy = this.getXY();
7928             var x = xy[0], y = xy[1];   
7929             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7930             // only move it if it needs it
7931             var moved = false;
7932             // first validate right/bottom
7933             if((x + w) > vw+s.left){
7934                 x = vw - w - this.shadowOffset;
7935                 moved = true;
7936             }
7937             if((y + h) > vh+s.top){
7938                 y = vh - h - this.shadowOffset;
7939                 moved = true;
7940             }
7941             // then make sure top/left isn't negative
7942             if(x < s.left){
7943                 x = s.left;
7944                 moved = true;
7945             }
7946             if(y < s.top){
7947                 y = s.top;
7948                 moved = true;
7949             }
7950             if(moved){
7951                 if(this.avoidY){
7952                     var ay = this.avoidY;
7953                     if(y <= ay && (y+h) >= ay){
7954                         y = ay-h-5;   
7955                     }
7956                 }
7957                 xy = [x, y];
7958                 this.storeXY(xy);
7959                 supr.setXY.call(this, xy);
7960                 this.sync();
7961             }
7962         }
7963     },
7964
7965     isVisible : function(){
7966         return this.visible;    
7967     },
7968
7969     // private
7970     showAction : function(){
7971         this.visible = true; // track visibility to prevent getStyle calls
7972         if(this.useDisplay === true){
7973             this.setDisplayed("");
7974         }else if(this.lastXY){
7975             supr.setXY.call(this, this.lastXY);
7976         }else if(this.lastLT){
7977             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7978         }
7979     },
7980
7981     // private
7982     hideAction : function(){
7983         this.visible = false;
7984         if(this.useDisplay === true){
7985             this.setDisplayed(false);
7986         }else{
7987             this.setLeftTop(-10000,-10000);
7988         }
7989     },
7990
7991     // overridden Element method
7992     setVisible : function(v, a, d, c, e){
7993         if(v){
7994             this.showAction();
7995         }
7996         if(a && v){
7997             var cb = function(){
7998                 this.sync(true);
7999                 if(c){
8000                     c();
8001                 }
8002             }.createDelegate(this);
8003             supr.setVisible.call(this, true, true, d, cb, e);
8004         }else{
8005             if(!v){
8006                 this.hideUnders(true);
8007             }
8008             var cb = c;
8009             if(a){
8010                 cb = function(){
8011                     this.hideAction();
8012                     if(c){
8013                         c();
8014                     }
8015                 }.createDelegate(this);
8016             }
8017             supr.setVisible.call(this, v, a, d, cb, e);
8018             if(v){
8019                 this.sync(true);
8020             }else if(!a){
8021                 this.hideAction();
8022             }
8023         }
8024     },
8025
8026     storeXY : function(xy){
8027         delete this.lastLT;
8028         this.lastXY = xy;
8029     },
8030
8031     storeLeftTop : function(left, top){
8032         delete this.lastXY;
8033         this.lastLT = [left, top];
8034     },
8035
8036     // private
8037     beforeFx : function(){
8038         this.beforeAction();
8039         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
8040     },
8041
8042     // private
8043     afterFx : function(){
8044         Roo.Layer.superclass.afterFx.apply(this, arguments);
8045         this.sync(this.isVisible());
8046     },
8047
8048     // private
8049     beforeAction : function(){
8050         if(!this.updating && this.shadow){
8051             this.shadow.hide();
8052         }
8053     },
8054
8055     // overridden Element method
8056     setLeft : function(left){
8057         this.storeLeftTop(left, this.getTop(true));
8058         supr.setLeft.apply(this, arguments);
8059         this.sync();
8060     },
8061
8062     setTop : function(top){
8063         this.storeLeftTop(this.getLeft(true), top);
8064         supr.setTop.apply(this, arguments);
8065         this.sync();
8066     },
8067
8068     setLeftTop : function(left, top){
8069         this.storeLeftTop(left, top);
8070         supr.setLeftTop.apply(this, arguments);
8071         this.sync();
8072     },
8073
8074     setXY : function(xy, a, d, c, e){
8075         this.fixDisplay();
8076         this.beforeAction();
8077         this.storeXY(xy);
8078         var cb = this.createCB(c);
8079         supr.setXY.call(this, xy, a, d, cb, e);
8080         if(!a){
8081             cb();
8082         }
8083     },
8084
8085     // private
8086     createCB : function(c){
8087         var el = this;
8088         return function(){
8089             el.constrainXY();
8090             el.sync(true);
8091             if(c){
8092                 c();
8093             }
8094         };
8095     },
8096
8097     // overridden Element method
8098     setX : function(x, a, d, c, e){
8099         this.setXY([x, this.getY()], a, d, c, e);
8100     },
8101
8102     // overridden Element method
8103     setY : function(y, a, d, c, e){
8104         this.setXY([this.getX(), y], a, d, c, e);
8105     },
8106
8107     // overridden Element method
8108     setSize : function(w, h, a, d, c, e){
8109         this.beforeAction();
8110         var cb = this.createCB(c);
8111         supr.setSize.call(this, w, h, a, d, cb, e);
8112         if(!a){
8113             cb();
8114         }
8115     },
8116
8117     // overridden Element method
8118     setWidth : function(w, a, d, c, e){
8119         this.beforeAction();
8120         var cb = this.createCB(c);
8121         supr.setWidth.call(this, w, a, d, cb, e);
8122         if(!a){
8123             cb();
8124         }
8125     },
8126
8127     // overridden Element method
8128     setHeight : function(h, a, d, c, e){
8129         this.beforeAction();
8130         var cb = this.createCB(c);
8131         supr.setHeight.call(this, h, a, d, cb, e);
8132         if(!a){
8133             cb();
8134         }
8135     },
8136
8137     // overridden Element method
8138     setBounds : function(x, y, w, h, a, d, c, e){
8139         this.beforeAction();
8140         var cb = this.createCB(c);
8141         if(!a){
8142             this.storeXY([x, y]);
8143             supr.setXY.call(this, [x, y]);
8144             supr.setSize.call(this, w, h, a, d, cb, e);
8145             cb();
8146         }else{
8147             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8148         }
8149         return this;
8150     },
8151     
8152     /**
8153      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8154      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8155      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8156      * @param {Number} zindex The new z-index to set
8157      * @return {this} The Layer
8158      */
8159     setZIndex : function(zindex){
8160         this.zindex = zindex;
8161         this.setStyle("z-index", zindex + 2);
8162         if(this.shadow){
8163             this.shadow.setZIndex(zindex + 1);
8164         }
8165         if(this.shim){
8166             this.shim.setStyle("z-index", zindex);
8167         }
8168     }
8169 });
8170 })();/*
8171  * Based on:
8172  * Ext JS Library 1.1.1
8173  * Copyright(c) 2006-2007, Ext JS, LLC.
8174  *
8175  * Originally Released Under LGPL - original licence link has changed is not relivant.
8176  *
8177  * Fork - LGPL
8178  * <script type="text/javascript">
8179  */
8180
8181
8182 /**
8183  * @class Roo.Shadow
8184  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8185  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8186  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8187  * @constructor
8188  * Create a new Shadow
8189  * @param {Object} config The config object
8190  */
8191 Roo.Shadow = function(config){
8192     Roo.apply(this, config);
8193     if(typeof this.mode != "string"){
8194         this.mode = this.defaultMode;
8195     }
8196     var o = this.offset, a = {h: 0};
8197     var rad = Math.floor(this.offset/2);
8198     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8199         case "drop":
8200             a.w = 0;
8201             a.l = a.t = o;
8202             a.t -= 1;
8203             if(Roo.isIE){
8204                 a.l -= this.offset + rad;
8205                 a.t -= this.offset + rad;
8206                 a.w -= rad;
8207                 a.h -= rad;
8208                 a.t += 1;
8209             }
8210         break;
8211         case "sides":
8212             a.w = (o*2);
8213             a.l = -o;
8214             a.t = o-1;
8215             if(Roo.isIE){
8216                 a.l -= (this.offset - rad);
8217                 a.t -= this.offset + rad;
8218                 a.l += 1;
8219                 a.w -= (this.offset - rad)*2;
8220                 a.w -= rad + 1;
8221                 a.h -= 1;
8222             }
8223         break;
8224         case "frame":
8225             a.w = a.h = (o*2);
8226             a.l = a.t = -o;
8227             a.t += 1;
8228             a.h -= 2;
8229             if(Roo.isIE){
8230                 a.l -= (this.offset - rad);
8231                 a.t -= (this.offset - rad);
8232                 a.l += 1;
8233                 a.w -= (this.offset + rad + 1);
8234                 a.h -= (this.offset + rad);
8235                 a.h += 1;
8236             }
8237         break;
8238     };
8239
8240     this.adjusts = a;
8241 };
8242
8243 Roo.Shadow.prototype = {
8244     /**
8245      * @cfg {String} mode
8246      * The shadow display mode.  Supports the following options:<br />
8247      * sides: Shadow displays on both sides and bottom only<br />
8248      * frame: Shadow displays equally on all four sides<br />
8249      * drop: Traditional bottom-right drop shadow (default)
8250      */
8251     /**
8252      * @cfg {String} offset
8253      * The number of pixels to offset the shadow from the element (defaults to 4)
8254      */
8255     offset: 4,
8256
8257     // private
8258     defaultMode: "drop",
8259
8260     /**
8261      * Displays the shadow under the target element
8262      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8263      */
8264     show : function(target){
8265         target = Roo.get(target);
8266         if(!this.el){
8267             this.el = Roo.Shadow.Pool.pull();
8268             if(this.el.dom.nextSibling != target.dom){
8269                 this.el.insertBefore(target);
8270             }
8271         }
8272         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8273         if(Roo.isIE){
8274             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8275         }
8276         this.realign(
8277             target.getLeft(true),
8278             target.getTop(true),
8279             target.getWidth(),
8280             target.getHeight()
8281         );
8282         this.el.dom.style.display = "block";
8283     },
8284
8285     /**
8286      * Returns true if the shadow is visible, else false
8287      */
8288     isVisible : function(){
8289         return this.el ? true : false;  
8290     },
8291
8292     /**
8293      * Direct alignment when values are already available. Show must be called at least once before
8294      * calling this method to ensure it is initialized.
8295      * @param {Number} left The target element left position
8296      * @param {Number} top The target element top position
8297      * @param {Number} width The target element width
8298      * @param {Number} height The target element height
8299      */
8300     realign : function(l, t, w, h){
8301         if(!this.el){
8302             return;
8303         }
8304         var a = this.adjusts, d = this.el.dom, s = d.style;
8305         var iea = 0;
8306         s.left = (l+a.l)+"px";
8307         s.top = (t+a.t)+"px";
8308         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8309  
8310         if(s.width != sws || s.height != shs){
8311             s.width = sws;
8312             s.height = shs;
8313             if(!Roo.isIE){
8314                 var cn = d.childNodes;
8315                 var sww = Math.max(0, (sw-12))+"px";
8316                 cn[0].childNodes[1].style.width = sww;
8317                 cn[1].childNodes[1].style.width = sww;
8318                 cn[2].childNodes[1].style.width = sww;
8319                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8320             }
8321         }
8322     },
8323
8324     /**
8325      * Hides this shadow
8326      */
8327     hide : function(){
8328         if(this.el){
8329             this.el.dom.style.display = "none";
8330             Roo.Shadow.Pool.push(this.el);
8331             delete this.el;
8332         }
8333     },
8334
8335     /**
8336      * Adjust the z-index of this shadow
8337      * @param {Number} zindex The new z-index
8338      */
8339     setZIndex : function(z){
8340         this.zIndex = z;
8341         if(this.el){
8342             this.el.setStyle("z-index", z);
8343         }
8344     }
8345 };
8346
8347 // Private utility class that manages the internal Shadow cache
8348 Roo.Shadow.Pool = function(){
8349     var p = [];
8350     var markup = Roo.isIE ?
8351                  '<div class="x-ie-shadow"></div>' :
8352                  '<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>';
8353     return {
8354         pull : function(){
8355             var sh = p.shift();
8356             if(!sh){
8357                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8358                 sh.autoBoxAdjust = false;
8359             }
8360             return sh;
8361         },
8362
8363         push : function(sh){
8364             p.push(sh);
8365         }
8366     };
8367 }();/*
8368  * Based on:
8369  * Ext JS Library 1.1.1
8370  * Copyright(c) 2006-2007, Ext JS, LLC.
8371  *
8372  * Originally Released Under LGPL - original licence link has changed is not relivant.
8373  *
8374  * Fork - LGPL
8375  * <script type="text/javascript">
8376  */
8377
8378 /**
8379  * @class Roo.BoxComponent
8380  * @extends Roo.Component
8381  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8382  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8383  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8384  * layout containers.
8385  * @constructor
8386  * @param {Roo.Element/String/Object} config The configuration options.
8387  */
8388 Roo.BoxComponent = function(config){
8389     Roo.Component.call(this, config);
8390     this.addEvents({
8391         /**
8392          * @event resize
8393          * Fires after the component is resized.
8394              * @param {Roo.Component} this
8395              * @param {Number} adjWidth The box-adjusted width that was set
8396              * @param {Number} adjHeight The box-adjusted height that was set
8397              * @param {Number} rawWidth The width that was originally specified
8398              * @param {Number} rawHeight The height that was originally specified
8399              */
8400         resize : true,
8401         /**
8402          * @event move
8403          * Fires after the component is moved.
8404              * @param {Roo.Component} this
8405              * @param {Number} x The new x position
8406              * @param {Number} y The new y position
8407              */
8408         move : true
8409     });
8410 };
8411
8412 Roo.extend(Roo.BoxComponent, Roo.Component, {
8413     // private, set in afterRender to signify that the component has been rendered
8414     boxReady : false,
8415     // private, used to defer height settings to subclasses
8416     deferHeight: false,
8417     /** @cfg {Number} width
8418      * width (optional) size of component
8419      */
8420      /** @cfg {Number} height
8421      * height (optional) size of component
8422      */
8423      
8424     /**
8425      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8426      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8427      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8428      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8429      * @return {Roo.BoxComponent} this
8430      */
8431     setSize : function(w, h){
8432         // support for standard size objects
8433         if(typeof w == 'object'){
8434             h = w.height;
8435             w = w.width;
8436         }
8437         // not rendered
8438         if(!this.boxReady){
8439             this.width = w;
8440             this.height = h;
8441             return this;
8442         }
8443
8444         // prevent recalcs when not needed
8445         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8446             return this;
8447         }
8448         this.lastSize = {width: w, height: h};
8449
8450         var adj = this.adjustSize(w, h);
8451         var aw = adj.width, ah = adj.height;
8452         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8453             var rz = this.getResizeEl();
8454             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8455                 rz.setSize(aw, ah);
8456             }else if(!this.deferHeight && ah !== undefined){
8457                 rz.setHeight(ah);
8458             }else if(aw !== undefined){
8459                 rz.setWidth(aw);
8460             }
8461             this.onResize(aw, ah, w, h);
8462             this.fireEvent('resize', this, aw, ah, w, h);
8463         }
8464         return this;
8465     },
8466
8467     /**
8468      * Gets the current size of the component's underlying element.
8469      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8470      */
8471     getSize : function(){
8472         return this.el.getSize();
8473     },
8474
8475     /**
8476      * Gets the current XY position of the component's underlying element.
8477      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8478      * @return {Array} The XY position of the element (e.g., [100, 200])
8479      */
8480     getPosition : function(local){
8481         if(local === true){
8482             return [this.el.getLeft(true), this.el.getTop(true)];
8483         }
8484         return this.xy || this.el.getXY();
8485     },
8486
8487     /**
8488      * Gets the current box measurements of the component's underlying element.
8489      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8490      * @returns {Object} box An object in the format {x, y, width, height}
8491      */
8492     getBox : function(local){
8493         var s = this.el.getSize();
8494         if(local){
8495             s.x = this.el.getLeft(true);
8496             s.y = this.el.getTop(true);
8497         }else{
8498             var xy = this.xy || this.el.getXY();
8499             s.x = xy[0];
8500             s.y = xy[1];
8501         }
8502         return s;
8503     },
8504
8505     /**
8506      * Sets the current box measurements of the component's underlying element.
8507      * @param {Object} box An object in the format {x, y, width, height}
8508      * @returns {Roo.BoxComponent} this
8509      */
8510     updateBox : function(box){
8511         this.setSize(box.width, box.height);
8512         this.setPagePosition(box.x, box.y);
8513         return this;
8514     },
8515
8516     // protected
8517     getResizeEl : function(){
8518         return this.resizeEl || this.el;
8519     },
8520
8521     // protected
8522     getPositionEl : function(){
8523         return this.positionEl || this.el;
8524     },
8525
8526     /**
8527      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8528      * This method fires the move event.
8529      * @param {Number} left The new left
8530      * @param {Number} top The new top
8531      * @returns {Roo.BoxComponent} this
8532      */
8533     setPosition : function(x, y){
8534         this.x = x;
8535         this.y = y;
8536         if(!this.boxReady){
8537             return this;
8538         }
8539         var adj = this.adjustPosition(x, y);
8540         var ax = adj.x, ay = adj.y;
8541
8542         var el = this.getPositionEl();
8543         if(ax !== undefined || ay !== undefined){
8544             if(ax !== undefined && ay !== undefined){
8545                 el.setLeftTop(ax, ay);
8546             }else if(ax !== undefined){
8547                 el.setLeft(ax);
8548             }else if(ay !== undefined){
8549                 el.setTop(ay);
8550             }
8551             this.onPosition(ax, ay);
8552             this.fireEvent('move', this, ax, ay);
8553         }
8554         return this;
8555     },
8556
8557     /**
8558      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8559      * This method fires the move event.
8560      * @param {Number} x The new x position
8561      * @param {Number} y The new y position
8562      * @returns {Roo.BoxComponent} this
8563      */
8564     setPagePosition : function(x, y){
8565         this.pageX = x;
8566         this.pageY = y;
8567         if(!this.boxReady){
8568             return;
8569         }
8570         if(x === undefined || y === undefined){ // cannot translate undefined points
8571             return;
8572         }
8573         var p = this.el.translatePoints(x, y);
8574         this.setPosition(p.left, p.top);
8575         return this;
8576     },
8577
8578     // private
8579     onRender : function(ct, position){
8580         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8581         if(this.resizeEl){
8582             this.resizeEl = Roo.get(this.resizeEl);
8583         }
8584         if(this.positionEl){
8585             this.positionEl = Roo.get(this.positionEl);
8586         }
8587     },
8588
8589     // private
8590     afterRender : function(){
8591         Roo.BoxComponent.superclass.afterRender.call(this);
8592         this.boxReady = true;
8593         this.setSize(this.width, this.height);
8594         if(this.x || this.y){
8595             this.setPosition(this.x, this.y);
8596         }
8597         if(this.pageX || this.pageY){
8598             this.setPagePosition(this.pageX, this.pageY);
8599         }
8600     },
8601
8602     /**
8603      * Force the component's size to recalculate based on the underlying element's current height and width.
8604      * @returns {Roo.BoxComponent} this
8605      */
8606     syncSize : function(){
8607         delete this.lastSize;
8608         this.setSize(this.el.getWidth(), this.el.getHeight());
8609         return this;
8610     },
8611
8612     /**
8613      * Called after the component is resized, this method is empty by default but can be implemented by any
8614      * subclass that needs to perform custom logic after a resize occurs.
8615      * @param {Number} adjWidth The box-adjusted width that was set
8616      * @param {Number} adjHeight The box-adjusted height that was set
8617      * @param {Number} rawWidth The width that was originally specified
8618      * @param {Number} rawHeight The height that was originally specified
8619      */
8620     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8621
8622     },
8623
8624     /**
8625      * Called after the component is moved, this method is empty by default but can be implemented by any
8626      * subclass that needs to perform custom logic after a move occurs.
8627      * @param {Number} x The new x position
8628      * @param {Number} y The new y position
8629      */
8630     onPosition : function(x, y){
8631
8632     },
8633
8634     // private
8635     adjustSize : function(w, h){
8636         if(this.autoWidth){
8637             w = 'auto';
8638         }
8639         if(this.autoHeight){
8640             h = 'auto';
8641         }
8642         return {width : w, height: h};
8643     },
8644
8645     // private
8646     adjustPosition : function(x, y){
8647         return {x : x, y: y};
8648     }
8649 });/*
8650  * Based on:
8651  * Ext JS Library 1.1.1
8652  * Copyright(c) 2006-2007, Ext JS, LLC.
8653  *
8654  * Originally Released Under LGPL - original licence link has changed is not relivant.
8655  *
8656  * Fork - LGPL
8657  * <script type="text/javascript">
8658  */
8659
8660
8661 /**
8662  * @class Roo.SplitBar
8663  * @extends Roo.util.Observable
8664  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8665  * <br><br>
8666  * Usage:
8667  * <pre><code>
8668 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8669                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8670 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8671 split.minSize = 100;
8672 split.maxSize = 600;
8673 split.animate = true;
8674 split.on('moved', splitterMoved);
8675 </code></pre>
8676  * @constructor
8677  * Create a new SplitBar
8678  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8679  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8680  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8681  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8682                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8683                         position of the SplitBar).
8684  */
8685 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8686     
8687     /** @private */
8688     this.el = Roo.get(dragElement, true);
8689     this.el.dom.unselectable = "on";
8690     /** @private */
8691     this.resizingEl = Roo.get(resizingElement, true);
8692
8693     /**
8694      * @private
8695      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8696      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8697      * @type Number
8698      */
8699     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8700     
8701     /**
8702      * The minimum size of the resizing element. (Defaults to 0)
8703      * @type Number
8704      */
8705     this.minSize = 0;
8706     
8707     /**
8708      * The maximum size of the resizing element. (Defaults to 2000)
8709      * @type Number
8710      */
8711     this.maxSize = 2000;
8712     
8713     /**
8714      * Whether to animate the transition to the new size
8715      * @type Boolean
8716      */
8717     this.animate = false;
8718     
8719     /**
8720      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8721      * @type Boolean
8722      */
8723     this.useShim = false;
8724     
8725     /** @private */
8726     this.shim = null;
8727     
8728     if(!existingProxy){
8729         /** @private */
8730         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8731     }else{
8732         this.proxy = Roo.get(existingProxy).dom;
8733     }
8734     /** @private */
8735     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8736     
8737     /** @private */
8738     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8739     
8740     /** @private */
8741     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8742     
8743     /** @private */
8744     this.dragSpecs = {};
8745     
8746     /**
8747      * @private The adapter to use to positon and resize elements
8748      */
8749     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8750     this.adapter.init(this);
8751     
8752     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8753         /** @private */
8754         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8755         this.el.addClass("x-splitbar-h");
8756     }else{
8757         /** @private */
8758         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8759         this.el.addClass("x-splitbar-v");
8760     }
8761     
8762     this.addEvents({
8763         /**
8764          * @event resize
8765          * Fires when the splitter is moved (alias for {@link #event-moved})
8766          * @param {Roo.SplitBar} this
8767          * @param {Number} newSize the new width or height
8768          */
8769         "resize" : true,
8770         /**
8771          * @event moved
8772          * Fires when the splitter is moved
8773          * @param {Roo.SplitBar} this
8774          * @param {Number} newSize the new width or height
8775          */
8776         "moved" : true,
8777         /**
8778          * @event beforeresize
8779          * Fires before the splitter is dragged
8780          * @param {Roo.SplitBar} this
8781          */
8782         "beforeresize" : true,
8783
8784         "beforeapply" : true
8785     });
8786
8787     Roo.util.Observable.call(this);
8788 };
8789
8790 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8791     onStartProxyDrag : function(x, y){
8792         this.fireEvent("beforeresize", this);
8793         if(!this.overlay){
8794             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8795             o.unselectable();
8796             o.enableDisplayMode("block");
8797             // all splitbars share the same overlay
8798             Roo.SplitBar.prototype.overlay = o;
8799         }
8800         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8801         this.overlay.show();
8802         Roo.get(this.proxy).setDisplayed("block");
8803         var size = this.adapter.getElementSize(this);
8804         this.activeMinSize = this.getMinimumSize();;
8805         this.activeMaxSize = this.getMaximumSize();;
8806         var c1 = size - this.activeMinSize;
8807         var c2 = Math.max(this.activeMaxSize - size, 0);
8808         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8809             this.dd.resetConstraints();
8810             this.dd.setXConstraint(
8811                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8812                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8813             );
8814             this.dd.setYConstraint(0, 0);
8815         }else{
8816             this.dd.resetConstraints();
8817             this.dd.setXConstraint(0, 0);
8818             this.dd.setYConstraint(
8819                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8820                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8821             );
8822          }
8823         this.dragSpecs.startSize = size;
8824         this.dragSpecs.startPoint = [x, y];
8825         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8826     },
8827     
8828     /** 
8829      * @private Called after the drag operation by the DDProxy
8830      */
8831     onEndProxyDrag : function(e){
8832         Roo.get(this.proxy).setDisplayed(false);
8833         var endPoint = Roo.lib.Event.getXY(e);
8834         if(this.overlay){
8835             this.overlay.hide();
8836         }
8837         var newSize;
8838         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8839             newSize = this.dragSpecs.startSize + 
8840                 (this.placement == Roo.SplitBar.LEFT ?
8841                     endPoint[0] - this.dragSpecs.startPoint[0] :
8842                     this.dragSpecs.startPoint[0] - endPoint[0]
8843                 );
8844         }else{
8845             newSize = this.dragSpecs.startSize + 
8846                 (this.placement == Roo.SplitBar.TOP ?
8847                     endPoint[1] - this.dragSpecs.startPoint[1] :
8848                     this.dragSpecs.startPoint[1] - endPoint[1]
8849                 );
8850         }
8851         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8852         if(newSize != this.dragSpecs.startSize){
8853             if(this.fireEvent('beforeapply', this, newSize) !== false){
8854                 this.adapter.setElementSize(this, newSize);
8855                 this.fireEvent("moved", this, newSize);
8856                 this.fireEvent("resize", this, newSize);
8857             }
8858         }
8859     },
8860     
8861     /**
8862      * Get the adapter this SplitBar uses
8863      * @return The adapter object
8864      */
8865     getAdapter : function(){
8866         return this.adapter;
8867     },
8868     
8869     /**
8870      * Set the adapter this SplitBar uses
8871      * @param {Object} adapter A SplitBar adapter object
8872      */
8873     setAdapter : function(adapter){
8874         this.adapter = adapter;
8875         this.adapter.init(this);
8876     },
8877     
8878     /**
8879      * Gets the minimum size for the resizing element
8880      * @return {Number} The minimum size
8881      */
8882     getMinimumSize : function(){
8883         return this.minSize;
8884     },
8885     
8886     /**
8887      * Sets the minimum size for the resizing element
8888      * @param {Number} minSize The minimum size
8889      */
8890     setMinimumSize : function(minSize){
8891         this.minSize = minSize;
8892     },
8893     
8894     /**
8895      * Gets the maximum size for the resizing element
8896      * @return {Number} The maximum size
8897      */
8898     getMaximumSize : function(){
8899         return this.maxSize;
8900     },
8901     
8902     /**
8903      * Sets the maximum size for the resizing element
8904      * @param {Number} maxSize The maximum size
8905      */
8906     setMaximumSize : function(maxSize){
8907         this.maxSize = maxSize;
8908     },
8909     
8910     /**
8911      * Sets the initialize size for the resizing element
8912      * @param {Number} size The initial size
8913      */
8914     setCurrentSize : function(size){
8915         var oldAnimate = this.animate;
8916         this.animate = false;
8917         this.adapter.setElementSize(this, size);
8918         this.animate = oldAnimate;
8919     },
8920     
8921     /**
8922      * Destroy this splitbar. 
8923      * @param {Boolean} removeEl True to remove the element
8924      */
8925     destroy : function(removeEl){
8926         if(this.shim){
8927             this.shim.remove();
8928         }
8929         this.dd.unreg();
8930         this.proxy.parentNode.removeChild(this.proxy);
8931         if(removeEl){
8932             this.el.remove();
8933         }
8934     }
8935 });
8936
8937 /**
8938  * @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.
8939  */
8940 Roo.SplitBar.createProxy = function(dir){
8941     var proxy = new Roo.Element(document.createElement("div"));
8942     proxy.unselectable();
8943     var cls = 'x-splitbar-proxy';
8944     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8945     document.body.appendChild(proxy.dom);
8946     return proxy.dom;
8947 };
8948
8949 /** 
8950  * @class Roo.SplitBar.BasicLayoutAdapter
8951  * Default Adapter. It assumes the splitter and resizing element are not positioned
8952  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8953  */
8954 Roo.SplitBar.BasicLayoutAdapter = function(){
8955 };
8956
8957 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8958     // do nothing for now
8959     init : function(s){
8960     
8961     },
8962     /**
8963      * Called before drag operations to get the current size of the resizing element. 
8964      * @param {Roo.SplitBar} s The SplitBar using this adapter
8965      */
8966      getElementSize : function(s){
8967         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8968             return s.resizingEl.getWidth();
8969         }else{
8970             return s.resizingEl.getHeight();
8971         }
8972     },
8973     
8974     /**
8975      * Called after drag operations to set the size of the resizing element.
8976      * @param {Roo.SplitBar} s The SplitBar using this adapter
8977      * @param {Number} newSize The new size to set
8978      * @param {Function} onComplete A function to be invoked when resizing is complete
8979      */
8980     setElementSize : function(s, newSize, onComplete){
8981         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8982             if(!s.animate){
8983                 s.resizingEl.setWidth(newSize);
8984                 if(onComplete){
8985                     onComplete(s, newSize);
8986                 }
8987             }else{
8988                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8989             }
8990         }else{
8991             
8992             if(!s.animate){
8993                 s.resizingEl.setHeight(newSize);
8994                 if(onComplete){
8995                     onComplete(s, newSize);
8996                 }
8997             }else{
8998                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
8999             }
9000         }
9001     }
9002 };
9003
9004 /** 
9005  *@class Roo.SplitBar.AbsoluteLayoutAdapter
9006  * @extends Roo.SplitBar.BasicLayoutAdapter
9007  * Adapter that  moves the splitter element to align with the resized sizing element. 
9008  * Used with an absolute positioned SplitBar.
9009  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
9010  * document.body, make sure you assign an id to the body element.
9011  */
9012 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
9013     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
9014     this.container = Roo.get(container);
9015 };
9016
9017 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
9018     init : function(s){
9019         this.basic.init(s);
9020     },
9021     
9022     getElementSize : function(s){
9023         return this.basic.getElementSize(s);
9024     },
9025     
9026     setElementSize : function(s, newSize, onComplete){
9027         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
9028     },
9029     
9030     moveSplitter : function(s){
9031         var yes = Roo.SplitBar;
9032         switch(s.placement){
9033             case yes.LEFT:
9034                 s.el.setX(s.resizingEl.getRight());
9035                 break;
9036             case yes.RIGHT:
9037                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
9038                 break;
9039             case yes.TOP:
9040                 s.el.setY(s.resizingEl.getBottom());
9041                 break;
9042             case yes.BOTTOM:
9043                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
9044                 break;
9045         }
9046     }
9047 };
9048
9049 /**
9050  * Orientation constant - Create a vertical SplitBar
9051  * @static
9052  * @type Number
9053  */
9054 Roo.SplitBar.VERTICAL = 1;
9055
9056 /**
9057  * Orientation constant - Create a horizontal SplitBar
9058  * @static
9059  * @type Number
9060  */
9061 Roo.SplitBar.HORIZONTAL = 2;
9062
9063 /**
9064  * Placement constant - The resizing element is to the left of the splitter element
9065  * @static
9066  * @type Number
9067  */
9068 Roo.SplitBar.LEFT = 1;
9069
9070 /**
9071  * Placement constant - The resizing element is to the right of the splitter element
9072  * @static
9073  * @type Number
9074  */
9075 Roo.SplitBar.RIGHT = 2;
9076
9077 /**
9078  * Placement constant - The resizing element is positioned above the splitter element
9079  * @static
9080  * @type Number
9081  */
9082 Roo.SplitBar.TOP = 3;
9083
9084 /**
9085  * Placement constant - The resizing element is positioned under splitter element
9086  * @static
9087  * @type Number
9088  */
9089 Roo.SplitBar.BOTTOM = 4;
9090 /*
9091  * Based on:
9092  * Ext JS Library 1.1.1
9093  * Copyright(c) 2006-2007, Ext JS, LLC.
9094  *
9095  * Originally Released Under LGPL - original licence link has changed is not relivant.
9096  *
9097  * Fork - LGPL
9098  * <script type="text/javascript">
9099  */
9100
9101 /**
9102  * @class Roo.View
9103  * @extends Roo.util.Observable
9104  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9105  * This class also supports single and multi selection modes. <br>
9106  * Create a data model bound view:
9107  <pre><code>
9108  var store = new Roo.data.Store(...);
9109
9110  var view = new Roo.View({
9111     el : "my-element",
9112     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9113  
9114     singleSelect: true,
9115     selectedClass: "ydataview-selected",
9116     store: store
9117  });
9118
9119  // listen for node click?
9120  view.on("click", function(vw, index, node, e){
9121  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9122  });
9123
9124  // load XML data
9125  dataModel.load("foobar.xml");
9126  </code></pre>
9127  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9128  * <br><br>
9129  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9130  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9131  * 
9132  * Note: old style constructor is still suported (container, template, config)
9133  * 
9134  * @constructor
9135  * Create a new View
9136  * @param {Object} config The config object
9137  * 
9138  */
9139 Roo.View = function(config, depreciated_tpl, depreciated_config){
9140     
9141     if (typeof(depreciated_tpl) == 'undefined') {
9142         // new way.. - universal constructor.
9143         Roo.apply(this, config);
9144         this.el  = Roo.get(this.el);
9145     } else {
9146         // old format..
9147         this.el  = Roo.get(config);
9148         this.tpl = depreciated_tpl;
9149         Roo.apply(this, depreciated_config);
9150     }
9151      
9152     
9153     if(typeof(this.tpl) == "string"){
9154         this.tpl = new Roo.Template(this.tpl);
9155     } else {
9156         // support xtype ctors..
9157         this.tpl = new Roo.factory(this.tpl, Roo);
9158     }
9159     
9160     
9161     this.tpl.compile();
9162    
9163
9164      
9165     /** @private */
9166     this.addEvents({
9167         /**
9168          * @event beforeclick
9169          * Fires before a click is processed. Returns false to cancel the default action.
9170          * @param {Roo.View} this
9171          * @param {Number} index The index of the target node
9172          * @param {HTMLElement} node The target node
9173          * @param {Roo.EventObject} e The raw event object
9174          */
9175             "beforeclick" : true,
9176         /**
9177          * @event click
9178          * Fires when a template node is clicked.
9179          * @param {Roo.View} this
9180          * @param {Number} index The index of the target node
9181          * @param {HTMLElement} node The target node
9182          * @param {Roo.EventObject} e The raw event object
9183          */
9184             "click" : true,
9185         /**
9186          * @event dblclick
9187          * Fires when a template node is double clicked.
9188          * @param {Roo.View} this
9189          * @param {Number} index The index of the target node
9190          * @param {HTMLElement} node The target node
9191          * @param {Roo.EventObject} e The raw event object
9192          */
9193             "dblclick" : true,
9194         /**
9195          * @event contextmenu
9196          * Fires when a template node is right clicked.
9197          * @param {Roo.View} this
9198          * @param {Number} index The index of the target node
9199          * @param {HTMLElement} node The target node
9200          * @param {Roo.EventObject} e The raw event object
9201          */
9202             "contextmenu" : true,
9203         /**
9204          * @event selectionchange
9205          * Fires when the selected nodes change.
9206          * @param {Roo.View} this
9207          * @param {Array} selections Array of the selected nodes
9208          */
9209             "selectionchange" : true,
9210     
9211         /**
9212          * @event beforeselect
9213          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9214          * @param {Roo.View} this
9215          * @param {HTMLElement} node The node to be selected
9216          * @param {Array} selections Array of currently selected nodes
9217          */
9218             "beforeselect" : true,
9219         /**
9220          * @event preparedata
9221          * Fires on every row to render, to allow you to change the data.
9222          * @param {Roo.View} this
9223          * @param {Object} data to be rendered (change this)
9224          */
9225           "preparedata" : true
9226         });
9227
9228     this.el.on({
9229         "click": this.onClick,
9230         "dblclick": this.onDblClick,
9231         "contextmenu": this.onContextMenu,
9232         scope:this
9233     });
9234
9235     this.selections = [];
9236     this.nodes = [];
9237     this.cmp = new Roo.CompositeElementLite([]);
9238     if(this.store){
9239         this.store = Roo.factory(this.store, Roo.data);
9240         this.setStore(this.store, true);
9241     }
9242     Roo.View.superclass.constructor.call(this);
9243 };
9244
9245 Roo.extend(Roo.View, Roo.util.Observable, {
9246     
9247      /**
9248      * @cfg {Roo.data.Store} store Data store to load data from.
9249      */
9250     store : false,
9251     
9252     /**
9253      * @cfg {String|Roo.Element} el The container element.
9254      */
9255     el : '',
9256     
9257     /**
9258      * @cfg {String|Roo.Template} tpl The template used by this View 
9259      */
9260     tpl : false,
9261     
9262     /**
9263      * @cfg {String} selectedClass The css class to add to selected nodes
9264      */
9265     selectedClass : "x-view-selected",
9266      /**
9267      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9268      */
9269     emptyText : "",
9270     /**
9271      * @cfg {Boolean} multiSelect Allow multiple selection
9272      */
9273     multiSelect : false,
9274     /**
9275      * @cfg {Boolean} singleSelect Allow single selection
9276      */
9277     singleSelect:  false,
9278     
9279     /**
9280      * @cfg {Boolean} toggleSelect - selecting 
9281      */
9282     toggleSelect : false,
9283     
9284     /**
9285      * Returns the element this view is bound to.
9286      * @return {Roo.Element}
9287      */
9288     getEl : function(){
9289         return this.el;
9290     },
9291
9292     /**
9293      * Refreshes the view.
9294      */
9295     refresh : function(){
9296         var t = this.tpl;
9297         this.clearSelections();
9298         this.el.update("");
9299         var html = [];
9300         var records = this.store.getRange();
9301         if(records.length < 1){
9302             this.el.update(this.emptyText);
9303             return;
9304         }
9305         for(var i = 0, len = records.length; i < len; i++){
9306             var data = this.prepareData(records[i].data, i, records[i]);
9307             this.fireEvent("preparedata", this, data, i, records[i]);
9308             html[html.length] = t.apply(data);
9309         }
9310         this.el.update(html.join(""));
9311         this.nodes = this.el.dom.childNodes;
9312         this.updateIndexes(0);
9313     },
9314
9315     /**
9316      * Function to override to reformat the data that is sent to
9317      * the template for each node.
9318      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9319      * a JSON object for an UpdateManager bound view).
9320      */
9321     prepareData : function(data){
9322         return data;
9323     },
9324
9325     onUpdate : function(ds, record){
9326         this.clearSelections();
9327         var index = this.store.indexOf(record);
9328         var n = this.nodes[index];
9329         this.tpl.insertBefore(n, this.prepareData(record.data));
9330         n.parentNode.removeChild(n);
9331         this.updateIndexes(index, index);
9332     },
9333
9334     onAdd : function(ds, records, index){
9335         this.clearSelections();
9336         if(this.nodes.length == 0){
9337             this.refresh();
9338             return;
9339         }
9340         var n = this.nodes[index];
9341         for(var i = 0, len = records.length; i < len; i++){
9342             var d = this.prepareData(records[i].data);
9343             if(n){
9344                 this.tpl.insertBefore(n, d);
9345             }else{
9346                 this.tpl.append(this.el, d);
9347             }
9348         }
9349         this.updateIndexes(index);
9350     },
9351
9352     onRemove : function(ds, record, index){
9353         this.clearSelections();
9354         this.el.dom.removeChild(this.nodes[index]);
9355         this.updateIndexes(index);
9356     },
9357
9358     /**
9359      * Refresh an individual node.
9360      * @param {Number} index
9361      */
9362     refreshNode : function(index){
9363         this.onUpdate(this.store, this.store.getAt(index));
9364     },
9365
9366     updateIndexes : function(startIndex, endIndex){
9367         var ns = this.nodes;
9368         startIndex = startIndex || 0;
9369         endIndex = endIndex || ns.length - 1;
9370         for(var i = startIndex; i <= endIndex; i++){
9371             ns[i].nodeIndex = i;
9372         }
9373     },
9374
9375     /**
9376      * Changes the data store this view uses and refresh the view.
9377      * @param {Store} store
9378      */
9379     setStore : function(store, initial){
9380         if(!initial && this.store){
9381             this.store.un("datachanged", this.refresh);
9382             this.store.un("add", this.onAdd);
9383             this.store.un("remove", this.onRemove);
9384             this.store.un("update", this.onUpdate);
9385             this.store.un("clear", this.refresh);
9386         }
9387         if(store){
9388           
9389             store.on("datachanged", this.refresh, this);
9390             store.on("add", this.onAdd, this);
9391             store.on("remove", this.onRemove, this);
9392             store.on("update", this.onUpdate, this);
9393             store.on("clear", this.refresh, this);
9394         }
9395         
9396         if(store){
9397             this.refresh();
9398         }
9399     },
9400
9401     /**
9402      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9403      * @param {HTMLElement} node
9404      * @return {HTMLElement} The template node
9405      */
9406     findItemFromChild : function(node){
9407         var el = this.el.dom;
9408         if(!node || node.parentNode == el){
9409                     return node;
9410             }
9411             var p = node.parentNode;
9412             while(p && p != el){
9413             if(p.parentNode == el){
9414                 return p;
9415             }
9416             p = p.parentNode;
9417         }
9418             return null;
9419     },
9420
9421     /** @ignore */
9422     onClick : function(e){
9423         var item = this.findItemFromChild(e.getTarget());
9424         if(item){
9425             var index = this.indexOf(item);
9426             if(this.onItemClick(item, index, e) !== false){
9427                 this.fireEvent("click", this, index, item, e);
9428             }
9429         }else{
9430             this.clearSelections();
9431         }
9432     },
9433
9434     /** @ignore */
9435     onContextMenu : function(e){
9436         var item = this.findItemFromChild(e.getTarget());
9437         if(item){
9438             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9439         }
9440     },
9441
9442     /** @ignore */
9443     onDblClick : function(e){
9444         var item = this.findItemFromChild(e.getTarget());
9445         if(item){
9446             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9447         }
9448     },
9449
9450     onItemClick : function(item, index, e)
9451     {
9452         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9453             return false;
9454         }
9455         if (this.toggleSelect) {
9456             var m = this.isSelected(item) ? 'unselect' : 'select';
9457             Roo.log(m);
9458             var _t = this;
9459             _t[m](item, true, false);
9460             return true;
9461         }
9462         if(this.multiSelect || this.singleSelect){
9463             if(this.multiSelect && e.shiftKey && this.lastSelection){
9464                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9465             }else{
9466                 this.select(item, this.multiSelect && e.ctrlKey);
9467                 this.lastSelection = item;
9468             }
9469             e.preventDefault();
9470         }
9471         return true;
9472     },
9473
9474     /**
9475      * Get the number of selected nodes.
9476      * @return {Number}
9477      */
9478     getSelectionCount : function(){
9479         return this.selections.length;
9480     },
9481
9482     /**
9483      * Get the currently selected nodes.
9484      * @return {Array} An array of HTMLElements
9485      */
9486     getSelectedNodes : function(){
9487         return this.selections;
9488     },
9489
9490     /**
9491      * Get the indexes of the selected nodes.
9492      * @return {Array}
9493      */
9494     getSelectedIndexes : function(){
9495         var indexes = [], s = this.selections;
9496         for(var i = 0, len = s.length; i < len; i++){
9497             indexes.push(s[i].nodeIndex);
9498         }
9499         return indexes;
9500     },
9501
9502     /**
9503      * Clear all selections
9504      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9505      */
9506     clearSelections : function(suppressEvent){
9507         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9508             this.cmp.elements = this.selections;
9509             this.cmp.removeClass(this.selectedClass);
9510             this.selections = [];
9511             if(!suppressEvent){
9512                 this.fireEvent("selectionchange", this, this.selections);
9513             }
9514         }
9515     },
9516
9517     /**
9518      * Returns true if the passed node is selected
9519      * @param {HTMLElement/Number} node The node or node index
9520      * @return {Boolean}
9521      */
9522     isSelected : function(node){
9523         var s = this.selections;
9524         if(s.length < 1){
9525             return false;
9526         }
9527         node = this.getNode(node);
9528         return s.indexOf(node) !== -1;
9529     },
9530
9531     /**
9532      * Selects nodes.
9533      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
9534      * @param {Boolean} keepExisting (optional) true to keep existing selections
9535      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9536      */
9537     select : function(nodeInfo, keepExisting, suppressEvent){
9538         if(nodeInfo instanceof Array){
9539             if(!keepExisting){
9540                 this.clearSelections(true);
9541             }
9542             for(var i = 0, len = nodeInfo.length; i < len; i++){
9543                 this.select(nodeInfo[i], true, true);
9544             }
9545             return;
9546         } 
9547         var node = this.getNode(nodeInfo);
9548         if(!node || this.isSelected(node)){
9549             return; // already selected.
9550         }
9551         if(!keepExisting){
9552             this.clearSelections(true);
9553         }
9554         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9555             Roo.fly(node).addClass(this.selectedClass);
9556             this.selections.push(node);
9557             if(!suppressEvent){
9558                 this.fireEvent("selectionchange", this, this.selections);
9559             }
9560         }
9561         
9562         
9563     },
9564       /**
9565      * Unselects nodes.
9566      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
9567      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9568      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9569      */
9570     unselect : function(nodeInfo, keepExisting, suppressEvent)
9571     {
9572         if(nodeInfo instanceof Array){
9573             Roo.each(this.selections, function(s) {
9574                 this.unselect(s, nodeInfo);
9575             }, this);
9576             return;
9577         }
9578         var node = this.getNode(nodeInfo);
9579         if(!node || !this.isSelected(node)){
9580             Roo.log("not selected");
9581             return; // not selected.
9582         }
9583         // fireevent???
9584         var ns = [];
9585         Roo.each(this.selections, function(s) {
9586             if (s == node ) {
9587                 Roo.fly(node).removeClass(this.selectedClass);
9588
9589                 return;
9590             }
9591             ns.push(s);
9592         },this);
9593         
9594         this.selections= ns;
9595         this.fireEvent("selectionchange", this, this.selections);
9596     },
9597
9598     /**
9599      * Gets a template node.
9600      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9601      * @return {HTMLElement} The node or null if it wasn't found
9602      */
9603     getNode : function(nodeInfo){
9604         if(typeof nodeInfo == "string"){
9605             return document.getElementById(nodeInfo);
9606         }else if(typeof nodeInfo == "number"){
9607             return this.nodes[nodeInfo];
9608         }
9609         return nodeInfo;
9610     },
9611
9612     /**
9613      * Gets a range template nodes.
9614      * @param {Number} startIndex
9615      * @param {Number} endIndex
9616      * @return {Array} An array of nodes
9617      */
9618     getNodes : function(start, end){
9619         var ns = this.nodes;
9620         start = start || 0;
9621         end = typeof end == "undefined" ? ns.length - 1 : end;
9622         var nodes = [];
9623         if(start <= end){
9624             for(var i = start; i <= end; i++){
9625                 nodes.push(ns[i]);
9626             }
9627         } else{
9628             for(var i = start; i >= end; i--){
9629                 nodes.push(ns[i]);
9630             }
9631         }
9632         return nodes;
9633     },
9634
9635     /**
9636      * Finds the index of the passed node
9637      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9638      * @return {Number} The index of the node or -1
9639      */
9640     indexOf : function(node){
9641         node = this.getNode(node);
9642         if(typeof node.nodeIndex == "number"){
9643             return node.nodeIndex;
9644         }
9645         var ns = this.nodes;
9646         for(var i = 0, len = ns.length; i < len; i++){
9647             if(ns[i] == node){
9648                 return i;
9649             }
9650         }
9651         return -1;
9652     }
9653 });
9654 /*
9655  * Based on:
9656  * Ext JS Library 1.1.1
9657  * Copyright(c) 2006-2007, Ext JS, LLC.
9658  *
9659  * Originally Released Under LGPL - original licence link has changed is not relivant.
9660  *
9661  * Fork - LGPL
9662  * <script type="text/javascript">
9663  */
9664
9665 /**
9666  * @class Roo.JsonView
9667  * @extends Roo.View
9668  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9669 <pre><code>
9670 var view = new Roo.JsonView({
9671     container: "my-element",
9672     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9673     multiSelect: true, 
9674     jsonRoot: "data" 
9675 });
9676
9677 // listen for node click?
9678 view.on("click", function(vw, index, node, e){
9679     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9680 });
9681
9682 // direct load of JSON data
9683 view.load("foobar.php");
9684
9685 // Example from my blog list
9686 var tpl = new Roo.Template(
9687     '&lt;div class="entry"&gt;' +
9688     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9689     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9690     "&lt;/div&gt;&lt;hr /&gt;"
9691 );
9692
9693 var moreView = new Roo.JsonView({
9694     container :  "entry-list", 
9695     template : tpl,
9696     jsonRoot: "posts"
9697 });
9698 moreView.on("beforerender", this.sortEntries, this);
9699 moreView.load({
9700     url: "/blog/get-posts.php",
9701     params: "allposts=true",
9702     text: "Loading Blog Entries..."
9703 });
9704 </code></pre>
9705
9706 * Note: old code is supported with arguments : (container, template, config)
9707
9708
9709  * @constructor
9710  * Create a new JsonView
9711  * 
9712  * @param {Object} config The config object
9713  * 
9714  */
9715 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9716     
9717     
9718     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9719
9720     var um = this.el.getUpdateManager();
9721     um.setRenderer(this);
9722     um.on("update", this.onLoad, this);
9723     um.on("failure", this.onLoadException, this);
9724
9725     /**
9726      * @event beforerender
9727      * Fires before rendering of the downloaded JSON data.
9728      * @param {Roo.JsonView} this
9729      * @param {Object} data The JSON data loaded
9730      */
9731     /**
9732      * @event load
9733      * Fires when data is loaded.
9734      * @param {Roo.JsonView} this
9735      * @param {Object} data The JSON data loaded
9736      * @param {Object} response The raw Connect response object
9737      */
9738     /**
9739      * @event loadexception
9740      * Fires when loading fails.
9741      * @param {Roo.JsonView} this
9742      * @param {Object} response The raw Connect response object
9743      */
9744     this.addEvents({
9745         'beforerender' : true,
9746         'load' : true,
9747         'loadexception' : true
9748     });
9749 };
9750 Roo.extend(Roo.JsonView, Roo.View, {
9751     /**
9752      * @type {String} The root property in the loaded JSON object that contains the data
9753      */
9754     jsonRoot : "",
9755
9756     /**
9757      * Refreshes the view.
9758      */
9759     refresh : function(){
9760         this.clearSelections();
9761         this.el.update("");
9762         var html = [];
9763         var o = this.jsonData;
9764         if(o && o.length > 0){
9765             for(var i = 0, len = o.length; i < len; i++){
9766                 var data = this.prepareData(o[i], i, o);
9767                 html[html.length] = this.tpl.apply(data);
9768             }
9769         }else{
9770             html.push(this.emptyText);
9771         }
9772         this.el.update(html.join(""));
9773         this.nodes = this.el.dom.childNodes;
9774         this.updateIndexes(0);
9775     },
9776
9777     /**
9778      * Performs an async HTTP request, and loads the JSON from the response. If <i>params</i> are specified it uses POST, otherwise it uses GET.
9779      * @param {Object/String/Function} url The URL for this request, or a function to call to get the URL, or a config object containing any of the following options:
9780      <pre><code>
9781      view.load({
9782          url: "your-url.php",
9783          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9784          callback: yourFunction,
9785          scope: yourObject, //(optional scope)
9786          discardUrl: false,
9787          nocache: false,
9788          text: "Loading...",
9789          timeout: 30,
9790          scripts: false
9791      });
9792      </code></pre>
9793      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9794      * are respectively shorthand for <i>disableCaching</i>, <i>indicatorText</i>, and <i>loadScripts</i> and are used to set their associated property on this UpdateManager instance.
9795      * @param {String/Object} params (optional) The parameters to pass, as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
9796      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9797      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
9798      */
9799     load : function(){
9800         var um = this.el.getUpdateManager();
9801         um.update.apply(um, arguments);
9802     },
9803
9804     render : function(el, response){
9805         this.clearSelections();
9806         this.el.update("");
9807         var o;
9808         try{
9809             o = Roo.util.JSON.decode(response.responseText);
9810             if(this.jsonRoot){
9811                 
9812                 o = o[this.jsonRoot];
9813             }
9814         } catch(e){
9815         }
9816         /**
9817          * The current JSON data or null
9818          */
9819         this.jsonData = o;
9820         this.beforeRender();
9821         this.refresh();
9822     },
9823
9824 /**
9825  * Get the number of records in the current JSON dataset
9826  * @return {Number}
9827  */
9828     getCount : function(){
9829         return this.jsonData ? this.jsonData.length : 0;
9830     },
9831
9832 /**
9833  * Returns the JSON object for the specified node(s)
9834  * @param {HTMLElement/Array} node The node or an array of nodes
9835  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9836  * you get the JSON object for the node
9837  */
9838     getNodeData : function(node){
9839         if(node instanceof Array){
9840             var data = [];
9841             for(var i = 0, len = node.length; i < len; i++){
9842                 data.push(this.getNodeData(node[i]));
9843             }
9844             return data;
9845         }
9846         return this.jsonData[this.indexOf(node)] || null;
9847     },
9848
9849     beforeRender : function(){
9850         this.snapshot = this.jsonData;
9851         if(this.sortInfo){
9852             this.sort.apply(this, this.sortInfo);
9853         }
9854         this.fireEvent("beforerender", this, this.jsonData);
9855     },
9856
9857     onLoad : function(el, o){
9858         this.fireEvent("load", this, this.jsonData, o);
9859     },
9860
9861     onLoadException : function(el, o){
9862         this.fireEvent("loadexception", this, o);
9863     },
9864
9865 /**
9866  * Filter the data by a specific property.
9867  * @param {String} property A property on your JSON objects
9868  * @param {String/RegExp} value Either string that the property values
9869  * should start with, or a RegExp to test against the property
9870  */
9871     filter : function(property, value){
9872         if(this.jsonData){
9873             var data = [];
9874             var ss = this.snapshot;
9875             if(typeof value == "string"){
9876                 var vlen = value.length;
9877                 if(vlen == 0){
9878                     this.clearFilter();
9879                     return;
9880                 }
9881                 value = value.toLowerCase();
9882                 for(var i = 0, len = ss.length; i < len; i++){
9883                     var o = ss[i];
9884                     if(o[property].substr(0, vlen).toLowerCase() == value){
9885                         data.push(o);
9886                     }
9887                 }
9888             } else if(value.exec){ // regex?
9889                 for(var i = 0, len = ss.length; i < len; i++){
9890                     var o = ss[i];
9891                     if(value.test(o[property])){
9892                         data.push(o);
9893                     }
9894                 }
9895             } else{
9896                 return;
9897             }
9898             this.jsonData = data;
9899             this.refresh();
9900         }
9901     },
9902
9903 /**
9904  * Filter by a function. The passed function will be called with each
9905  * object in the current dataset. If the function returns true the value is kept,
9906  * otherwise it is filtered.
9907  * @param {Function} fn
9908  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9909  */
9910     filterBy : function(fn, scope){
9911         if(this.jsonData){
9912             var data = [];
9913             var ss = this.snapshot;
9914             for(var i = 0, len = ss.length; i < len; i++){
9915                 var o = ss[i];
9916                 if(fn.call(scope || this, o)){
9917                     data.push(o);
9918                 }
9919             }
9920             this.jsonData = data;
9921             this.refresh();
9922         }
9923     },
9924
9925 /**
9926  * Clears the current filter.
9927  */
9928     clearFilter : function(){
9929         if(this.snapshot && this.jsonData != this.snapshot){
9930             this.jsonData = this.snapshot;
9931             this.refresh();
9932         }
9933     },
9934
9935
9936 /**
9937  * Sorts the data for this view and refreshes it.
9938  * @param {String} property A property on your JSON objects to sort on
9939  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9940  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9941  */
9942     sort : function(property, dir, sortType){
9943         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9944         if(this.jsonData){
9945             var p = property;
9946             var dsc = dir && dir.toLowerCase() == "desc";
9947             var f = function(o1, o2){
9948                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9949                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9950                 ;
9951                 if(v1 < v2){
9952                     return dsc ? +1 : -1;
9953                 } else if(v1 > v2){
9954                     return dsc ? -1 : +1;
9955                 } else{
9956                     return 0;
9957                 }
9958             };
9959             this.jsonData.sort(f);
9960             this.refresh();
9961             if(this.jsonData != this.snapshot){
9962                 this.snapshot.sort(f);
9963             }
9964         }
9965     }
9966 });/*
9967  * Based on:
9968  * Ext JS Library 1.1.1
9969  * Copyright(c) 2006-2007, Ext JS, LLC.
9970  *
9971  * Originally Released Under LGPL - original licence link has changed is not relivant.
9972  *
9973  * Fork - LGPL
9974  * <script type="text/javascript">
9975  */
9976  
9977
9978 /**
9979  * @class Roo.ColorPalette
9980  * @extends Roo.Component
9981  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9982  * Here's an example of typical usage:
9983  * <pre><code>
9984 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9985 cp.render('my-div');
9986
9987 cp.on('select', function(palette, selColor){
9988     // do something with selColor
9989 });
9990 </code></pre>
9991  * @constructor
9992  * Create a new ColorPalette
9993  * @param {Object} config The config object
9994  */
9995 Roo.ColorPalette = function(config){
9996     Roo.ColorPalette.superclass.constructor.call(this, config);
9997     this.addEvents({
9998         /**
9999              * @event select
10000              * Fires when a color is selected
10001              * @param {ColorPalette} this
10002              * @param {String} color The 6-digit color hex code (without the # symbol)
10003              */
10004         select: true
10005     });
10006
10007     if(this.handler){
10008         this.on("select", this.handler, this.scope, true);
10009     }
10010 };
10011 Roo.extend(Roo.ColorPalette, Roo.Component, {
10012     /**
10013      * @cfg {String} itemCls
10014      * The CSS class to apply to the containing element (defaults to "x-color-palette")
10015      */
10016     itemCls : "x-color-palette",
10017     /**
10018      * @cfg {String} value
10019      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
10020      * the hex codes are case-sensitive.
10021      */
10022     value : null,
10023     clickEvent:'click',
10024     // private
10025     ctype: "Roo.ColorPalette",
10026
10027     /**
10028      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
10029      */
10030     allowReselect : false,
10031
10032     /**
10033      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
10034      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
10035      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
10036      * of colors with the width setting until the box is symmetrical.</p>
10037      * <p>You can override individual colors if needed:</p>
10038      * <pre><code>
10039 var cp = new Roo.ColorPalette();
10040 cp.colors[0] = "FF0000";  // change the first box to red
10041 </code></pre>
10042
10043 Or you can provide a custom array of your own for complete control:
10044 <pre><code>
10045 var cp = new Roo.ColorPalette();
10046 cp.colors = ["000000", "993300", "333300"];
10047 </code></pre>
10048      * @type Array
10049      */
10050     colors : [
10051         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
10052         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
10053         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
10054         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
10055         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
10056     ],
10057
10058     // private
10059     onRender : function(container, position){
10060         var t = new Roo.MasterTemplate(
10061             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
10062         );
10063         var c = this.colors;
10064         for(var i = 0, len = c.length; i < len; i++){
10065             t.add([c[i]]);
10066         }
10067         var el = document.createElement("div");
10068         el.className = this.itemCls;
10069         t.overwrite(el);
10070         container.dom.insertBefore(el, position);
10071         this.el = Roo.get(el);
10072         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
10073         if(this.clickEvent != 'click'){
10074             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
10075         }
10076     },
10077
10078     // private
10079     afterRender : function(){
10080         Roo.ColorPalette.superclass.afterRender.call(this);
10081         if(this.value){
10082             var s = this.value;
10083             this.value = null;
10084             this.select(s);
10085         }
10086     },
10087
10088     // private
10089     handleClick : function(e, t){
10090         e.preventDefault();
10091         if(!this.disabled){
10092             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
10093             this.select(c.toUpperCase());
10094         }
10095     },
10096
10097     /**
10098      * Selects the specified color in the palette (fires the select event)
10099      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10100      */
10101     select : function(color){
10102         color = color.replace("#", "");
10103         if(color != this.value || this.allowReselect){
10104             var el = this.el;
10105             if(this.value){
10106                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10107             }
10108             el.child("a.color-"+color).addClass("x-color-palette-sel");
10109             this.value = color;
10110             this.fireEvent("select", this, color);
10111         }
10112     }
10113 });/*
10114  * Based on:
10115  * Ext JS Library 1.1.1
10116  * Copyright(c) 2006-2007, Ext JS, LLC.
10117  *
10118  * Originally Released Under LGPL - original licence link has changed is not relivant.
10119  *
10120  * Fork - LGPL
10121  * <script type="text/javascript">
10122  */
10123  
10124 /**
10125  * @class Roo.DatePicker
10126  * @extends Roo.Component
10127  * Simple date picker class.
10128  * @constructor
10129  * Create a new DatePicker
10130  * @param {Object} config The config object
10131  */
10132 Roo.DatePicker = function(config){
10133     Roo.DatePicker.superclass.constructor.call(this, config);
10134
10135     this.value = config && config.value ?
10136                  config.value.clearTime() : new Date().clearTime();
10137
10138     this.addEvents({
10139         /**
10140              * @event select
10141              * Fires when a date is selected
10142              * @param {DatePicker} this
10143              * @param {Date} date The selected date
10144              */
10145         'select': true,
10146         /**
10147              * @event monthchange
10148              * Fires when the displayed month changes 
10149              * @param {DatePicker} this
10150              * @param {Date} date The selected month
10151              */
10152         'monthchange': true
10153     });
10154
10155     if(this.handler){
10156         this.on("select", this.handler,  this.scope || this);
10157     }
10158     // build the disabledDatesRE
10159     if(!this.disabledDatesRE && this.disabledDates){
10160         var dd = this.disabledDates;
10161         var re = "(?:";
10162         for(var i = 0; i < dd.length; i++){
10163             re += dd[i];
10164             if(i != dd.length-1) re += "|";
10165         }
10166         this.disabledDatesRE = new RegExp(re + ")");
10167     }
10168 };
10169
10170 Roo.extend(Roo.DatePicker, Roo.Component, {
10171     /**
10172      * @cfg {String} todayText
10173      * The text to display on the button that selects the current date (defaults to "Today")
10174      */
10175     todayText : "Today",
10176     /**
10177      * @cfg {String} okText
10178      * The text to display on the ok button
10179      */
10180     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10181     /**
10182      * @cfg {String} cancelText
10183      * The text to display on the cancel button
10184      */
10185     cancelText : "Cancel",
10186     /**
10187      * @cfg {String} todayTip
10188      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10189      */
10190     todayTip : "{0} (Spacebar)",
10191     /**
10192      * @cfg {Date} minDate
10193      * Minimum allowable date (JavaScript date object, defaults to null)
10194      */
10195     minDate : null,
10196     /**
10197      * @cfg {Date} maxDate
10198      * Maximum allowable date (JavaScript date object, defaults to null)
10199      */
10200     maxDate : null,
10201     /**
10202      * @cfg {String} minText
10203      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10204      */
10205     minText : "This date is before the minimum date",
10206     /**
10207      * @cfg {String} maxText
10208      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10209      */
10210     maxText : "This date is after the maximum date",
10211     /**
10212      * @cfg {String} format
10213      * The default date format string which can be overriden for localization support.  The format must be
10214      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10215      */
10216     format : "m/d/y",
10217     /**
10218      * @cfg {Array} disabledDays
10219      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10220      */
10221     disabledDays : null,
10222     /**
10223      * @cfg {String} disabledDaysText
10224      * The tooltip to display when the date falls on a disabled day (defaults to "")
10225      */
10226     disabledDaysText : "",
10227     /**
10228      * @cfg {RegExp} disabledDatesRE
10229      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10230      */
10231     disabledDatesRE : null,
10232     /**
10233      * @cfg {String} disabledDatesText
10234      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10235      */
10236     disabledDatesText : "",
10237     /**
10238      * @cfg {Boolean} constrainToViewport
10239      * True to constrain the date picker to the viewport (defaults to true)
10240      */
10241     constrainToViewport : true,
10242     /**
10243      * @cfg {Array} monthNames
10244      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10245      */
10246     monthNames : Date.monthNames,
10247     /**
10248      * @cfg {Array} dayNames
10249      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10250      */
10251     dayNames : Date.dayNames,
10252     /**
10253      * @cfg {String} nextText
10254      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10255      */
10256     nextText: 'Next Month (Control+Right)',
10257     /**
10258      * @cfg {String} prevText
10259      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10260      */
10261     prevText: 'Previous Month (Control+Left)',
10262     /**
10263      * @cfg {String} monthYearText
10264      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10265      */
10266     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10267     /**
10268      * @cfg {Number} startDay
10269      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10270      */
10271     startDay : 0,
10272     /**
10273      * @cfg {Bool} showClear
10274      * Show a clear button (usefull for date form elements that can be blank.)
10275      */
10276     
10277     showClear: false,
10278     
10279     /**
10280      * Sets the value of the date field
10281      * @param {Date} value The date to set
10282      */
10283     setValue : function(value){
10284         var old = this.value;
10285         this.value = value.clearTime(true);
10286         if(this.el){
10287             this.update(this.value);
10288         }
10289     },
10290
10291     /**
10292      * Gets the current selected value of the date field
10293      * @return {Date} The selected date
10294      */
10295     getValue : function(){
10296         return this.value;
10297     },
10298
10299     // private
10300     focus : function(){
10301         if(this.el){
10302             this.update(this.activeDate);
10303         }
10304     },
10305
10306     // private
10307     onRender : function(container, position){
10308         var m = [
10309              '<table cellspacing="0">',
10310                 '<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>',
10311                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10312         var dn = this.dayNames;
10313         for(var i = 0; i < 7; i++){
10314             var d = this.startDay+i;
10315             if(d > 6){
10316                 d = d-7;
10317             }
10318             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10319         }
10320         m[m.length] = "</tr></thead><tbody><tr>";
10321         for(var i = 0; i < 42; i++) {
10322             if(i % 7 == 0 && i != 0){
10323                 m[m.length] = "</tr><tr>";
10324             }
10325             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10326         }
10327         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10328             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10329
10330         var el = document.createElement("div");
10331         el.className = "x-date-picker";
10332         el.innerHTML = m.join("");
10333
10334         container.dom.insertBefore(el, position);
10335
10336         this.el = Roo.get(el);
10337         this.eventEl = Roo.get(el.firstChild);
10338
10339         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10340             handler: this.showPrevMonth,
10341             scope: this,
10342             preventDefault:true,
10343             stopDefault:true
10344         });
10345
10346         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10347             handler: this.showNextMonth,
10348             scope: this,
10349             preventDefault:true,
10350             stopDefault:true
10351         });
10352
10353         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10354
10355         this.monthPicker = this.el.down('div.x-date-mp');
10356         this.monthPicker.enableDisplayMode('block');
10357         
10358         var kn = new Roo.KeyNav(this.eventEl, {
10359             "left" : function(e){
10360                 e.ctrlKey ?
10361                     this.showPrevMonth() :
10362                     this.update(this.activeDate.add("d", -1));
10363             },
10364
10365             "right" : function(e){
10366                 e.ctrlKey ?
10367                     this.showNextMonth() :
10368                     this.update(this.activeDate.add("d", 1));
10369             },
10370
10371             "up" : function(e){
10372                 e.ctrlKey ?
10373                     this.showNextYear() :
10374                     this.update(this.activeDate.add("d", -7));
10375             },
10376
10377             "down" : function(e){
10378                 e.ctrlKey ?
10379                     this.showPrevYear() :
10380                     this.update(this.activeDate.add("d", 7));
10381             },
10382
10383             "pageUp" : function(e){
10384                 this.showNextMonth();
10385             },
10386
10387             "pageDown" : function(e){
10388                 this.showPrevMonth();
10389             },
10390
10391             "enter" : function(e){
10392                 e.stopPropagation();
10393                 return true;
10394             },
10395
10396             scope : this
10397         });
10398
10399         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10400
10401         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10402
10403         this.el.unselectable();
10404         
10405         this.cells = this.el.select("table.x-date-inner tbody td");
10406         this.textNodes = this.el.query("table.x-date-inner tbody span");
10407
10408         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10409             text: "&#160;",
10410             tooltip: this.monthYearText
10411         });
10412
10413         this.mbtn.on('click', this.showMonthPicker, this);
10414         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10415
10416
10417         var today = (new Date()).dateFormat(this.format);
10418         
10419         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10420         if (this.showClear) {
10421             baseTb.add( new Roo.Toolbar.Fill());
10422         }
10423         baseTb.add({
10424             text: String.format(this.todayText, today),
10425             tooltip: String.format(this.todayTip, today),
10426             handler: this.selectToday,
10427             scope: this
10428         });
10429         
10430         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10431             
10432         //});
10433         if (this.showClear) {
10434             
10435             baseTb.add( new Roo.Toolbar.Fill());
10436             baseTb.add({
10437                 text: '&#160;',
10438                 cls: 'x-btn-icon x-btn-clear',
10439                 handler: function() {
10440                     //this.value = '';
10441                     this.fireEvent("select", this, '');
10442                 },
10443                 scope: this
10444             });
10445         }
10446         
10447         
10448         if(Roo.isIE){
10449             this.el.repaint();
10450         }
10451         this.update(this.value);
10452     },
10453
10454     createMonthPicker : function(){
10455         if(!this.monthPicker.dom.firstChild){
10456             var buf = ['<table border="0" cellspacing="0">'];
10457             for(var i = 0; i < 6; i++){
10458                 buf.push(
10459                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10460                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10461                     i == 0 ?
10462                     '<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>' :
10463                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10464                 );
10465             }
10466             buf.push(
10467                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10468                     this.okText,
10469                     '</button><button type="button" class="x-date-mp-cancel">',
10470                     this.cancelText,
10471                     '</button></td></tr>',
10472                 '</table>'
10473             );
10474             this.monthPicker.update(buf.join(''));
10475             this.monthPicker.on('click', this.onMonthClick, this);
10476             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10477
10478             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10479             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10480
10481             this.mpMonths.each(function(m, a, i){
10482                 i += 1;
10483                 if((i%2) == 0){
10484                     m.dom.xmonth = 5 + Math.round(i * .5);
10485                 }else{
10486                     m.dom.xmonth = Math.round((i-1) * .5);
10487                 }
10488             });
10489         }
10490     },
10491
10492     showMonthPicker : function(){
10493         this.createMonthPicker();
10494         var size = this.el.getSize();
10495         this.monthPicker.setSize(size);
10496         this.monthPicker.child('table').setSize(size);
10497
10498         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10499         this.updateMPMonth(this.mpSelMonth);
10500         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10501         this.updateMPYear(this.mpSelYear);
10502
10503         this.monthPicker.slideIn('t', {duration:.2});
10504     },
10505
10506     updateMPYear : function(y){
10507         this.mpyear = y;
10508         var ys = this.mpYears.elements;
10509         for(var i = 1; i <= 10; i++){
10510             var td = ys[i-1], y2;
10511             if((i%2) == 0){
10512                 y2 = y + Math.round(i * .5);
10513                 td.firstChild.innerHTML = y2;
10514                 td.xyear = y2;
10515             }else{
10516                 y2 = y - (5-Math.round(i * .5));
10517                 td.firstChild.innerHTML = y2;
10518                 td.xyear = y2;
10519             }
10520             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10521         }
10522     },
10523
10524     updateMPMonth : function(sm){
10525         this.mpMonths.each(function(m, a, i){
10526             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10527         });
10528     },
10529
10530     selectMPMonth: function(m){
10531         
10532     },
10533
10534     onMonthClick : function(e, t){
10535         e.stopEvent();
10536         var el = new Roo.Element(t), pn;
10537         if(el.is('button.x-date-mp-cancel')){
10538             this.hideMonthPicker();
10539         }
10540         else if(el.is('button.x-date-mp-ok')){
10541             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10542             this.hideMonthPicker();
10543         }
10544         else if(pn = el.up('td.x-date-mp-month', 2)){
10545             this.mpMonths.removeClass('x-date-mp-sel');
10546             pn.addClass('x-date-mp-sel');
10547             this.mpSelMonth = pn.dom.xmonth;
10548         }
10549         else if(pn = el.up('td.x-date-mp-year', 2)){
10550             this.mpYears.removeClass('x-date-mp-sel');
10551             pn.addClass('x-date-mp-sel');
10552             this.mpSelYear = pn.dom.xyear;
10553         }
10554         else if(el.is('a.x-date-mp-prev')){
10555             this.updateMPYear(this.mpyear-10);
10556         }
10557         else if(el.is('a.x-date-mp-next')){
10558             this.updateMPYear(this.mpyear+10);
10559         }
10560     },
10561
10562     onMonthDblClick : function(e, t){
10563         e.stopEvent();
10564         var el = new Roo.Element(t), pn;
10565         if(pn = el.up('td.x-date-mp-month', 2)){
10566             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10567             this.hideMonthPicker();
10568         }
10569         else if(pn = el.up('td.x-date-mp-year', 2)){
10570             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10571             this.hideMonthPicker();
10572         }
10573     },
10574
10575     hideMonthPicker : function(disableAnim){
10576         if(this.monthPicker){
10577             if(disableAnim === true){
10578                 this.monthPicker.hide();
10579             }else{
10580                 this.monthPicker.slideOut('t', {duration:.2});
10581             }
10582         }
10583     },
10584
10585     // private
10586     showPrevMonth : function(e){
10587         this.update(this.activeDate.add("mo", -1));
10588     },
10589
10590     // private
10591     showNextMonth : function(e){
10592         this.update(this.activeDate.add("mo", 1));
10593     },
10594
10595     // private
10596     showPrevYear : function(){
10597         this.update(this.activeDate.add("y", -1));
10598     },
10599
10600     // private
10601     showNextYear : function(){
10602         this.update(this.activeDate.add("y", 1));
10603     },
10604
10605     // private
10606     handleMouseWheel : function(e){
10607         var delta = e.getWheelDelta();
10608         if(delta > 0){
10609             this.showPrevMonth();
10610             e.stopEvent();
10611         } else if(delta < 0){
10612             this.showNextMonth();
10613             e.stopEvent();
10614         }
10615     },
10616
10617     // private
10618     handleDateClick : function(e, t){
10619         e.stopEvent();
10620         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10621             this.setValue(new Date(t.dateValue));
10622             this.fireEvent("select", this, this.value);
10623         }
10624     },
10625
10626     // private
10627     selectToday : function(){
10628         this.setValue(new Date().clearTime());
10629         this.fireEvent("select", this, this.value);
10630     },
10631
10632     // private
10633     update : function(date)
10634     {
10635         var vd = this.activeDate;
10636         this.activeDate = date;
10637         if(vd && this.el){
10638             var t = date.getTime();
10639             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10640                 this.cells.removeClass("x-date-selected");
10641                 this.cells.each(function(c){
10642                    if(c.dom.firstChild.dateValue == t){
10643                        c.addClass("x-date-selected");
10644                        setTimeout(function(){
10645                             try{c.dom.firstChild.focus();}catch(e){}
10646                        }, 50);
10647                        return false;
10648                    }
10649                 });
10650                 return;
10651             }
10652         }
10653         
10654         var days = date.getDaysInMonth();
10655         var firstOfMonth = date.getFirstDateOfMonth();
10656         var startingPos = firstOfMonth.getDay()-this.startDay;
10657
10658         if(startingPos <= this.startDay){
10659             startingPos += 7;
10660         }
10661
10662         var pm = date.add("mo", -1);
10663         var prevStart = pm.getDaysInMonth()-startingPos;
10664
10665         var cells = this.cells.elements;
10666         var textEls = this.textNodes;
10667         days += startingPos;
10668
10669         // convert everything to numbers so it's fast
10670         var day = 86400000;
10671         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10672         var today = new Date().clearTime().getTime();
10673         var sel = date.clearTime().getTime();
10674         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10675         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10676         var ddMatch = this.disabledDatesRE;
10677         var ddText = this.disabledDatesText;
10678         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10679         var ddaysText = this.disabledDaysText;
10680         var format = this.format;
10681
10682         var setCellClass = function(cal, cell){
10683             cell.title = "";
10684             var t = d.getTime();
10685             cell.firstChild.dateValue = t;
10686             if(t == today){
10687                 cell.className += " x-date-today";
10688                 cell.title = cal.todayText;
10689             }
10690             if(t == sel){
10691                 cell.className += " x-date-selected";
10692                 setTimeout(function(){
10693                     try{cell.firstChild.focus();}catch(e){}
10694                 }, 50);
10695             }
10696             // disabling
10697             if(t < min) {
10698                 cell.className = " x-date-disabled";
10699                 cell.title = cal.minText;
10700                 return;
10701             }
10702             if(t > max) {
10703                 cell.className = " x-date-disabled";
10704                 cell.title = cal.maxText;
10705                 return;
10706             }
10707             if(ddays){
10708                 if(ddays.indexOf(d.getDay()) != -1){
10709                     cell.title = ddaysText;
10710                     cell.className = " x-date-disabled";
10711                 }
10712             }
10713             if(ddMatch && format){
10714                 var fvalue = d.dateFormat(format);
10715                 if(ddMatch.test(fvalue)){
10716                     cell.title = ddText.replace("%0", fvalue);
10717                     cell.className = " x-date-disabled";
10718                 }
10719             }
10720         };
10721
10722         var i = 0;
10723         for(; i < startingPos; i++) {
10724             textEls[i].innerHTML = (++prevStart);
10725             d.setDate(d.getDate()+1);
10726             cells[i].className = "x-date-prevday";
10727             setCellClass(this, cells[i]);
10728         }
10729         for(; i < days; i++){
10730             intDay = i - startingPos + 1;
10731             textEls[i].innerHTML = (intDay);
10732             d.setDate(d.getDate()+1);
10733             cells[i].className = "x-date-active";
10734             setCellClass(this, cells[i]);
10735         }
10736         var extraDays = 0;
10737         for(; i < 42; i++) {
10738              textEls[i].innerHTML = (++extraDays);
10739              d.setDate(d.getDate()+1);
10740              cells[i].className = "x-date-nextday";
10741              setCellClass(this, cells[i]);
10742         }
10743
10744         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10745         this.fireEvent('monthchange', this, date);
10746         
10747         if(!this.internalRender){
10748             var main = this.el.dom.firstChild;
10749             var w = main.offsetWidth;
10750             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10751             Roo.fly(main).setWidth(w);
10752             this.internalRender = true;
10753             // opera does not respect the auto grow header center column
10754             // then, after it gets a width opera refuses to recalculate
10755             // without a second pass
10756             if(Roo.isOpera && !this.secondPass){
10757                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10758                 this.secondPass = true;
10759                 this.update.defer(10, this, [date]);
10760             }
10761         }
10762         
10763         
10764     }
10765 });        /*
10766  * Based on:
10767  * Ext JS Library 1.1.1
10768  * Copyright(c) 2006-2007, Ext JS, LLC.
10769  *
10770  * Originally Released Under LGPL - original licence link has changed is not relivant.
10771  *
10772  * Fork - LGPL
10773  * <script type="text/javascript">
10774  */
10775 /**
10776  * @class Roo.TabPanel
10777  * @extends Roo.util.Observable
10778  * A lightweight tab container.
10779  * <br><br>
10780  * Usage:
10781  * <pre><code>
10782 // basic tabs 1, built from existing content
10783 var tabs = new Roo.TabPanel("tabs1");
10784 tabs.addTab("script", "View Script");
10785 tabs.addTab("markup", "View Markup");
10786 tabs.activate("script");
10787
10788 // more advanced tabs, built from javascript
10789 var jtabs = new Roo.TabPanel("jtabs");
10790 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10791
10792 // set up the UpdateManager
10793 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10794 var updater = tab2.getUpdateManager();
10795 updater.setDefaultUrl("ajax1.htm");
10796 tab2.on('activate', updater.refresh, updater, true);
10797
10798 // Use setUrl for Ajax loading
10799 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10800 tab3.setUrl("ajax2.htm", null, true);
10801
10802 // Disabled tab
10803 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10804 tab4.disable();
10805
10806 jtabs.activate("jtabs-1");
10807  * </code></pre>
10808  * @constructor
10809  * Create a new TabPanel.
10810  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10811  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10812  */
10813 Roo.TabPanel = function(container, config){
10814     /**
10815     * The container element for this TabPanel.
10816     * @type Roo.Element
10817     */
10818     this.el = Roo.get(container, true);
10819     if(config){
10820         if(typeof config == "boolean"){
10821             this.tabPosition = config ? "bottom" : "top";
10822         }else{
10823             Roo.apply(this, config);
10824         }
10825     }
10826     if(this.tabPosition == "bottom"){
10827         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10828         this.el.addClass("x-tabs-bottom");
10829     }
10830     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10831     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10832     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10833     if(Roo.isIE){
10834         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10835     }
10836     if(this.tabPosition != "bottom"){
10837         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10838          * @type Roo.Element
10839          */
10840         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10841         this.el.addClass("x-tabs-top");
10842     }
10843     this.items = [];
10844
10845     this.bodyEl.setStyle("position", "relative");
10846
10847     this.active = null;
10848     this.activateDelegate = this.activate.createDelegate(this);
10849
10850     this.addEvents({
10851         /**
10852          * @event tabchange
10853          * Fires when the active tab changes
10854          * @param {Roo.TabPanel} this
10855          * @param {Roo.TabPanelItem} activePanel The new active tab
10856          */
10857         "tabchange": true,
10858         /**
10859          * @event beforetabchange
10860          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10861          * @param {Roo.TabPanel} this
10862          * @param {Object} e Set cancel to true on this object to cancel the tab change
10863          * @param {Roo.TabPanelItem} tab The tab being changed to
10864          */
10865         "beforetabchange" : true
10866     });
10867
10868     Roo.EventManager.onWindowResize(this.onResize, this);
10869     this.cpad = this.el.getPadding("lr");
10870     this.hiddenCount = 0;
10871
10872
10873     // toolbar on the tabbar support...
10874     if (this.toolbar) {
10875         var tcfg = this.toolbar;
10876         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
10877         this.toolbar = new Roo.Toolbar(tcfg);
10878         if (Roo.isSafari) {
10879             var tbl = tcfg.container.child('table', true);
10880             tbl.setAttribute('width', '100%');
10881         }
10882         
10883     }
10884    
10885
10886
10887     Roo.TabPanel.superclass.constructor.call(this);
10888 };
10889
10890 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10891     /*
10892      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10893      */
10894     tabPosition : "top",
10895     /*
10896      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10897      */
10898     currentTabWidth : 0,
10899     /*
10900      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10901      */
10902     minTabWidth : 40,
10903     /*
10904      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10905      */
10906     maxTabWidth : 250,
10907     /*
10908      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10909      */
10910     preferredTabWidth : 175,
10911     /*
10912      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10913      */
10914     resizeTabs : false,
10915     /*
10916      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10917      */
10918     monitorResize : true,
10919     /*
10920      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
10921      */
10922     toolbar : false,
10923
10924     /**
10925      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10926      * @param {String} id The id of the div to use <b>or create</b>
10927      * @param {String} text The text for the tab
10928      * @param {String} content (optional) Content to put in the TabPanelItem body
10929      * @param {Boolean} closable (optional) True to create a close icon on the tab
10930      * @return {Roo.TabPanelItem} The created TabPanelItem
10931      */
10932     addTab : function(id, text, content, closable){
10933         var item = new Roo.TabPanelItem(this, id, text, closable);
10934         this.addTabItem(item);
10935         if(content){
10936             item.setContent(content);
10937         }
10938         return item;
10939     },
10940
10941     /**
10942      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10943      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10944      * @return {Roo.TabPanelItem}
10945      */
10946     getTab : function(id){
10947         return this.items[id];
10948     },
10949
10950     /**
10951      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10952      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10953      */
10954     hideTab : function(id){
10955         var t = this.items[id];
10956         if(!t.isHidden()){
10957            t.setHidden(true);
10958            this.hiddenCount++;
10959            this.autoSizeTabs();
10960         }
10961     },
10962
10963     /**
10964      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10965      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10966      */
10967     unhideTab : function(id){
10968         var t = this.items[id];
10969         if(t.isHidden()){
10970            t.setHidden(false);
10971            this.hiddenCount--;
10972            this.autoSizeTabs();
10973         }
10974     },
10975
10976     /**
10977      * Adds an existing {@link Roo.TabPanelItem}.
10978      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10979      */
10980     addTabItem : function(item){
10981         this.items[item.id] = item;
10982         this.items.push(item);
10983         if(this.resizeTabs){
10984            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10985            this.autoSizeTabs();
10986         }else{
10987             item.autoSize();
10988         }
10989     },
10990
10991     /**
10992      * Removes a {@link Roo.TabPanelItem}.
10993      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10994      */
10995     removeTab : function(id){
10996         var items = this.items;
10997         var tab = items[id];
10998         if(!tab) { return; }
10999         var index = items.indexOf(tab);
11000         if(this.active == tab && items.length > 1){
11001             var newTab = this.getNextAvailable(index);
11002             if(newTab) {
11003                 newTab.activate();
11004             }
11005         }
11006         this.stripEl.dom.removeChild(tab.pnode.dom);
11007         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
11008             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
11009         }
11010         items.splice(index, 1);
11011         delete this.items[tab.id];
11012         tab.fireEvent("close", tab);
11013         tab.purgeListeners();
11014         this.autoSizeTabs();
11015     },
11016
11017     getNextAvailable : function(start){
11018         var items = this.items;
11019         var index = start;
11020         // look for a next tab that will slide over to
11021         // replace the one being removed
11022         while(index < items.length){
11023             var item = items[++index];
11024             if(item && !item.isHidden()){
11025                 return item;
11026             }
11027         }
11028         // if one isn't found select the previous tab (on the left)
11029         index = start;
11030         while(index >= 0){
11031             var item = items[--index];
11032             if(item && !item.isHidden()){
11033                 return item;
11034             }
11035         }
11036         return null;
11037     },
11038
11039     /**
11040      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
11041      * @param {String/Number} id The id or index of the TabPanelItem to disable.
11042      */
11043     disableTab : function(id){
11044         var tab = this.items[id];
11045         if(tab && this.active != tab){
11046             tab.disable();
11047         }
11048     },
11049
11050     /**
11051      * Enables a {@link Roo.TabPanelItem} that is disabled.
11052      * @param {String/Number} id The id or index of the TabPanelItem to enable.
11053      */
11054     enableTab : function(id){
11055         var tab = this.items[id];
11056         tab.enable();
11057     },
11058
11059     /**
11060      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
11061      * @param {String/Number} id The id or index of the TabPanelItem to activate.
11062      * @return {Roo.TabPanelItem} The TabPanelItem.
11063      */
11064     activate : function(id){
11065         var tab = this.items[id];
11066         if(!tab){
11067             return null;
11068         }
11069         if(tab == this.active || tab.disabled){
11070             return tab;
11071         }
11072         var e = {};
11073         this.fireEvent("beforetabchange", this, e, tab);
11074         if(e.cancel !== true && !tab.disabled){
11075             if(this.active){
11076                 this.active.hide();
11077             }
11078             this.active = this.items[id];
11079             this.active.show();
11080             this.fireEvent("tabchange", this, this.active);
11081         }
11082         return tab;
11083     },
11084
11085     /**
11086      * Gets the active {@link Roo.TabPanelItem}.
11087      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
11088      */
11089     getActiveTab : function(){
11090         return this.active;
11091     },
11092
11093     /**
11094      * Updates the tab body element to fit the height of the container element
11095      * for overflow scrolling
11096      * @param {Number} targetHeight (optional) Override the starting height from the elements height
11097      */
11098     syncHeight : function(targetHeight){
11099         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
11100         var bm = this.bodyEl.getMargins();
11101         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
11102         this.bodyEl.setHeight(newHeight);
11103         return newHeight;
11104     },
11105
11106     onResize : function(){
11107         if(this.monitorResize){
11108             this.autoSizeTabs();
11109         }
11110     },
11111
11112     /**
11113      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
11114      */
11115     beginUpdate : function(){
11116         this.updating = true;
11117     },
11118
11119     /**
11120      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
11121      */
11122     endUpdate : function(){
11123         this.updating = false;
11124         this.autoSizeTabs();
11125     },
11126
11127     /**
11128      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11129      */
11130     autoSizeTabs : function(){
11131         var count = this.items.length;
11132         var vcount = count - this.hiddenCount;
11133         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11134         var w = Math.max(this.el.getWidth() - this.cpad, 10);
11135         var availWidth = Math.floor(w / vcount);
11136         var b = this.stripBody;
11137         if(b.getWidth() > w){
11138             var tabs = this.items;
11139             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11140             if(availWidth < this.minTabWidth){
11141                 /*if(!this.sleft){    // incomplete scrolling code
11142                     this.createScrollButtons();
11143                 }
11144                 this.showScroll();
11145                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11146             }
11147         }else{
11148             if(this.currentTabWidth < this.preferredTabWidth){
11149                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11150             }
11151         }
11152     },
11153
11154     /**
11155      * Returns the number of tabs in this TabPanel.
11156      * @return {Number}
11157      */
11158      getCount : function(){
11159          return this.items.length;
11160      },
11161
11162     /**
11163      * Resizes all the tabs to the passed width
11164      * @param {Number} The new width
11165      */
11166     setTabWidth : function(width){
11167         this.currentTabWidth = width;
11168         for(var i = 0, len = this.items.length; i < len; i++) {
11169                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11170         }
11171     },
11172
11173     /**
11174      * Destroys this TabPanel
11175      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11176      */
11177     destroy : function(removeEl){
11178         Roo.EventManager.removeResizeListener(this.onResize, this);
11179         for(var i = 0, len = this.items.length; i < len; i++){
11180             this.items[i].purgeListeners();
11181         }
11182         if(removeEl === true){
11183             this.el.update("");
11184             this.el.remove();
11185         }
11186     }
11187 });
11188
11189 /**
11190  * @class Roo.TabPanelItem
11191  * @extends Roo.util.Observable
11192  * Represents an individual item (tab plus body) in a TabPanel.
11193  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11194  * @param {String} id The id of this TabPanelItem
11195  * @param {String} text The text for the tab of this TabPanelItem
11196  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11197  */
11198 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11199     /**
11200      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11201      * @type Roo.TabPanel
11202      */
11203     this.tabPanel = tabPanel;
11204     /**
11205      * The id for this TabPanelItem
11206      * @type String
11207      */
11208     this.id = id;
11209     /** @private */
11210     this.disabled = false;
11211     /** @private */
11212     this.text = text;
11213     /** @private */
11214     this.loaded = false;
11215     this.closable = closable;
11216
11217     /**
11218      * The body element for this TabPanelItem.
11219      * @type Roo.Element
11220      */
11221     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11222     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11223     this.bodyEl.setStyle("display", "block");
11224     this.bodyEl.setStyle("zoom", "1");
11225     this.hideAction();
11226
11227     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11228     /** @private */
11229     this.el = Roo.get(els.el, true);
11230     this.inner = Roo.get(els.inner, true);
11231     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11232     this.pnode = Roo.get(els.el.parentNode, true);
11233     this.el.on("mousedown", this.onTabMouseDown, this);
11234     this.el.on("click", this.onTabClick, this);
11235     /** @private */
11236     if(closable){
11237         var c = Roo.get(els.close, true);
11238         c.dom.title = this.closeText;
11239         c.addClassOnOver("close-over");
11240         c.on("click", this.closeClick, this);
11241      }
11242
11243     this.addEvents({
11244          /**
11245          * @event activate
11246          * Fires when this tab becomes the active tab.
11247          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11248          * @param {Roo.TabPanelItem} this
11249          */
11250         "activate": true,
11251         /**
11252          * @event beforeclose
11253          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11254          * @param {Roo.TabPanelItem} this
11255          * @param {Object} e Set cancel to true on this object to cancel the close.
11256          */
11257         "beforeclose": true,
11258         /**
11259          * @event close
11260          * Fires when this tab is closed.
11261          * @param {Roo.TabPanelItem} this
11262          */
11263          "close": true,
11264         /**
11265          * @event deactivate
11266          * Fires when this tab is no longer the active tab.
11267          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11268          * @param {Roo.TabPanelItem} this
11269          */
11270          "deactivate" : true
11271     });
11272     this.hidden = false;
11273
11274     Roo.TabPanelItem.superclass.constructor.call(this);
11275 };
11276
11277 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11278     purgeListeners : function(){
11279        Roo.util.Observable.prototype.purgeListeners.call(this);
11280        this.el.removeAllListeners();
11281     },
11282     /**
11283      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11284      */
11285     show : function(){
11286         this.pnode.addClass("on");
11287         this.showAction();
11288         if(Roo.isOpera){
11289             this.tabPanel.stripWrap.repaint();
11290         }
11291         this.fireEvent("activate", this.tabPanel, this);
11292     },
11293
11294     /**
11295      * Returns true if this tab is the active tab.
11296      * @return {Boolean}
11297      */
11298     isActive : function(){
11299         return this.tabPanel.getActiveTab() == this;
11300     },
11301
11302     /**
11303      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11304      */
11305     hide : function(){
11306         this.pnode.removeClass("on");
11307         this.hideAction();
11308         this.fireEvent("deactivate", this.tabPanel, this);
11309     },
11310
11311     hideAction : function(){
11312         this.bodyEl.hide();
11313         this.bodyEl.setStyle("position", "absolute");
11314         this.bodyEl.setLeft("-20000px");
11315         this.bodyEl.setTop("-20000px");
11316     },
11317
11318     showAction : function(){
11319         this.bodyEl.setStyle("position", "relative");
11320         this.bodyEl.setTop("");
11321         this.bodyEl.setLeft("");
11322         this.bodyEl.show();
11323     },
11324
11325     /**
11326      * Set the tooltip for the tab.
11327      * @param {String} tooltip The tab's tooltip
11328      */
11329     setTooltip : function(text){
11330         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11331             this.textEl.dom.qtip = text;
11332             this.textEl.dom.removeAttribute('title');
11333         }else{
11334             this.textEl.dom.title = text;
11335         }
11336     },
11337
11338     onTabClick : function(e){
11339         e.preventDefault();
11340         this.tabPanel.activate(this.id);
11341     },
11342
11343     onTabMouseDown : function(e){
11344         e.preventDefault();
11345         this.tabPanel.activate(this.id);
11346     },
11347
11348     getWidth : function(){
11349         return this.inner.getWidth();
11350     },
11351
11352     setWidth : function(width){
11353         var iwidth = width - this.pnode.getPadding("lr");
11354         this.inner.setWidth(iwidth);
11355         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11356         this.pnode.setWidth(width);
11357     },
11358
11359     /**
11360      * Show or hide the tab
11361      * @param {Boolean} hidden True to hide or false to show.
11362      */
11363     setHidden : function(hidden){
11364         this.hidden = hidden;
11365         this.pnode.setStyle("display", hidden ? "none" : "");
11366     },
11367
11368     /**
11369      * Returns true if this tab is "hidden"
11370      * @return {Boolean}
11371      */
11372     isHidden : function(){
11373         return this.hidden;
11374     },
11375
11376     /**
11377      * Returns the text for this tab
11378      * @return {String}
11379      */
11380     getText : function(){
11381         return this.text;
11382     },
11383
11384     autoSize : function(){
11385         //this.el.beginMeasure();
11386         this.textEl.setWidth(1);
11387         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11388         //this.el.endMeasure();
11389     },
11390
11391     /**
11392      * Sets the text for the tab (Note: this also sets the tooltip text)
11393      * @param {String} text The tab's text and tooltip
11394      */
11395     setText : function(text){
11396         this.text = text;
11397         this.textEl.update(text);
11398         this.setTooltip(text);
11399         if(!this.tabPanel.resizeTabs){
11400             this.autoSize();
11401         }
11402     },
11403     /**
11404      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11405      */
11406     activate : function(){
11407         this.tabPanel.activate(this.id);
11408     },
11409
11410     /**
11411      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11412      */
11413     disable : function(){
11414         if(this.tabPanel.active != this){
11415             this.disabled = true;
11416             this.pnode.addClass("disabled");
11417         }
11418     },
11419
11420     /**
11421      * Enables this TabPanelItem if it was previously disabled.
11422      */
11423     enable : function(){
11424         this.disabled = false;
11425         this.pnode.removeClass("disabled");
11426     },
11427
11428     /**
11429      * Sets the content for this TabPanelItem.
11430      * @param {String} content The content
11431      * @param {Boolean} loadScripts true to look for and load scripts
11432      */
11433     setContent : function(content, loadScripts){
11434         this.bodyEl.update(content, loadScripts);
11435     },
11436
11437     /**
11438      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11439      * @return {Roo.UpdateManager} The UpdateManager
11440      */
11441     getUpdateManager : function(){
11442         return this.bodyEl.getUpdateManager();
11443     },
11444
11445     /**
11446      * Set a URL to be used to load the content for this TabPanelItem.
11447      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11448      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
11449      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
11450      * @return {Roo.UpdateManager} The UpdateManager
11451      */
11452     setUrl : function(url, params, loadOnce){
11453         if(this.refreshDelegate){
11454             this.un('activate', this.refreshDelegate);
11455         }
11456         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11457         this.on("activate", this.refreshDelegate);
11458         return this.bodyEl.getUpdateManager();
11459     },
11460
11461     /** @private */
11462     _handleRefresh : function(url, params, loadOnce){
11463         if(!loadOnce || !this.loaded){
11464             var updater = this.bodyEl.getUpdateManager();
11465             updater.update(url, params, this._setLoaded.createDelegate(this));
11466         }
11467     },
11468
11469     /**
11470      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11471      *   Will fail silently if the setUrl method has not been called.
11472      *   This does not activate the panel, just updates its content.
11473      */
11474     refresh : function(){
11475         if(this.refreshDelegate){
11476            this.loaded = false;
11477            this.refreshDelegate();
11478         }
11479     },
11480
11481     /** @private */
11482     _setLoaded : function(){
11483         this.loaded = true;
11484     },
11485
11486     /** @private */
11487     closeClick : function(e){
11488         var o = {};
11489         e.stopEvent();
11490         this.fireEvent("beforeclose", this, o);
11491         if(o.cancel !== true){
11492             this.tabPanel.removeTab(this.id);
11493         }
11494     },
11495     /**
11496      * The text displayed in the tooltip for the close icon.
11497      * @type String
11498      */
11499     closeText : "Close this tab"
11500 });
11501
11502 /** @private */
11503 Roo.TabPanel.prototype.createStrip = function(container){
11504     var strip = document.createElement("div");
11505     strip.className = "x-tabs-wrap";
11506     container.appendChild(strip);
11507     return strip;
11508 };
11509 /** @private */
11510 Roo.TabPanel.prototype.createStripList = function(strip){
11511     // div wrapper for retard IE
11512     // returns the "tr" element.
11513     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
11514         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
11515         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
11516     return strip.firstChild.firstChild.firstChild.firstChild;
11517 };
11518 /** @private */
11519 Roo.TabPanel.prototype.createBody = function(container){
11520     var body = document.createElement("div");
11521     Roo.id(body, "tab-body");
11522     Roo.fly(body).addClass("x-tabs-body");
11523     container.appendChild(body);
11524     return body;
11525 };
11526 /** @private */
11527 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11528     var body = Roo.getDom(id);
11529     if(!body){
11530         body = document.createElement("div");
11531         body.id = id;
11532     }
11533     Roo.fly(body).addClass("x-tabs-item-body");
11534     bodyEl.insertBefore(body, bodyEl.firstChild);
11535     return body;
11536 };
11537 /** @private */
11538 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11539     var td = document.createElement("td");
11540     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
11541     //stripEl.appendChild(td);
11542     if(closable){
11543         td.className = "x-tabs-closable";
11544         if(!this.closeTpl){
11545             this.closeTpl = new Roo.Template(
11546                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11547                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11548                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11549             );
11550         }
11551         var el = this.closeTpl.overwrite(td, {"text": text});
11552         var close = el.getElementsByTagName("div")[0];
11553         var inner = el.getElementsByTagName("em")[0];
11554         return {"el": el, "close": close, "inner": inner};
11555     } else {
11556         if(!this.tabTpl){
11557             this.tabTpl = new Roo.Template(
11558                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11559                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11560             );
11561         }
11562         var el = this.tabTpl.overwrite(td, {"text": text});
11563         var inner = el.getElementsByTagName("em")[0];
11564         return {"el": el, "inner": inner};
11565     }
11566 };/*
11567  * Based on:
11568  * Ext JS Library 1.1.1
11569  * Copyright(c) 2006-2007, Ext JS, LLC.
11570  *
11571  * Originally Released Under LGPL - original licence link has changed is not relivant.
11572  *
11573  * Fork - LGPL
11574  * <script type="text/javascript">
11575  */
11576
11577 /**
11578  * @class Roo.Button
11579  * @extends Roo.util.Observable
11580  * Simple Button class
11581  * @cfg {String} text The button text
11582  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11583  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11584  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11585  * @cfg {Object} scope The scope of the handler
11586  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11587  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11588  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11589  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11590  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11591  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11592    applies if enableToggle = true)
11593  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11594  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11595   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11596  * @constructor
11597  * Create a new button
11598  * @param {Object} config The config object
11599  */
11600 Roo.Button = function(renderTo, config)
11601 {
11602     if (!config) {
11603         config = renderTo;
11604         renderTo = config.renderTo || false;
11605     }
11606     
11607     Roo.apply(this, config);
11608     this.addEvents({
11609         /**
11610              * @event click
11611              * Fires when this button is clicked
11612              * @param {Button} this
11613              * @param {EventObject} e The click event
11614              */
11615             "click" : true,
11616         /**
11617              * @event toggle
11618              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11619              * @param {Button} this
11620              * @param {Boolean} pressed
11621              */
11622             "toggle" : true,
11623         /**
11624              * @event mouseover
11625              * Fires when the mouse hovers over the button
11626              * @param {Button} this
11627              * @param {Event} e The event object
11628              */
11629         'mouseover' : true,
11630         /**
11631              * @event mouseout
11632              * Fires when the mouse exits the button
11633              * @param {Button} this
11634              * @param {Event} e The event object
11635              */
11636         'mouseout': true,
11637          /**
11638              * @event render
11639              * Fires when the button is rendered
11640              * @param {Button} this
11641              */
11642         'render': true
11643     });
11644     if(this.menu){
11645         this.menu = Roo.menu.MenuMgr.get(this.menu);
11646     }
11647     // register listeners first!!  - so render can be captured..
11648     Roo.util.Observable.call(this);
11649     if(renderTo){
11650         this.render(renderTo);
11651     }
11652     
11653   
11654 };
11655
11656 Roo.extend(Roo.Button, Roo.util.Observable, {
11657     /**
11658      * 
11659      */
11660     
11661     /**
11662      * Read-only. True if this button is hidden
11663      * @type Boolean
11664      */
11665     hidden : false,
11666     /**
11667      * Read-only. True if this button is disabled
11668      * @type Boolean
11669      */
11670     disabled : false,
11671     /**
11672      * Read-only. True if this button is pressed (only if enableToggle = true)
11673      * @type Boolean
11674      */
11675     pressed : false,
11676
11677     /**
11678      * @cfg {Number} tabIndex 
11679      * The DOM tabIndex for this button (defaults to undefined)
11680      */
11681     tabIndex : undefined,
11682
11683     /**
11684      * @cfg {Boolean} enableToggle
11685      * True to enable pressed/not pressed toggling (defaults to false)
11686      */
11687     enableToggle: false,
11688     /**
11689      * @cfg {Mixed} menu
11690      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11691      */
11692     menu : undefined,
11693     /**
11694      * @cfg {String} menuAlign
11695      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11696      */
11697     menuAlign : "tl-bl?",
11698
11699     /**
11700      * @cfg {String} iconCls
11701      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11702      */
11703     iconCls : undefined,
11704     /**
11705      * @cfg {String} type
11706      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11707      */
11708     type : 'button',
11709
11710     // private
11711     menuClassTarget: 'tr',
11712
11713     /**
11714      * @cfg {String} clickEvent
11715      * The type of event to map to the button's event handler (defaults to 'click')
11716      */
11717     clickEvent : 'click',
11718
11719     /**
11720      * @cfg {Boolean} handleMouseEvents
11721      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11722      */
11723     handleMouseEvents : true,
11724
11725     /**
11726      * @cfg {String} tooltipType
11727      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11728      */
11729     tooltipType : 'qtip',
11730
11731     /**
11732      * @cfg {String} cls
11733      * A CSS class to apply to the button's main element.
11734      */
11735     
11736     /**
11737      * @cfg {Roo.Template} template (Optional)
11738      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11739      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11740      * require code modifications if required elements (e.g. a button) aren't present.
11741      */
11742
11743     // private
11744     render : function(renderTo){
11745         var btn;
11746         if(this.hideParent){
11747             this.parentEl = Roo.get(renderTo);
11748         }
11749         if(!this.dhconfig){
11750             if(!this.template){
11751                 if(!Roo.Button.buttonTemplate){
11752                     // hideous table template
11753                     Roo.Button.buttonTemplate = new Roo.Template(
11754                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11755                         '<td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><em unselectable="on"><button class="x-btn-text" type="{1}">{0}</button></em></td><td class="x-btn-right"><i>&#160;</i></td>',
11756                         "</tr></tbody></table>");
11757                 }
11758                 this.template = Roo.Button.buttonTemplate;
11759             }
11760             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11761             var btnEl = btn.child("button:first");
11762             btnEl.on('focus', this.onFocus, this);
11763             btnEl.on('blur', this.onBlur, this);
11764             if(this.cls){
11765                 btn.addClass(this.cls);
11766             }
11767             if(this.icon){
11768                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11769             }
11770             if(this.iconCls){
11771                 btnEl.addClass(this.iconCls);
11772                 if(!this.cls){
11773                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11774                 }
11775             }
11776             if(this.tabIndex !== undefined){
11777                 btnEl.dom.tabIndex = this.tabIndex;
11778             }
11779             if(this.tooltip){
11780                 if(typeof this.tooltip == 'object'){
11781                     Roo.QuickTips.tips(Roo.apply({
11782                           target: btnEl.id
11783                     }, this.tooltip));
11784                 } else {
11785                     btnEl.dom[this.tooltipType] = this.tooltip;
11786                 }
11787             }
11788         }else{
11789             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11790         }
11791         this.el = btn;
11792         if(this.id){
11793             this.el.dom.id = this.el.id = this.id;
11794         }
11795         if(this.menu){
11796             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11797             this.menu.on("show", this.onMenuShow, this);
11798             this.menu.on("hide", this.onMenuHide, this);
11799         }
11800         btn.addClass("x-btn");
11801         if(Roo.isIE && !Roo.isIE7){
11802             this.autoWidth.defer(1, this);
11803         }else{
11804             this.autoWidth();
11805         }
11806         if(this.handleMouseEvents){
11807             btn.on("mouseover", this.onMouseOver, this);
11808             btn.on("mouseout", this.onMouseOut, this);
11809             btn.on("mousedown", this.onMouseDown, this);
11810         }
11811         btn.on(this.clickEvent, this.onClick, this);
11812         //btn.on("mouseup", this.onMouseUp, this);
11813         if(this.hidden){
11814             this.hide();
11815         }
11816         if(this.disabled){
11817             this.disable();
11818         }
11819         Roo.ButtonToggleMgr.register(this);
11820         if(this.pressed){
11821             this.el.addClass("x-btn-pressed");
11822         }
11823         if(this.repeat){
11824             var repeater = new Roo.util.ClickRepeater(btn,
11825                 typeof this.repeat == "object" ? this.repeat : {}
11826             );
11827             repeater.on("click", this.onClick,  this);
11828         }
11829         
11830         this.fireEvent('render', this);
11831         
11832     },
11833     /**
11834      * Returns the button's underlying element
11835      * @return {Roo.Element} The element
11836      */
11837     getEl : function(){
11838         return this.el;  
11839     },
11840     
11841     /**
11842      * Destroys this Button and removes any listeners.
11843      */
11844     destroy : function(){
11845         Roo.ButtonToggleMgr.unregister(this);
11846         this.el.removeAllListeners();
11847         this.purgeListeners();
11848         this.el.remove();
11849     },
11850
11851     // private
11852     autoWidth : function(){
11853         if(this.el){
11854             this.el.setWidth("auto");
11855             if(Roo.isIE7 && Roo.isStrict){
11856                 var ib = this.el.child('button');
11857                 if(ib && ib.getWidth() > 20){
11858                     ib.clip();
11859                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11860                 }
11861             }
11862             if(this.minWidth){
11863                 if(this.hidden){
11864                     this.el.beginMeasure();
11865                 }
11866                 if(this.el.getWidth() < this.minWidth){
11867                     this.el.setWidth(this.minWidth);
11868                 }
11869                 if(this.hidden){
11870                     this.el.endMeasure();
11871                 }
11872             }
11873         }
11874     },
11875
11876     /**
11877      * Assigns this button's click handler
11878      * @param {Function} handler The function to call when the button is clicked
11879      * @param {Object} scope (optional) Scope for the function passed in
11880      */
11881     setHandler : function(handler, scope){
11882         this.handler = handler;
11883         this.scope = scope;  
11884     },
11885     
11886     /**
11887      * Sets this button's text
11888      * @param {String} text The button text
11889      */
11890     setText : function(text){
11891         this.text = text;
11892         if(this.el){
11893             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11894         }
11895         this.autoWidth();
11896     },
11897     
11898     /**
11899      * Gets the text for this button
11900      * @return {String} The button text
11901      */
11902     getText : function(){
11903         return this.text;  
11904     },
11905     
11906     /**
11907      * Show this button
11908      */
11909     show: function(){
11910         this.hidden = false;
11911         if(this.el){
11912             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11913         }
11914     },
11915     
11916     /**
11917      * Hide this button
11918      */
11919     hide: function(){
11920         this.hidden = true;
11921         if(this.el){
11922             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11923         }
11924     },
11925     
11926     /**
11927      * Convenience function for boolean show/hide
11928      * @param {Boolean} visible True to show, false to hide
11929      */
11930     setVisible: function(visible){
11931         if(visible) {
11932             this.show();
11933         }else{
11934             this.hide();
11935         }
11936     },
11937     
11938     /**
11939      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11940      * @param {Boolean} state (optional) Force a particular state
11941      */
11942     toggle : function(state){
11943         state = state === undefined ? !this.pressed : state;
11944         if(state != this.pressed){
11945             if(state){
11946                 this.el.addClass("x-btn-pressed");
11947                 this.pressed = true;
11948                 this.fireEvent("toggle", this, true);
11949             }else{
11950                 this.el.removeClass("x-btn-pressed");
11951                 this.pressed = false;
11952                 this.fireEvent("toggle", this, false);
11953             }
11954             if(this.toggleHandler){
11955                 this.toggleHandler.call(this.scope || this, this, state);
11956             }
11957         }
11958     },
11959     
11960     /**
11961      * Focus the button
11962      */
11963     focus : function(){
11964         this.el.child('button:first').focus();
11965     },
11966     
11967     /**
11968      * Disable this button
11969      */
11970     disable : function(){
11971         if(this.el){
11972             this.el.addClass("x-btn-disabled");
11973         }
11974         this.disabled = true;
11975     },
11976     
11977     /**
11978      * Enable this button
11979      */
11980     enable : function(){
11981         if(this.el){
11982             this.el.removeClass("x-btn-disabled");
11983         }
11984         this.disabled = false;
11985     },
11986
11987     /**
11988      * Convenience function for boolean enable/disable
11989      * @param {Boolean} enabled True to enable, false to disable
11990      */
11991     setDisabled : function(v){
11992         this[v !== true ? "enable" : "disable"]();
11993     },
11994
11995     // private
11996     onClick : function(e){
11997         if(e){
11998             e.preventDefault();
11999         }
12000         if(e.button != 0){
12001             return;
12002         }
12003         if(!this.disabled){
12004             if(this.enableToggle){
12005                 this.toggle();
12006             }
12007             if(this.menu && !this.menu.isVisible()){
12008                 this.menu.show(this.el, this.menuAlign);
12009             }
12010             this.fireEvent("click", this, e);
12011             if(this.handler){
12012                 this.el.removeClass("x-btn-over");
12013                 this.handler.call(this.scope || this, this, e);
12014             }
12015         }
12016     },
12017     // private
12018     onMouseOver : function(e){
12019         if(!this.disabled){
12020             this.el.addClass("x-btn-over");
12021             this.fireEvent('mouseover', this, e);
12022         }
12023     },
12024     // private
12025     onMouseOut : function(e){
12026         if(!e.within(this.el,  true)){
12027             this.el.removeClass("x-btn-over");
12028             this.fireEvent('mouseout', this, e);
12029         }
12030     },
12031     // private
12032     onFocus : function(e){
12033         if(!this.disabled){
12034             this.el.addClass("x-btn-focus");
12035         }
12036     },
12037     // private
12038     onBlur : function(e){
12039         this.el.removeClass("x-btn-focus");
12040     },
12041     // private
12042     onMouseDown : function(e){
12043         if(!this.disabled && e.button == 0){
12044             this.el.addClass("x-btn-click");
12045             Roo.get(document).on('mouseup', this.onMouseUp, this);
12046         }
12047     },
12048     // private
12049     onMouseUp : function(e){
12050         if(e.button == 0){
12051             this.el.removeClass("x-btn-click");
12052             Roo.get(document).un('mouseup', this.onMouseUp, this);
12053         }
12054     },
12055     // private
12056     onMenuShow : function(e){
12057         this.el.addClass("x-btn-menu-active");
12058     },
12059     // private
12060     onMenuHide : function(e){
12061         this.el.removeClass("x-btn-menu-active");
12062     }   
12063 });
12064
12065 // Private utility class used by Button
12066 Roo.ButtonToggleMgr = function(){
12067    var groups = {};
12068    
12069    function toggleGroup(btn, state){
12070        if(state){
12071            var g = groups[btn.toggleGroup];
12072            for(var i = 0, l = g.length; i < l; i++){
12073                if(g[i] != btn){
12074                    g[i].toggle(false);
12075                }
12076            }
12077        }
12078    }
12079    
12080    return {
12081        register : function(btn){
12082            if(!btn.toggleGroup){
12083                return;
12084            }
12085            var g = groups[btn.toggleGroup];
12086            if(!g){
12087                g = groups[btn.toggleGroup] = [];
12088            }
12089            g.push(btn);
12090            btn.on("toggle", toggleGroup);
12091        },
12092        
12093        unregister : function(btn){
12094            if(!btn.toggleGroup){
12095                return;
12096            }
12097            var g = groups[btn.toggleGroup];
12098            if(g){
12099                g.remove(btn);
12100                btn.un("toggle", toggleGroup);
12101            }
12102        }
12103    };
12104 }();/*
12105  * Based on:
12106  * Ext JS Library 1.1.1
12107  * Copyright(c) 2006-2007, Ext JS, LLC.
12108  *
12109  * Originally Released Under LGPL - original licence link has changed is not relivant.
12110  *
12111  * Fork - LGPL
12112  * <script type="text/javascript">
12113  */
12114  
12115 /**
12116  * @class Roo.SplitButton
12117  * @extends Roo.Button
12118  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
12119  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
12120  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
12121  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
12122  * @cfg {String} arrowTooltip The title attribute of the arrow
12123  * @constructor
12124  * Create a new menu button
12125  * @param {String/HTMLElement/Element} renderTo The element to append the button to
12126  * @param {Object} config The config object
12127  */
12128 Roo.SplitButton = function(renderTo, config){
12129     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12130     /**
12131      * @event arrowclick
12132      * Fires when this button's arrow is clicked
12133      * @param {SplitButton} this
12134      * @param {EventObject} e The click event
12135      */
12136     this.addEvents({"arrowclick":true});
12137 };
12138
12139 Roo.extend(Roo.SplitButton, Roo.Button, {
12140     render : function(renderTo){
12141         // this is one sweet looking template!
12142         var tpl = new Roo.Template(
12143             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12144             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12145             '<tr><td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><button class="x-btn-text" type="{1}">{0}</button></td></tr>',
12146             "</tbody></table></td><td>",
12147             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12148             '<tr><td class="x-btn-center"><button class="x-btn-menu-arrow-el" type="button">&#160;</button></td><td class="x-btn-right"><i>&#160;</i></td></tr>',
12149             "</tbody></table></td></tr></table>"
12150         );
12151         var btn = tpl.append(renderTo, [this.text, this.type], true);
12152         var btnEl = btn.child("button");
12153         if(this.cls){
12154             btn.addClass(this.cls);
12155         }
12156         if(this.icon){
12157             btnEl.setStyle('background-image', 'url(' +this.icon +')');
12158         }
12159         if(this.iconCls){
12160             btnEl.addClass(this.iconCls);
12161             if(!this.cls){
12162                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12163             }
12164         }
12165         this.el = btn;
12166         if(this.handleMouseEvents){
12167             btn.on("mouseover", this.onMouseOver, this);
12168             btn.on("mouseout", this.onMouseOut, this);
12169             btn.on("mousedown", this.onMouseDown, this);
12170             btn.on("mouseup", this.onMouseUp, this);
12171         }
12172         btn.on(this.clickEvent, this.onClick, this);
12173         if(this.tooltip){
12174             if(typeof this.tooltip == 'object'){
12175                 Roo.QuickTips.tips(Roo.apply({
12176                       target: btnEl.id
12177                 }, this.tooltip));
12178             } else {
12179                 btnEl.dom[this.tooltipType] = this.tooltip;
12180             }
12181         }
12182         if(this.arrowTooltip){
12183             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12184         }
12185         if(this.hidden){
12186             this.hide();
12187         }
12188         if(this.disabled){
12189             this.disable();
12190         }
12191         if(this.pressed){
12192             this.el.addClass("x-btn-pressed");
12193         }
12194         if(Roo.isIE && !Roo.isIE7){
12195             this.autoWidth.defer(1, this);
12196         }else{
12197             this.autoWidth();
12198         }
12199         if(this.menu){
12200             this.menu.on("show", this.onMenuShow, this);
12201             this.menu.on("hide", this.onMenuHide, this);
12202         }
12203         this.fireEvent('render', this);
12204     },
12205
12206     // private
12207     autoWidth : function(){
12208         if(this.el){
12209             var tbl = this.el.child("table:first");
12210             var tbl2 = this.el.child("table:last");
12211             this.el.setWidth("auto");
12212             tbl.setWidth("auto");
12213             if(Roo.isIE7 && Roo.isStrict){
12214                 var ib = this.el.child('button:first');
12215                 if(ib && ib.getWidth() > 20){
12216                     ib.clip();
12217                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12218                 }
12219             }
12220             if(this.minWidth){
12221                 if(this.hidden){
12222                     this.el.beginMeasure();
12223                 }
12224                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12225                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12226                 }
12227                 if(this.hidden){
12228                     this.el.endMeasure();
12229                 }
12230             }
12231             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12232         } 
12233     },
12234     /**
12235      * Sets this button's click handler
12236      * @param {Function} handler The function to call when the button is clicked
12237      * @param {Object} scope (optional) Scope for the function passed above
12238      */
12239     setHandler : function(handler, scope){
12240         this.handler = handler;
12241         this.scope = scope;  
12242     },
12243     
12244     /**
12245      * Sets this button's arrow click handler
12246      * @param {Function} handler The function to call when the arrow is clicked
12247      * @param {Object} scope (optional) Scope for the function passed above
12248      */
12249     setArrowHandler : function(handler, scope){
12250         this.arrowHandler = handler;
12251         this.scope = scope;  
12252     },
12253     
12254     /**
12255      * Focus the button
12256      */
12257     focus : function(){
12258         if(this.el){
12259             this.el.child("button:first").focus();
12260         }
12261     },
12262
12263     // private
12264     onClick : function(e){
12265         e.preventDefault();
12266         if(!this.disabled){
12267             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12268                 if(this.menu && !this.menu.isVisible()){
12269                     this.menu.show(this.el, this.menuAlign);
12270                 }
12271                 this.fireEvent("arrowclick", this, e);
12272                 if(this.arrowHandler){
12273                     this.arrowHandler.call(this.scope || this, this, e);
12274                 }
12275             }else{
12276                 this.fireEvent("click", this, e);
12277                 if(this.handler){
12278                     this.handler.call(this.scope || this, this, e);
12279                 }
12280             }
12281         }
12282     },
12283     // private
12284     onMouseDown : function(e){
12285         if(!this.disabled){
12286             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12287         }
12288     },
12289     // private
12290     onMouseUp : function(e){
12291         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12292     }   
12293 });
12294
12295
12296 // backwards compat
12297 Roo.MenuButton = Roo.SplitButton;/*
12298  * Based on:
12299  * Ext JS Library 1.1.1
12300  * Copyright(c) 2006-2007, Ext JS, LLC.
12301  *
12302  * Originally Released Under LGPL - original licence link has changed is not relivant.
12303  *
12304  * Fork - LGPL
12305  * <script type="text/javascript">
12306  */
12307
12308 /**
12309  * @class Roo.Toolbar
12310  * Basic Toolbar class.
12311  * @constructor
12312  * Creates a new Toolbar
12313  * @param {Object} container The config object
12314  */ 
12315 Roo.Toolbar = function(container, buttons, config)
12316 {
12317     /// old consturctor format still supported..
12318     if(container instanceof Array){ // omit the container for later rendering
12319         buttons = container;
12320         config = buttons;
12321         container = null;
12322     }
12323     if (typeof(container) == 'object' && container.xtype) {
12324         config = container;
12325         container = config.container;
12326         buttons = config.buttons || []; // not really - use items!!
12327     }
12328     var xitems = [];
12329     if (config && config.items) {
12330         xitems = config.items;
12331         delete config.items;
12332     }
12333     Roo.apply(this, config);
12334     this.buttons = buttons;
12335     
12336     if(container){
12337         this.render(container);
12338     }
12339     this.xitems = xitems;
12340     Roo.each(xitems, function(b) {
12341         this.add(b);
12342     }, this);
12343     
12344 };
12345
12346 Roo.Toolbar.prototype = {
12347     /**
12348      * @cfg {Array} items
12349      * array of button configs or elements to add (will be converted to a MixedCollection)
12350      */
12351     
12352     /**
12353      * @cfg {String/HTMLElement/Element} container
12354      * The id or element that will contain the toolbar
12355      */
12356     // private
12357     render : function(ct){
12358         this.el = Roo.get(ct);
12359         if(this.cls){
12360             this.el.addClass(this.cls);
12361         }
12362         // using a table allows for vertical alignment
12363         // 100% width is needed by Safari...
12364         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12365         this.tr = this.el.child("tr", true);
12366         var autoId = 0;
12367         this.items = new Roo.util.MixedCollection(false, function(o){
12368             return o.id || ("item" + (++autoId));
12369         });
12370         if(this.buttons){
12371             this.add.apply(this, this.buttons);
12372             delete this.buttons;
12373         }
12374     },
12375
12376     /**
12377      * Adds element(s) to the toolbar -- this function takes a variable number of 
12378      * arguments of mixed type and adds them to the toolbar.
12379      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12380      * <ul>
12381      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12382      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12383      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12384      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12385      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12386      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12387      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12388      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12389      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12390      * </ul>
12391      * @param {Mixed} arg2
12392      * @param {Mixed} etc.
12393      */
12394     add : function(){
12395         var a = arguments, l = a.length;
12396         for(var i = 0; i < l; i++){
12397             this._add(a[i]);
12398         }
12399     },
12400     // private..
12401     _add : function(el) {
12402         
12403         if (el.xtype) {
12404             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12405         }
12406         
12407         if (el.applyTo){ // some kind of form field
12408             return this.addField(el);
12409         } 
12410         if (el.render){ // some kind of Toolbar.Item
12411             return this.addItem(el);
12412         }
12413         if (typeof el == "string"){ // string
12414             if(el == "separator" || el == "-"){
12415                 return this.addSeparator();
12416             }
12417             if (el == " "){
12418                 return this.addSpacer();
12419             }
12420             if(el == "->"){
12421                 return this.addFill();
12422             }
12423             return this.addText(el);
12424             
12425         }
12426         if(el.tagName){ // element
12427             return this.addElement(el);
12428         }
12429         if(typeof el == "object"){ // must be button config?
12430             return this.addButton(el);
12431         }
12432         // and now what?!?!
12433         return false;
12434         
12435     },
12436     
12437     /**
12438      * Add an Xtype element
12439      * @param {Object} xtype Xtype Object
12440      * @return {Object} created Object
12441      */
12442     addxtype : function(e){
12443         return this.add(e);  
12444     },
12445     
12446     /**
12447      * Returns the Element for this toolbar.
12448      * @return {Roo.Element}
12449      */
12450     getEl : function(){
12451         return this.el;  
12452     },
12453     
12454     /**
12455      * Adds a separator
12456      * @return {Roo.Toolbar.Item} The separator item
12457      */
12458     addSeparator : function(){
12459         return this.addItem(new Roo.Toolbar.Separator());
12460     },
12461
12462     /**
12463      * Adds a spacer element
12464      * @return {Roo.Toolbar.Spacer} The spacer item
12465      */
12466     addSpacer : function(){
12467         return this.addItem(new Roo.Toolbar.Spacer());
12468     },
12469
12470     /**
12471      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12472      * @return {Roo.Toolbar.Fill} The fill item
12473      */
12474     addFill : function(){
12475         return this.addItem(new Roo.Toolbar.Fill());
12476     },
12477
12478     /**
12479      * Adds any standard HTML element to the toolbar
12480      * @param {String/HTMLElement/Element} el The element or id of the element to add
12481      * @return {Roo.Toolbar.Item} The element's item
12482      */
12483     addElement : function(el){
12484         return this.addItem(new Roo.Toolbar.Item(el));
12485     },
12486     /**
12487      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12488      * @type Roo.util.MixedCollection  
12489      */
12490     items : false,
12491      
12492     /**
12493      * Adds any Toolbar.Item or subclass
12494      * @param {Roo.Toolbar.Item} item
12495      * @return {Roo.Toolbar.Item} The item
12496      */
12497     addItem : function(item){
12498         var td = this.nextBlock();
12499         item.render(td);
12500         this.items.add(item);
12501         return item;
12502     },
12503     
12504     /**
12505      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12506      * @param {Object/Array} config A button config or array of configs
12507      * @return {Roo.Toolbar.Button/Array}
12508      */
12509     addButton : function(config){
12510         if(config instanceof Array){
12511             var buttons = [];
12512             for(var i = 0, len = config.length; i < len; i++) {
12513                 buttons.push(this.addButton(config[i]));
12514             }
12515             return buttons;
12516         }
12517         var b = config;
12518         if(!(config instanceof Roo.Toolbar.Button)){
12519             b = config.split ?
12520                 new Roo.Toolbar.SplitButton(config) :
12521                 new Roo.Toolbar.Button(config);
12522         }
12523         var td = this.nextBlock();
12524         b.render(td);
12525         this.items.add(b);
12526         return b;
12527     },
12528     
12529     /**
12530      * Adds text to the toolbar
12531      * @param {String} text The text to add
12532      * @return {Roo.Toolbar.Item} The element's item
12533      */
12534     addText : function(text){
12535         return this.addItem(new Roo.Toolbar.TextItem(text));
12536     },
12537     
12538     /**
12539      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12540      * @param {Number} index The index where the item is to be inserted
12541      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12542      * @return {Roo.Toolbar.Button/Item}
12543      */
12544     insertButton : function(index, item){
12545         if(item instanceof Array){
12546             var buttons = [];
12547             for(var i = 0, len = item.length; i < len; i++) {
12548                buttons.push(this.insertButton(index + i, item[i]));
12549             }
12550             return buttons;
12551         }
12552         if (!(item instanceof Roo.Toolbar.Button)){
12553            item = new Roo.Toolbar.Button(item);
12554         }
12555         var td = document.createElement("td");
12556         this.tr.insertBefore(td, this.tr.childNodes[index]);
12557         item.render(td);
12558         this.items.insert(index, item);
12559         return item;
12560     },
12561     
12562     /**
12563      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12564      * @param {Object} config
12565      * @return {Roo.Toolbar.Item} The element's item
12566      */
12567     addDom : function(config, returnEl){
12568         var td = this.nextBlock();
12569         Roo.DomHelper.overwrite(td, config);
12570         var ti = new Roo.Toolbar.Item(td.firstChild);
12571         ti.render(td);
12572         this.items.add(ti);
12573         return ti;
12574     },
12575
12576     /**
12577      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12578      * @type Roo.util.MixedCollection  
12579      */
12580     fields : false,
12581     
12582     /**
12583      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
12584      * Note: the field should not have been rendered yet. For a field that has already been
12585      * rendered, use {@link #addElement}.
12586      * @param {Roo.form.Field} field
12587      * @return {Roo.ToolbarItem}
12588      */
12589      
12590       
12591     addField : function(field) {
12592         if (!this.fields) {
12593             var autoId = 0;
12594             this.fields = new Roo.util.MixedCollection(false, function(o){
12595                 return o.id || ("item" + (++autoId));
12596             });
12597
12598         }
12599         
12600         var td = this.nextBlock();
12601         field.render(td);
12602         var ti = new Roo.Toolbar.Item(td.firstChild);
12603         ti.render(td);
12604         this.items.add(ti);
12605         this.fields.add(field);
12606         return ti;
12607     },
12608     /**
12609      * Hide the toolbar
12610      * @method hide
12611      */
12612      
12613       
12614     hide : function()
12615     {
12616         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12617         this.el.child('div').hide();
12618     },
12619     /**
12620      * Show the toolbar
12621      * @method show
12622      */
12623     show : function()
12624     {
12625         this.el.child('div').show();
12626     },
12627       
12628     // private
12629     nextBlock : function(){
12630         var td = document.createElement("td");
12631         this.tr.appendChild(td);
12632         return td;
12633     },
12634
12635     // private
12636     destroy : function(){
12637         if(this.items){ // rendered?
12638             Roo.destroy.apply(Roo, this.items.items);
12639         }
12640         if(this.fields){ // rendered?
12641             Roo.destroy.apply(Roo, this.fields.items);
12642         }
12643         Roo.Element.uncache(this.el, this.tr);
12644     }
12645 };
12646
12647 /**
12648  * @class Roo.Toolbar.Item
12649  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12650  * @constructor
12651  * Creates a new Item
12652  * @param {HTMLElement} el 
12653  */
12654 Roo.Toolbar.Item = function(el){
12655     this.el = Roo.getDom(el);
12656     this.id = Roo.id(this.el);
12657     this.hidden = false;
12658 };
12659
12660 Roo.Toolbar.Item.prototype = {
12661     
12662     /**
12663      * Get this item's HTML Element
12664      * @return {HTMLElement}
12665      */
12666     getEl : function(){
12667        return this.el;  
12668     },
12669
12670     // private
12671     render : function(td){
12672         this.td = td;
12673         td.appendChild(this.el);
12674     },
12675     
12676     /**
12677      * Removes and destroys this item.
12678      */
12679     destroy : function(){
12680         this.td.parentNode.removeChild(this.td);
12681     },
12682     
12683     /**
12684      * Shows this item.
12685      */
12686     show: function(){
12687         this.hidden = false;
12688         this.td.style.display = "";
12689     },
12690     
12691     /**
12692      * Hides this item.
12693      */
12694     hide: function(){
12695         this.hidden = true;
12696         this.td.style.display = "none";
12697     },
12698     
12699     /**
12700      * Convenience function for boolean show/hide.
12701      * @param {Boolean} visible true to show/false to hide
12702      */
12703     setVisible: function(visible){
12704         if(visible) {
12705             this.show();
12706         }else{
12707             this.hide();
12708         }
12709     },
12710     
12711     /**
12712      * Try to focus this item.
12713      */
12714     focus : function(){
12715         Roo.fly(this.el).focus();
12716     },
12717     
12718     /**
12719      * Disables this item.
12720      */
12721     disable : function(){
12722         Roo.fly(this.td).addClass("x-item-disabled");
12723         this.disabled = true;
12724         this.el.disabled = true;
12725     },
12726     
12727     /**
12728      * Enables this item.
12729      */
12730     enable : function(){
12731         Roo.fly(this.td).removeClass("x-item-disabled");
12732         this.disabled = false;
12733         this.el.disabled = false;
12734     }
12735 };
12736
12737
12738 /**
12739  * @class Roo.Toolbar.Separator
12740  * @extends Roo.Toolbar.Item
12741  * A simple toolbar separator class
12742  * @constructor
12743  * Creates a new Separator
12744  */
12745 Roo.Toolbar.Separator = function(){
12746     var s = document.createElement("span");
12747     s.className = "ytb-sep";
12748     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12749 };
12750 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12751     enable:Roo.emptyFn,
12752     disable:Roo.emptyFn,
12753     focus:Roo.emptyFn
12754 });
12755
12756 /**
12757  * @class Roo.Toolbar.Spacer
12758  * @extends Roo.Toolbar.Item
12759  * A simple element that adds extra horizontal space to a toolbar.
12760  * @constructor
12761  * Creates a new Spacer
12762  */
12763 Roo.Toolbar.Spacer = function(){
12764     var s = document.createElement("div");
12765     s.className = "ytb-spacer";
12766     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12767 };
12768 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12769     enable:Roo.emptyFn,
12770     disable:Roo.emptyFn,
12771     focus:Roo.emptyFn
12772 });
12773
12774 /**
12775  * @class Roo.Toolbar.Fill
12776  * @extends Roo.Toolbar.Spacer
12777  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12778  * @constructor
12779  * Creates a new Spacer
12780  */
12781 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12782     // private
12783     render : function(td){
12784         td.style.width = '100%';
12785         Roo.Toolbar.Fill.superclass.render.call(this, td);
12786     }
12787 });
12788
12789 /**
12790  * @class Roo.Toolbar.TextItem
12791  * @extends Roo.Toolbar.Item
12792  * A simple class that renders text directly into a toolbar.
12793  * @constructor
12794  * Creates a new TextItem
12795  * @param {String} text
12796  */
12797 Roo.Toolbar.TextItem = function(text){
12798     if (typeof(text) == 'object') {
12799         text = text.text;
12800     }
12801     var s = document.createElement("span");
12802     s.className = "ytb-text";
12803     s.innerHTML = text;
12804     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12805 };
12806 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12807     enable:Roo.emptyFn,
12808     disable:Roo.emptyFn,
12809     focus:Roo.emptyFn
12810 });
12811
12812 /**
12813  * @class Roo.Toolbar.Button
12814  * @extends Roo.Button
12815  * A button that renders into a toolbar.
12816  * @constructor
12817  * Creates a new Button
12818  * @param {Object} config A standard {@link Roo.Button} config object
12819  */
12820 Roo.Toolbar.Button = function(config){
12821     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12822 };
12823 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12824     render : function(td){
12825         this.td = td;
12826         Roo.Toolbar.Button.superclass.render.call(this, td);
12827     },
12828     
12829     /**
12830      * Removes and destroys this button
12831      */
12832     destroy : function(){
12833         Roo.Toolbar.Button.superclass.destroy.call(this);
12834         this.td.parentNode.removeChild(this.td);
12835     },
12836     
12837     /**
12838      * Shows this button
12839      */
12840     show: function(){
12841         this.hidden = false;
12842         this.td.style.display = "";
12843     },
12844     
12845     /**
12846      * Hides this button
12847      */
12848     hide: function(){
12849         this.hidden = true;
12850         this.td.style.display = "none";
12851     },
12852
12853     /**
12854      * Disables this item
12855      */
12856     disable : function(){
12857         Roo.fly(this.td).addClass("x-item-disabled");
12858         this.disabled = true;
12859     },
12860
12861     /**
12862      * Enables this item
12863      */
12864     enable : function(){
12865         Roo.fly(this.td).removeClass("x-item-disabled");
12866         this.disabled = false;
12867     }
12868 });
12869 // backwards compat
12870 Roo.ToolbarButton = Roo.Toolbar.Button;
12871
12872 /**
12873  * @class Roo.Toolbar.SplitButton
12874  * @extends Roo.SplitButton
12875  * A menu button that renders into a toolbar.
12876  * @constructor
12877  * Creates a new SplitButton
12878  * @param {Object} config A standard {@link Roo.SplitButton} config object
12879  */
12880 Roo.Toolbar.SplitButton = function(config){
12881     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12882 };
12883 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12884     render : function(td){
12885         this.td = td;
12886         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12887     },
12888     
12889     /**
12890      * Removes and destroys this button
12891      */
12892     destroy : function(){
12893         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12894         this.td.parentNode.removeChild(this.td);
12895     },
12896     
12897     /**
12898      * Shows this button
12899      */
12900     show: function(){
12901         this.hidden = false;
12902         this.td.style.display = "";
12903     },
12904     
12905     /**
12906      * Hides this button
12907      */
12908     hide: function(){
12909         this.hidden = true;
12910         this.td.style.display = "none";
12911     }
12912 });
12913
12914 // backwards compat
12915 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12916  * Based on:
12917  * Ext JS Library 1.1.1
12918  * Copyright(c) 2006-2007, Ext JS, LLC.
12919  *
12920  * Originally Released Under LGPL - original licence link has changed is not relivant.
12921  *
12922  * Fork - LGPL
12923  * <script type="text/javascript">
12924  */
12925  
12926 /**
12927  * @class Roo.PagingToolbar
12928  * @extends Roo.Toolbar
12929  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12930  * @constructor
12931  * Create a new PagingToolbar
12932  * @param {Object} config The config object
12933  */
12934 Roo.PagingToolbar = function(el, ds, config)
12935 {
12936     // old args format still supported... - xtype is prefered..
12937     if (typeof(el) == 'object' && el.xtype) {
12938         // created from xtype...
12939         config = el;
12940         ds = el.dataSource;
12941         el = config.container;
12942     }
12943     var items = [];
12944     if (config.items) {
12945         items = config.items;
12946         config.items = [];
12947     }
12948     
12949     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12950     this.ds = ds;
12951     this.cursor = 0;
12952     this.renderButtons(this.el);
12953     this.bind(ds);
12954     
12955     // supprot items array.
12956    
12957     Roo.each(items, function(e) {
12958         this.add(Roo.factory(e));
12959     },this);
12960     
12961 };
12962
12963 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12964     /**
12965      * @cfg {Roo.data.Store} dataSource
12966      * The underlying data store providing the paged data
12967      */
12968     /**
12969      * @cfg {String/HTMLElement/Element} container
12970      * container The id or element that will contain the toolbar
12971      */
12972     /**
12973      * @cfg {Boolean} displayInfo
12974      * True to display the displayMsg (defaults to false)
12975      */
12976     /**
12977      * @cfg {Number} pageSize
12978      * The number of records to display per page (defaults to 20)
12979      */
12980     pageSize: 20,
12981     /**
12982      * @cfg {String} displayMsg
12983      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12984      */
12985     displayMsg : 'Displaying {0} - {1} of {2}',
12986     /**
12987      * @cfg {String} emptyMsg
12988      * The message to display when no records are found (defaults to "No data to display")
12989      */
12990     emptyMsg : 'No data to display',
12991     /**
12992      * Customizable piece of the default paging text (defaults to "Page")
12993      * @type String
12994      */
12995     beforePageText : "Page",
12996     /**
12997      * Customizable piece of the default paging text (defaults to "of %0")
12998      * @type String
12999      */
13000     afterPageText : "of {0}",
13001     /**
13002      * Customizable piece of the default paging text (defaults to "First Page")
13003      * @type String
13004      */
13005     firstText : "First Page",
13006     /**
13007      * Customizable piece of the default paging text (defaults to "Previous Page")
13008      * @type String
13009      */
13010     prevText : "Previous Page",
13011     /**
13012      * Customizable piece of the default paging text (defaults to "Next Page")
13013      * @type String
13014      */
13015     nextText : "Next Page",
13016     /**
13017      * Customizable piece of the default paging text (defaults to "Last Page")
13018      * @type String
13019      */
13020     lastText : "Last Page",
13021     /**
13022      * Customizable piece of the default paging text (defaults to "Refresh")
13023      * @type String
13024      */
13025     refreshText : "Refresh",
13026
13027     // private
13028     renderButtons : function(el){
13029         Roo.PagingToolbar.superclass.render.call(this, el);
13030         this.first = this.addButton({
13031             tooltip: this.firstText,
13032             cls: "x-btn-icon x-grid-page-first",
13033             disabled: true,
13034             handler: this.onClick.createDelegate(this, ["first"])
13035         });
13036         this.prev = this.addButton({
13037             tooltip: this.prevText,
13038             cls: "x-btn-icon x-grid-page-prev",
13039             disabled: true,
13040             handler: this.onClick.createDelegate(this, ["prev"])
13041         });
13042         //this.addSeparator();
13043         this.add(this.beforePageText);
13044         this.field = Roo.get(this.addDom({
13045            tag: "input",
13046            type: "text",
13047            size: "3",
13048            value: "1",
13049            cls: "x-grid-page-number"
13050         }).el);
13051         this.field.on("keydown", this.onPagingKeydown, this);
13052         this.field.on("focus", function(){this.dom.select();});
13053         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
13054         this.field.setHeight(18);
13055         //this.addSeparator();
13056         this.next = this.addButton({
13057             tooltip: this.nextText,
13058             cls: "x-btn-icon x-grid-page-next",
13059             disabled: true,
13060             handler: this.onClick.createDelegate(this, ["next"])
13061         });
13062         this.last = this.addButton({
13063             tooltip: this.lastText,
13064             cls: "x-btn-icon x-grid-page-last",
13065             disabled: true,
13066             handler: this.onClick.createDelegate(this, ["last"])
13067         });
13068         //this.addSeparator();
13069         this.loading = this.addButton({
13070             tooltip: this.refreshText,
13071             cls: "x-btn-icon x-grid-loading",
13072             handler: this.onClick.createDelegate(this, ["refresh"])
13073         });
13074
13075         if(this.displayInfo){
13076             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
13077         }
13078     },
13079
13080     // private
13081     updateInfo : function(){
13082         if(this.displayEl){
13083             var count = this.ds.getCount();
13084             var msg = count == 0 ?
13085                 this.emptyMsg :
13086                 String.format(
13087                     this.displayMsg,
13088                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
13089                 );
13090             this.displayEl.update(msg);
13091         }
13092     },
13093
13094     // private
13095     onLoad : function(ds, r, o){
13096        this.cursor = o.params ? o.params.start : 0;
13097        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
13098
13099        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
13100        this.field.dom.value = ap;
13101        this.first.setDisabled(ap == 1);
13102        this.prev.setDisabled(ap == 1);
13103        this.next.setDisabled(ap == ps);
13104        this.last.setDisabled(ap == ps);
13105        this.loading.enable();
13106        this.updateInfo();
13107     },
13108
13109     // private
13110     getPageData : function(){
13111         var total = this.ds.getTotalCount();
13112         return {
13113             total : total,
13114             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
13115             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
13116         };
13117     },
13118
13119     // private
13120     onLoadError : function(){
13121         this.loading.enable();
13122     },
13123
13124     // private
13125     onPagingKeydown : function(e){
13126         var k = e.getKey();
13127         var d = this.getPageData();
13128         if(k == e.RETURN){
13129             var v = this.field.dom.value, pageNum;
13130             if(!v || isNaN(pageNum = parseInt(v, 10))){
13131                 this.field.dom.value = d.activePage;
13132                 return;
13133             }
13134             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13135             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13136             e.stopEvent();
13137         }
13138         else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
13139         {
13140           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13141           this.field.dom.value = pageNum;
13142           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13143           e.stopEvent();
13144         }
13145         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13146         {
13147           var v = this.field.dom.value, pageNum; 
13148           var increment = (e.shiftKey) ? 10 : 1;
13149           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13150             increment *= -1;
13151           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13152             this.field.dom.value = d.activePage;
13153             return;
13154           }
13155           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13156           {
13157             this.field.dom.value = parseInt(v, 10) + increment;
13158             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13159             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13160           }
13161           e.stopEvent();
13162         }
13163     },
13164
13165     // private
13166     beforeLoad : function(){
13167         if(this.loading){
13168             this.loading.disable();
13169         }
13170     },
13171
13172     // private
13173     onClick : function(which){
13174         var ds = this.ds;
13175         switch(which){
13176             case "first":
13177                 ds.load({params:{start: 0, limit: this.pageSize}});
13178             break;
13179             case "prev":
13180                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13181             break;
13182             case "next":
13183                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13184             break;
13185             case "last":
13186                 var total = ds.getTotalCount();
13187                 var extra = total % this.pageSize;
13188                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13189                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13190             break;
13191             case "refresh":
13192                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13193             break;
13194         }
13195     },
13196
13197     /**
13198      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13199      * @param {Roo.data.Store} store The data store to unbind
13200      */
13201     unbind : function(ds){
13202         ds.un("beforeload", this.beforeLoad, this);
13203         ds.un("load", this.onLoad, this);
13204         ds.un("loadexception", this.onLoadError, this);
13205         ds.un("remove", this.updateInfo, this);
13206         ds.un("add", this.updateInfo, this);
13207         this.ds = undefined;
13208     },
13209
13210     /**
13211      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13212      * @param {Roo.data.Store} store The data store to bind
13213      */
13214     bind : function(ds){
13215         ds.on("beforeload", this.beforeLoad, this);
13216         ds.on("load", this.onLoad, this);
13217         ds.on("loadexception", this.onLoadError, this);
13218         ds.on("remove", this.updateInfo, this);
13219         ds.on("add", this.updateInfo, this);
13220         this.ds = ds;
13221     }
13222 });/*
13223  * Based on:
13224  * Ext JS Library 1.1.1
13225  * Copyright(c) 2006-2007, Ext JS, LLC.
13226  *
13227  * Originally Released Under LGPL - original licence link has changed is not relivant.
13228  *
13229  * Fork - LGPL
13230  * <script type="text/javascript">
13231  */
13232
13233 /**
13234  * @class Roo.Resizable
13235  * @extends Roo.util.Observable
13236  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13237  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13238  * 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
13239  * the element will be wrapped for you automatically.</p>
13240  * <p>Here is the list of valid resize handles:</p>
13241  * <pre>
13242 Value   Description
13243 ------  -------------------
13244  'n'     north
13245  's'     south
13246  'e'     east
13247  'w'     west
13248  'nw'    northwest
13249  'sw'    southwest
13250  'se'    southeast
13251  'ne'    northeast
13252  'hd'    horizontal drag
13253  'all'   all
13254 </pre>
13255  * <p>Here's an example showing the creation of a typical Resizable:</p>
13256  * <pre><code>
13257 var resizer = new Roo.Resizable("element-id", {
13258     handles: 'all',
13259     minWidth: 200,
13260     minHeight: 100,
13261     maxWidth: 500,
13262     maxHeight: 400,
13263     pinned: true
13264 });
13265 resizer.on("resize", myHandler);
13266 </code></pre>
13267  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13268  * resizer.east.setDisplayed(false);</p>
13269  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13270  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13271  * resize operation's new size (defaults to [0, 0])
13272  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13273  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13274  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13275  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13276  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13277  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13278  * @cfg {Number} width The width of the element in pixels (defaults to null)
13279  * @cfg {Number} height The height of the element in pixels (defaults to null)
13280  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13281  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13282  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13283  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13284  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13285  * in favor of the handles config option (defaults to false)
13286  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13287  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13288  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13289  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13290  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13291  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13292  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13293  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13294  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13295  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13296  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13297  * @constructor
13298  * Create a new resizable component
13299  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13300  * @param {Object} config configuration options
13301   */
13302 Roo.Resizable = function(el, config)
13303 {
13304     this.el = Roo.get(el);
13305
13306     if(config && config.wrap){
13307         config.resizeChild = this.el;
13308         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13309         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13310         this.el.setStyle("overflow", "hidden");
13311         this.el.setPositioning(config.resizeChild.getPositioning());
13312         config.resizeChild.clearPositioning();
13313         if(!config.width || !config.height){
13314             var csize = config.resizeChild.getSize();
13315             this.el.setSize(csize.width, csize.height);
13316         }
13317         if(config.pinned && !config.adjustments){
13318             config.adjustments = "auto";
13319         }
13320     }
13321
13322     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13323     this.proxy.unselectable();
13324     this.proxy.enableDisplayMode('block');
13325
13326     Roo.apply(this, config);
13327
13328     if(this.pinned){
13329         this.disableTrackOver = true;
13330         this.el.addClass("x-resizable-pinned");
13331     }
13332     // if the element isn't positioned, make it relative
13333     var position = this.el.getStyle("position");
13334     if(position != "absolute" && position != "fixed"){
13335         this.el.setStyle("position", "relative");
13336     }
13337     if(!this.handles){ // no handles passed, must be legacy style
13338         this.handles = 's,e,se';
13339         if(this.multiDirectional){
13340             this.handles += ',n,w';
13341         }
13342     }
13343     if(this.handles == "all"){
13344         this.handles = "n s e w ne nw se sw";
13345     }
13346     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13347     var ps = Roo.Resizable.positions;
13348     for(var i = 0, len = hs.length; i < len; i++){
13349         if(hs[i] && ps[hs[i]]){
13350             var pos = ps[hs[i]];
13351             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13352         }
13353     }
13354     // legacy
13355     this.corner = this.southeast;
13356     
13357     // updateBox = the box can move..
13358     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13359         this.updateBox = true;
13360     }
13361
13362     this.activeHandle = null;
13363
13364     if(this.resizeChild){
13365         if(typeof this.resizeChild == "boolean"){
13366             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13367         }else{
13368             this.resizeChild = Roo.get(this.resizeChild, true);
13369         }
13370     }
13371     
13372     if(this.adjustments == "auto"){
13373         var rc = this.resizeChild;
13374         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13375         if(rc && (hw || hn)){
13376             rc.position("relative");
13377             rc.setLeft(hw ? hw.el.getWidth() : 0);
13378             rc.setTop(hn ? hn.el.getHeight() : 0);
13379         }
13380         this.adjustments = [
13381             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13382             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13383         ];
13384     }
13385
13386     if(this.draggable){
13387         this.dd = this.dynamic ?
13388             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13389         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13390     }
13391
13392     // public events
13393     this.addEvents({
13394         /**
13395          * @event beforeresize
13396          * Fired before resize is allowed. Set enabled to false to cancel resize.
13397          * @param {Roo.Resizable} this
13398          * @param {Roo.EventObject} e The mousedown event
13399          */
13400         "beforeresize" : true,
13401         /**
13402          * @event resize
13403          * Fired after a resize.
13404          * @param {Roo.Resizable} this
13405          * @param {Number} width The new width
13406          * @param {Number} height The new height
13407          * @param {Roo.EventObject} e The mouseup event
13408          */
13409         "resize" : true
13410     });
13411
13412     if(this.width !== null && this.height !== null){
13413         this.resizeTo(this.width, this.height);
13414     }else{
13415         this.updateChildSize();
13416     }
13417     if(Roo.isIE){
13418         this.el.dom.style.zoom = 1;
13419     }
13420     Roo.Resizable.superclass.constructor.call(this);
13421 };
13422
13423 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13424         resizeChild : false,
13425         adjustments : [0, 0],
13426         minWidth : 5,
13427         minHeight : 5,
13428         maxWidth : 10000,
13429         maxHeight : 10000,
13430         enabled : true,
13431         animate : false,
13432         duration : .35,
13433         dynamic : false,
13434         handles : false,
13435         multiDirectional : false,
13436         disableTrackOver : false,
13437         easing : 'easeOutStrong',
13438         widthIncrement : 0,
13439         heightIncrement : 0,
13440         pinned : false,
13441         width : null,
13442         height : null,
13443         preserveRatio : false,
13444         transparent: false,
13445         minX: 0,
13446         minY: 0,
13447         draggable: false,
13448
13449         /**
13450          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13451          */
13452         constrainTo: undefined,
13453         /**
13454          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13455          */
13456         resizeRegion: undefined,
13457
13458
13459     /**
13460      * Perform a manual resize
13461      * @param {Number} width
13462      * @param {Number} height
13463      */
13464     resizeTo : function(width, height){
13465         this.el.setSize(width, height);
13466         this.updateChildSize();
13467         this.fireEvent("resize", this, width, height, null);
13468     },
13469
13470     // private
13471     startSizing : function(e, handle){
13472         this.fireEvent("beforeresize", this, e);
13473         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13474
13475             if(!this.overlay){
13476                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13477                 this.overlay.unselectable();
13478                 this.overlay.enableDisplayMode("block");
13479                 this.overlay.on("mousemove", this.onMouseMove, this);
13480                 this.overlay.on("mouseup", this.onMouseUp, this);
13481             }
13482             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13483
13484             this.resizing = true;
13485             this.startBox = this.el.getBox();
13486             this.startPoint = e.getXY();
13487             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13488                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13489
13490             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13491             this.overlay.show();
13492
13493             if(this.constrainTo) {
13494                 var ct = Roo.get(this.constrainTo);
13495                 this.resizeRegion = ct.getRegion().adjust(
13496                     ct.getFrameWidth('t'),
13497                     ct.getFrameWidth('l'),
13498                     -ct.getFrameWidth('b'),
13499                     -ct.getFrameWidth('r')
13500                 );
13501             }
13502
13503             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13504             this.proxy.show();
13505             this.proxy.setBox(this.startBox);
13506             if(!this.dynamic){
13507                 this.proxy.setStyle('visibility', 'visible');
13508             }
13509         }
13510     },
13511
13512     // private
13513     onMouseDown : function(handle, e){
13514         if(this.enabled){
13515             e.stopEvent();
13516             this.activeHandle = handle;
13517             this.startSizing(e, handle);
13518         }
13519     },
13520
13521     // private
13522     onMouseUp : function(e){
13523         var size = this.resizeElement();
13524         this.resizing = false;
13525         this.handleOut();
13526         this.overlay.hide();
13527         this.proxy.hide();
13528         this.fireEvent("resize", this, size.width, size.height, e);
13529     },
13530
13531     // private
13532     updateChildSize : function(){
13533         if(this.resizeChild){
13534             var el = this.el;
13535             var child = this.resizeChild;
13536             var adj = this.adjustments;
13537             if(el.dom.offsetWidth){
13538                 var b = el.getSize(true);
13539                 child.setSize(b.width+adj[0], b.height+adj[1]);
13540             }
13541             // Second call here for IE
13542             // The first call enables instant resizing and
13543             // the second call corrects scroll bars if they
13544             // exist
13545             if(Roo.isIE){
13546                 setTimeout(function(){
13547                     if(el.dom.offsetWidth){
13548                         var b = el.getSize(true);
13549                         child.setSize(b.width+adj[0], b.height+adj[1]);
13550                     }
13551                 }, 10);
13552             }
13553         }
13554     },
13555
13556     // private
13557     snap : function(value, inc, min){
13558         if(!inc || !value) return value;
13559         var newValue = value;
13560         var m = value % inc;
13561         if(m > 0){
13562             if(m > (inc/2)){
13563                 newValue = value + (inc-m);
13564             }else{
13565                 newValue = value - m;
13566             }
13567         }
13568         return Math.max(min, newValue);
13569     },
13570
13571     // private
13572     resizeElement : function(){
13573         var box = this.proxy.getBox();
13574         if(this.updateBox){
13575             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13576         }else{
13577             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13578         }
13579         this.updateChildSize();
13580         if(!this.dynamic){
13581             this.proxy.hide();
13582         }
13583         return box;
13584     },
13585
13586     // private
13587     constrain : function(v, diff, m, mx){
13588         if(v - diff < m){
13589             diff = v - m;
13590         }else if(v - diff > mx){
13591             diff = mx - v;
13592         }
13593         return diff;
13594     },
13595
13596     // private
13597     onMouseMove : function(e){
13598         if(this.enabled){
13599             try{// try catch so if something goes wrong the user doesn't get hung
13600
13601             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13602                 return;
13603             }
13604
13605             //var curXY = this.startPoint;
13606             var curSize = this.curSize || this.startBox;
13607             var x = this.startBox.x, y = this.startBox.y;
13608             var ox = x, oy = y;
13609             var w = curSize.width, h = curSize.height;
13610             var ow = w, oh = h;
13611             var mw = this.minWidth, mh = this.minHeight;
13612             var mxw = this.maxWidth, mxh = this.maxHeight;
13613             var wi = this.widthIncrement;
13614             var hi = this.heightIncrement;
13615
13616             var eventXY = e.getXY();
13617             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13618             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13619
13620             var pos = this.activeHandle.position;
13621
13622             switch(pos){
13623                 case "east":
13624                     w += diffX;
13625                     w = Math.min(Math.max(mw, w), mxw);
13626                     break;
13627              
13628                 case "south":
13629                     h += diffY;
13630                     h = Math.min(Math.max(mh, h), mxh);
13631                     break;
13632                 case "southeast":
13633                     w += diffX;
13634                     h += diffY;
13635                     w = Math.min(Math.max(mw, w), mxw);
13636                     h = Math.min(Math.max(mh, h), mxh);
13637                     break;
13638                 case "north":
13639                     diffY = this.constrain(h, diffY, mh, mxh);
13640                     y += diffY;
13641                     h -= diffY;
13642                     break;
13643                 case "hdrag":
13644                     
13645                     if (wi) {
13646                         var adiffX = Math.abs(diffX);
13647                         var sub = (adiffX % wi); // how much 
13648                         if (sub > (wi/2)) { // far enough to snap
13649                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13650                         } else {
13651                             // remove difference.. 
13652                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13653                         }
13654                     }
13655                     x += diffX;
13656                     x = Math.max(this.minX, x);
13657                     break;
13658                 case "west":
13659                     diffX = this.constrain(w, diffX, mw, mxw);
13660                     x += diffX;
13661                     w -= diffX;
13662                     break;
13663                 case "northeast":
13664                     w += diffX;
13665                     w = Math.min(Math.max(mw, w), mxw);
13666                     diffY = this.constrain(h, diffY, mh, mxh);
13667                     y += diffY;
13668                     h -= diffY;
13669                     break;
13670                 case "northwest":
13671                     diffX = this.constrain(w, diffX, mw, mxw);
13672                     diffY = this.constrain(h, diffY, mh, mxh);
13673                     y += diffY;
13674                     h -= diffY;
13675                     x += diffX;
13676                     w -= diffX;
13677                     break;
13678                case "southwest":
13679                     diffX = this.constrain(w, diffX, mw, mxw);
13680                     h += diffY;
13681                     h = Math.min(Math.max(mh, h), mxh);
13682                     x += diffX;
13683                     w -= diffX;
13684                     break;
13685             }
13686
13687             var sw = this.snap(w, wi, mw);
13688             var sh = this.snap(h, hi, mh);
13689             if(sw != w || sh != h){
13690                 switch(pos){
13691                     case "northeast":
13692                         y -= sh - h;
13693                     break;
13694                     case "north":
13695                         y -= sh - h;
13696                         break;
13697                     case "southwest":
13698                         x -= sw - w;
13699                     break;
13700                     case "west":
13701                         x -= sw - w;
13702                         break;
13703                     case "northwest":
13704                         x -= sw - w;
13705                         y -= sh - h;
13706                     break;
13707                 }
13708                 w = sw;
13709                 h = sh;
13710             }
13711
13712             if(this.preserveRatio){
13713                 switch(pos){
13714                     case "southeast":
13715                     case "east":
13716                         h = oh * (w/ow);
13717                         h = Math.min(Math.max(mh, h), mxh);
13718                         w = ow * (h/oh);
13719                        break;
13720                     case "south":
13721                         w = ow * (h/oh);
13722                         w = Math.min(Math.max(mw, w), mxw);
13723                         h = oh * (w/ow);
13724                         break;
13725                     case "northeast":
13726                         w = ow * (h/oh);
13727                         w = Math.min(Math.max(mw, w), mxw);
13728                         h = oh * (w/ow);
13729                     break;
13730                     case "north":
13731                         var tw = w;
13732                         w = ow * (h/oh);
13733                         w = Math.min(Math.max(mw, w), mxw);
13734                         h = oh * (w/ow);
13735                         x += (tw - w) / 2;
13736                         break;
13737                     case "southwest":
13738                         h = oh * (w/ow);
13739                         h = Math.min(Math.max(mh, h), mxh);
13740                         var tw = w;
13741                         w = ow * (h/oh);
13742                         x += tw - w;
13743                         break;
13744                     case "west":
13745                         var th = h;
13746                         h = oh * (w/ow);
13747                         h = Math.min(Math.max(mh, h), mxh);
13748                         y += (th - h) / 2;
13749                         var tw = w;
13750                         w = ow * (h/oh);
13751                         x += tw - w;
13752                        break;
13753                     case "northwest":
13754                         var tw = w;
13755                         var th = h;
13756                         h = oh * (w/ow);
13757                         h = Math.min(Math.max(mh, h), mxh);
13758                         w = ow * (h/oh);
13759                         y += th - h;
13760                         x += tw - w;
13761                        break;
13762
13763                 }
13764             }
13765             if (pos == 'hdrag') {
13766                 w = ow;
13767             }
13768             this.proxy.setBounds(x, y, w, h);
13769             if(this.dynamic){
13770                 this.resizeElement();
13771             }
13772             }catch(e){}
13773         }
13774     },
13775
13776     // private
13777     handleOver : function(){
13778         if(this.enabled){
13779             this.el.addClass("x-resizable-over");
13780         }
13781     },
13782
13783     // private
13784     handleOut : function(){
13785         if(!this.resizing){
13786             this.el.removeClass("x-resizable-over");
13787         }
13788     },
13789
13790     /**
13791      * Returns the element this component is bound to.
13792      * @return {Roo.Element}
13793      */
13794     getEl : function(){
13795         return this.el;
13796     },
13797
13798     /**
13799      * Returns the resizeChild element (or null).
13800      * @return {Roo.Element}
13801      */
13802     getResizeChild : function(){
13803         return this.resizeChild;
13804     },
13805
13806     /**
13807      * Destroys this resizable. If the element was wrapped and
13808      * removeEl is not true then the element remains.
13809      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13810      */
13811     destroy : function(removeEl){
13812         this.proxy.remove();
13813         if(this.overlay){
13814             this.overlay.removeAllListeners();
13815             this.overlay.remove();
13816         }
13817         var ps = Roo.Resizable.positions;
13818         for(var k in ps){
13819             if(typeof ps[k] != "function" && this[ps[k]]){
13820                 var h = this[ps[k]];
13821                 h.el.removeAllListeners();
13822                 h.el.remove();
13823             }
13824         }
13825         if(removeEl){
13826             this.el.update("");
13827             this.el.remove();
13828         }
13829     }
13830 });
13831
13832 // private
13833 // hash to map config positions to true positions
13834 Roo.Resizable.positions = {
13835     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13836     hd: "hdrag"
13837 };
13838
13839 // private
13840 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13841     if(!this.tpl){
13842         // only initialize the template if resizable is used
13843         var tpl = Roo.DomHelper.createTemplate(
13844             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13845         );
13846         tpl.compile();
13847         Roo.Resizable.Handle.prototype.tpl = tpl;
13848     }
13849     this.position = pos;
13850     this.rz = rz;
13851     // show north drag fro topdra
13852     var handlepos = pos == 'hdrag' ? 'north' : pos;
13853     
13854     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13855     if (pos == 'hdrag') {
13856         this.el.setStyle('cursor', 'pointer');
13857     }
13858     this.el.unselectable();
13859     if(transparent){
13860         this.el.setOpacity(0);
13861     }
13862     this.el.on("mousedown", this.onMouseDown, this);
13863     if(!disableTrackOver){
13864         this.el.on("mouseover", this.onMouseOver, this);
13865         this.el.on("mouseout", this.onMouseOut, this);
13866     }
13867 };
13868
13869 // private
13870 Roo.Resizable.Handle.prototype = {
13871     afterResize : function(rz){
13872         // do nothing
13873     },
13874     // private
13875     onMouseDown : function(e){
13876         this.rz.onMouseDown(this, e);
13877     },
13878     // private
13879     onMouseOver : function(e){
13880         this.rz.handleOver(this, e);
13881     },
13882     // private
13883     onMouseOut : function(e){
13884         this.rz.handleOut(this, e);
13885     }
13886 };/*
13887  * Based on:
13888  * Ext JS Library 1.1.1
13889  * Copyright(c) 2006-2007, Ext JS, LLC.
13890  *
13891  * Originally Released Under LGPL - original licence link has changed is not relivant.
13892  *
13893  * Fork - LGPL
13894  * <script type="text/javascript">
13895  */
13896
13897 /**
13898  * @class Roo.Editor
13899  * @extends Roo.Component
13900  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13901  * @constructor
13902  * Create a new Editor
13903  * @param {Roo.form.Field} field The Field object (or descendant)
13904  * @param {Object} config The config object
13905  */
13906 Roo.Editor = function(field, config){
13907     Roo.Editor.superclass.constructor.call(this, config);
13908     this.field = field;
13909     this.addEvents({
13910         /**
13911              * @event beforestartedit
13912              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13913              * false from the handler of this event.
13914              * @param {Editor} this
13915              * @param {Roo.Element} boundEl The underlying element bound to this editor
13916              * @param {Mixed} value The field value being set
13917              */
13918         "beforestartedit" : true,
13919         /**
13920              * @event startedit
13921              * Fires when this editor is displayed
13922              * @param {Roo.Element} boundEl The underlying element bound to this editor
13923              * @param {Mixed} value The starting field value
13924              */
13925         "startedit" : true,
13926         /**
13927              * @event beforecomplete
13928              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13929              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13930              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13931              * event will not fire since no edit actually occurred.
13932              * @param {Editor} this
13933              * @param {Mixed} value The current field value
13934              * @param {Mixed} startValue The original field value
13935              */
13936         "beforecomplete" : true,
13937         /**
13938              * @event complete
13939              * Fires after editing is complete and any changed value has been written to the underlying field.
13940              * @param {Editor} this
13941              * @param {Mixed} value The current field value
13942              * @param {Mixed} startValue The original field value
13943              */
13944         "complete" : true,
13945         /**
13946          * @event specialkey
13947          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13948          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13949          * @param {Roo.form.Field} this
13950          * @param {Roo.EventObject} e The event object
13951          */
13952         "specialkey" : true
13953     });
13954 };
13955
13956 Roo.extend(Roo.Editor, Roo.Component, {
13957     /**
13958      * @cfg {Boolean/String} autosize
13959      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13960      * or "height" to adopt the height only (defaults to false)
13961      */
13962     /**
13963      * @cfg {Boolean} revertInvalid
13964      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13965      * validation fails (defaults to true)
13966      */
13967     /**
13968      * @cfg {Boolean} ignoreNoChange
13969      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13970      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13971      * will never be ignored.
13972      */
13973     /**
13974      * @cfg {Boolean} hideEl
13975      * False to keep the bound element visible while the editor is displayed (defaults to true)
13976      */
13977     /**
13978      * @cfg {Mixed} value
13979      * The data value of the underlying field (defaults to "")
13980      */
13981     value : "",
13982     /**
13983      * @cfg {String} alignment
13984      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13985      */
13986     alignment: "c-c?",
13987     /**
13988      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13989      * for bottom-right shadow (defaults to "frame")
13990      */
13991     shadow : "frame",
13992     /**
13993      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13994      */
13995     constrain : false,
13996     /**
13997      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13998      */
13999     completeOnEnter : false,
14000     /**
14001      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
14002      */
14003     cancelOnEsc : false,
14004     /**
14005      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
14006      */
14007     updateEl : false,
14008
14009     // private
14010     onRender : function(ct, position){
14011         this.el = new Roo.Layer({
14012             shadow: this.shadow,
14013             cls: "x-editor",
14014             parentEl : ct,
14015             shim : this.shim,
14016             shadowOffset:4,
14017             id: this.id,
14018             constrain: this.constrain
14019         });
14020         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
14021         if(this.field.msgTarget != 'title'){
14022             this.field.msgTarget = 'qtip';
14023         }
14024         this.field.render(this.el);
14025         if(Roo.isGecko){
14026             this.field.el.dom.setAttribute('autocomplete', 'off');
14027         }
14028         this.field.on("specialkey", this.onSpecialKey, this);
14029         if(this.swallowKeys){
14030             this.field.el.swallowEvent(['keydown','keypress']);
14031         }
14032         this.field.show();
14033         this.field.on("blur", this.onBlur, this);
14034         if(this.field.grow){
14035             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
14036         }
14037     },
14038
14039     onSpecialKey : function(field, e)
14040     {
14041         //Roo.log('editor onSpecialKey');
14042         if(this.completeOnEnter && e.getKey() == e.ENTER){
14043             e.stopEvent();
14044             this.completeEdit();
14045             return;
14046         }
14047         // do not fire special key otherwise it might hide close the editor...
14048         if(e.getKey() == e.ENTER){    
14049             return;
14050         }
14051         if(this.cancelOnEsc && e.getKey() == e.ESC){
14052             this.cancelEdit();
14053             return;
14054         } 
14055         this.fireEvent('specialkey', field, e);
14056     
14057     },
14058
14059     /**
14060      * Starts the editing process and shows the editor.
14061      * @param {String/HTMLElement/Element} el The element to edit
14062      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
14063       * to the innerHTML of el.
14064      */
14065     startEdit : function(el, value){
14066         if(this.editing){
14067             this.completeEdit();
14068         }
14069         this.boundEl = Roo.get(el);
14070         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
14071         if(!this.rendered){
14072             this.render(this.parentEl || document.body);
14073         }
14074         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
14075             return;
14076         }
14077         this.startValue = v;
14078         this.field.setValue(v);
14079         if(this.autoSize){
14080             var sz = this.boundEl.getSize();
14081             switch(this.autoSize){
14082                 case "width":
14083                 this.setSize(sz.width,  "");
14084                 break;
14085                 case "height":
14086                 this.setSize("",  sz.height);
14087                 break;
14088                 default:
14089                 this.setSize(sz.width,  sz.height);
14090             }
14091         }
14092         this.el.alignTo(this.boundEl, this.alignment);
14093         this.editing = true;
14094         if(Roo.QuickTips){
14095             Roo.QuickTips.disable();
14096         }
14097         this.show();
14098     },
14099
14100     /**
14101      * Sets the height and width of this editor.
14102      * @param {Number} width The new width
14103      * @param {Number} height The new height
14104      */
14105     setSize : function(w, h){
14106         this.field.setSize(w, h);
14107         if(this.el){
14108             this.el.sync();
14109         }
14110     },
14111
14112     /**
14113      * Realigns the editor to the bound field based on the current alignment config value.
14114      */
14115     realign : function(){
14116         this.el.alignTo(this.boundEl, this.alignment);
14117     },
14118
14119     /**
14120      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
14121      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
14122      */
14123     completeEdit : function(remainVisible){
14124         if(!this.editing){
14125             return;
14126         }
14127         var v = this.getValue();
14128         if(this.revertInvalid !== false && !this.field.isValid()){
14129             v = this.startValue;
14130             this.cancelEdit(true);
14131         }
14132         if(String(v) === String(this.startValue) && this.ignoreNoChange){
14133             this.editing = false;
14134             this.hide();
14135             return;
14136         }
14137         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
14138             this.editing = false;
14139             if(this.updateEl && this.boundEl){
14140                 this.boundEl.update(v);
14141             }
14142             if(remainVisible !== true){
14143                 this.hide();
14144             }
14145             this.fireEvent("complete", this, v, this.startValue);
14146         }
14147     },
14148
14149     // private
14150     onShow : function(){
14151         this.el.show();
14152         if(this.hideEl !== false){
14153             this.boundEl.hide();
14154         }
14155         this.field.show();
14156         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14157             this.fixIEFocus = true;
14158             this.deferredFocus.defer(50, this);
14159         }else{
14160             this.field.focus();
14161         }
14162         this.fireEvent("startedit", this.boundEl, this.startValue);
14163     },
14164
14165     deferredFocus : function(){
14166         if(this.editing){
14167             this.field.focus();
14168         }
14169     },
14170
14171     /**
14172      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14173      * reverted to the original starting value.
14174      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14175      * cancel (defaults to false)
14176      */
14177     cancelEdit : function(remainVisible){
14178         if(this.editing){
14179             this.setValue(this.startValue);
14180             if(remainVisible !== true){
14181                 this.hide();
14182             }
14183         }
14184     },
14185
14186     // private
14187     onBlur : function(){
14188         if(this.allowBlur !== true && this.editing){
14189             this.completeEdit();
14190         }
14191     },
14192
14193     // private
14194     onHide : function(){
14195         if(this.editing){
14196             this.completeEdit();
14197             return;
14198         }
14199         this.field.blur();
14200         if(this.field.collapse){
14201             this.field.collapse();
14202         }
14203         this.el.hide();
14204         if(this.hideEl !== false){
14205             this.boundEl.show();
14206         }
14207         if(Roo.QuickTips){
14208             Roo.QuickTips.enable();
14209         }
14210     },
14211
14212     /**
14213      * Sets the data value of the editor
14214      * @param {Mixed} value Any valid value supported by the underlying field
14215      */
14216     setValue : function(v){
14217         this.field.setValue(v);
14218     },
14219
14220     /**
14221      * Gets the data value of the editor
14222      * @return {Mixed} The data value
14223      */
14224     getValue : function(){
14225         return this.field.getValue();
14226     }
14227 });/*
14228  * Based on:
14229  * Ext JS Library 1.1.1
14230  * Copyright(c) 2006-2007, Ext JS, LLC.
14231  *
14232  * Originally Released Under LGPL - original licence link has changed is not relivant.
14233  *
14234  * Fork - LGPL
14235  * <script type="text/javascript">
14236  */
14237  
14238 /**
14239  * @class Roo.BasicDialog
14240  * @extends Roo.util.Observable
14241  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14242  * <pre><code>
14243 var dlg = new Roo.BasicDialog("my-dlg", {
14244     height: 200,
14245     width: 300,
14246     minHeight: 100,
14247     minWidth: 150,
14248     modal: true,
14249     proxyDrag: true,
14250     shadow: true
14251 });
14252 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14253 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14254 dlg.addButton('Cancel', dlg.hide, dlg);
14255 dlg.show();
14256 </code></pre>
14257   <b>A Dialog should always be a direct child of the body element.</b>
14258  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14259  * @cfg {String} title Default text to display in the title bar (defaults to null)
14260  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14261  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14262  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14263  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14264  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14265  * (defaults to null with no animation)
14266  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14267  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14268  * property for valid values (defaults to 'all')
14269  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14270  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14271  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14272  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14273  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14274  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14275  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14276  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14277  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14278  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14279  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14280  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14281  * draggable = true (defaults to false)
14282  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14283  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14284  * shadow (defaults to false)
14285  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14286  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14287  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14288  * @cfg {Array} buttons Array of buttons
14289  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14290  * @constructor
14291  * Create a new BasicDialog.
14292  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14293  * @param {Object} config Configuration options
14294  */
14295 Roo.BasicDialog = function(el, config){
14296     this.el = Roo.get(el);
14297     var dh = Roo.DomHelper;
14298     if(!this.el && config && config.autoCreate){
14299         if(typeof config.autoCreate == "object"){
14300             if(!config.autoCreate.id){
14301                 config.autoCreate.id = el;
14302             }
14303             this.el = dh.append(document.body,
14304                         config.autoCreate, true);
14305         }else{
14306             this.el = dh.append(document.body,
14307                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14308         }
14309     }
14310     el = this.el;
14311     el.setDisplayed(true);
14312     el.hide = this.hideAction;
14313     this.id = el.id;
14314     el.addClass("x-dlg");
14315
14316     Roo.apply(this, config);
14317
14318     this.proxy = el.createProxy("x-dlg-proxy");
14319     this.proxy.hide = this.hideAction;
14320     this.proxy.setOpacity(.5);
14321     this.proxy.hide();
14322
14323     if(config.width){
14324         el.setWidth(config.width);
14325     }
14326     if(config.height){
14327         el.setHeight(config.height);
14328     }
14329     this.size = el.getSize();
14330     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14331         this.xy = [config.x,config.y];
14332     }else{
14333         this.xy = el.getCenterXY(true);
14334     }
14335     /** The header element @type Roo.Element */
14336     this.header = el.child("> .x-dlg-hd");
14337     /** The body element @type Roo.Element */
14338     this.body = el.child("> .x-dlg-bd");
14339     /** The footer element @type Roo.Element */
14340     this.footer = el.child("> .x-dlg-ft");
14341
14342     if(!this.header){
14343         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14344     }
14345     if(!this.body){
14346         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14347     }
14348
14349     this.header.unselectable();
14350     if(this.title){
14351         this.header.update(this.title);
14352     }
14353     // this element allows the dialog to be focused for keyboard event
14354     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14355     this.focusEl.swallowEvent("click", true);
14356
14357     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14358
14359     // wrap the body and footer for special rendering
14360     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14361     if(this.footer){
14362         this.bwrap.dom.appendChild(this.footer.dom);
14363     }
14364
14365     this.bg = this.el.createChild({
14366         tag: "div", cls:"x-dlg-bg",
14367         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14368     });
14369     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14370
14371
14372     if(this.autoScroll !== false && !this.autoTabs){
14373         this.body.setStyle("overflow", "auto");
14374     }
14375
14376     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14377
14378     if(this.closable !== false){
14379         this.el.addClass("x-dlg-closable");
14380         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14381         this.close.on("click", this.closeClick, this);
14382         this.close.addClassOnOver("x-dlg-close-over");
14383     }
14384     if(this.collapsible !== false){
14385         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14386         this.collapseBtn.on("click", this.collapseClick, this);
14387         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14388         this.header.on("dblclick", this.collapseClick, this);
14389     }
14390     if(this.resizable !== false){
14391         this.el.addClass("x-dlg-resizable");
14392         this.resizer = new Roo.Resizable(el, {
14393             minWidth: this.minWidth || 80,
14394             minHeight:this.minHeight || 80,
14395             handles: this.resizeHandles || "all",
14396             pinned: true
14397         });
14398         this.resizer.on("beforeresize", this.beforeResize, this);
14399         this.resizer.on("resize", this.onResize, this);
14400     }
14401     if(this.draggable !== false){
14402         el.addClass("x-dlg-draggable");
14403         if (!this.proxyDrag) {
14404             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14405         }
14406         else {
14407             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14408         }
14409         dd.setHandleElId(this.header.id);
14410         dd.endDrag = this.endMove.createDelegate(this);
14411         dd.startDrag = this.startMove.createDelegate(this);
14412         dd.onDrag = this.onDrag.createDelegate(this);
14413         dd.scroll = false;
14414         this.dd = dd;
14415     }
14416     if(this.modal){
14417         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14418         this.mask.enableDisplayMode("block");
14419         this.mask.hide();
14420         this.el.addClass("x-dlg-modal");
14421     }
14422     if(this.shadow){
14423         this.shadow = new Roo.Shadow({
14424             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14425             offset : this.shadowOffset
14426         });
14427     }else{
14428         this.shadowOffset = 0;
14429     }
14430     if(Roo.useShims && this.shim !== false){
14431         this.shim = this.el.createShim();
14432         this.shim.hide = this.hideAction;
14433         this.shim.hide();
14434     }else{
14435         this.shim = false;
14436     }
14437     if(this.autoTabs){
14438         this.initTabs();
14439     }
14440     if (this.buttons) { 
14441         var bts= this.buttons;
14442         this.buttons = [];
14443         Roo.each(bts, function(b) {
14444             this.addButton(b);
14445         }, this);
14446     }
14447     
14448     
14449     this.addEvents({
14450         /**
14451          * @event keydown
14452          * Fires when a key is pressed
14453          * @param {Roo.BasicDialog} this
14454          * @param {Roo.EventObject} e
14455          */
14456         "keydown" : true,
14457         /**
14458          * @event move
14459          * Fires when this dialog is moved by the user.
14460          * @param {Roo.BasicDialog} this
14461          * @param {Number} x The new page X
14462          * @param {Number} y The new page Y
14463          */
14464         "move" : true,
14465         /**
14466          * @event resize
14467          * Fires when this dialog is resized by the user.
14468          * @param {Roo.BasicDialog} this
14469          * @param {Number} width The new width
14470          * @param {Number} height The new height
14471          */
14472         "resize" : true,
14473         /**
14474          * @event beforehide
14475          * Fires before this dialog is hidden.
14476          * @param {Roo.BasicDialog} this
14477          */
14478         "beforehide" : true,
14479         /**
14480          * @event hide
14481          * Fires when this dialog is hidden.
14482          * @param {Roo.BasicDialog} this
14483          */
14484         "hide" : true,
14485         /**
14486          * @event beforeshow
14487          * Fires before this dialog is shown.
14488          * @param {Roo.BasicDialog} this
14489          */
14490         "beforeshow" : true,
14491         /**
14492          * @event show
14493          * Fires when this dialog is shown.
14494          * @param {Roo.BasicDialog} this
14495          */
14496         "show" : true
14497     });
14498     el.on("keydown", this.onKeyDown, this);
14499     el.on("mousedown", this.toFront, this);
14500     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14501     this.el.hide();
14502     Roo.DialogManager.register(this);
14503     Roo.BasicDialog.superclass.constructor.call(this);
14504 };
14505
14506 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14507     shadowOffset: Roo.isIE ? 6 : 5,
14508     minHeight: 80,
14509     minWidth: 200,
14510     minButtonWidth: 75,
14511     defaultButton: null,
14512     buttonAlign: "right",
14513     tabTag: 'div',
14514     firstShow: true,
14515
14516     /**
14517      * Sets the dialog title text
14518      * @param {String} text The title text to display
14519      * @return {Roo.BasicDialog} this
14520      */
14521     setTitle : function(text){
14522         this.header.update(text);
14523         return this;
14524     },
14525
14526     // private
14527     closeClick : function(){
14528         this.hide();
14529     },
14530
14531     // private
14532     collapseClick : function(){
14533         this[this.collapsed ? "expand" : "collapse"]();
14534     },
14535
14536     /**
14537      * Collapses the dialog to its minimized state (only the title bar is visible).
14538      * Equivalent to the user clicking the collapse dialog button.
14539      */
14540     collapse : function(){
14541         if(!this.collapsed){
14542             this.collapsed = true;
14543             this.el.addClass("x-dlg-collapsed");
14544             this.restoreHeight = this.el.getHeight();
14545             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14546         }
14547     },
14548
14549     /**
14550      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14551      * clicking the expand dialog button.
14552      */
14553     expand : function(){
14554         if(this.collapsed){
14555             this.collapsed = false;
14556             this.el.removeClass("x-dlg-collapsed");
14557             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14558         }
14559     },
14560
14561     /**
14562      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14563      * @return {Roo.TabPanel} The tabs component
14564      */
14565     initTabs : function(){
14566         var tabs = this.getTabs();
14567         while(tabs.getTab(0)){
14568             tabs.removeTab(0);
14569         }
14570         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14571             var dom = el.dom;
14572             tabs.addTab(Roo.id(dom), dom.title);
14573             dom.title = "";
14574         });
14575         tabs.activate(0);
14576         return tabs;
14577     },
14578
14579     // private
14580     beforeResize : function(){
14581         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14582     },
14583
14584     // private
14585     onResize : function(){
14586         this.refreshSize();
14587         this.syncBodyHeight();
14588         this.adjustAssets();
14589         this.focus();
14590         this.fireEvent("resize", this, this.size.width, this.size.height);
14591     },
14592
14593     // private
14594     onKeyDown : function(e){
14595         if(this.isVisible()){
14596             this.fireEvent("keydown", this, e);
14597         }
14598     },
14599
14600     /**
14601      * Resizes the dialog.
14602      * @param {Number} width
14603      * @param {Number} height
14604      * @return {Roo.BasicDialog} this
14605      */
14606     resizeTo : function(width, height){
14607         this.el.setSize(width, height);
14608         this.size = {width: width, height: height};
14609         this.syncBodyHeight();
14610         if(this.fixedcenter){
14611             this.center();
14612         }
14613         if(this.isVisible()){
14614             this.constrainXY();
14615             this.adjustAssets();
14616         }
14617         this.fireEvent("resize", this, width, height);
14618         return this;
14619     },
14620
14621
14622     /**
14623      * Resizes the dialog to fit the specified content size.
14624      * @param {Number} width
14625      * @param {Number} height
14626      * @return {Roo.BasicDialog} this
14627      */
14628     setContentSize : function(w, h){
14629         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14630         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14631         //if(!this.el.isBorderBox()){
14632             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14633             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14634         //}
14635         if(this.tabs){
14636             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14637             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14638         }
14639         this.resizeTo(w, h);
14640         return this;
14641     },
14642
14643     /**
14644      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14645      * executed in response to a particular key being pressed while the dialog is active.
14646      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14647      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14648      * @param {Function} fn The function to call
14649      * @param {Object} scope (optional) The scope of the function
14650      * @return {Roo.BasicDialog} this
14651      */
14652     addKeyListener : function(key, fn, scope){
14653         var keyCode, shift, ctrl, alt;
14654         if(typeof key == "object" && !(key instanceof Array)){
14655             keyCode = key["key"];
14656             shift = key["shift"];
14657             ctrl = key["ctrl"];
14658             alt = key["alt"];
14659         }else{
14660             keyCode = key;
14661         }
14662         var handler = function(dlg, e){
14663             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14664                 var k = e.getKey();
14665                 if(keyCode instanceof Array){
14666                     for(var i = 0, len = keyCode.length; i < len; i++){
14667                         if(keyCode[i] == k){
14668                           fn.call(scope || window, dlg, k, e);
14669                           return;
14670                         }
14671                     }
14672                 }else{
14673                     if(k == keyCode){
14674                         fn.call(scope || window, dlg, k, e);
14675                     }
14676                 }
14677             }
14678         };
14679         this.on("keydown", handler);
14680         return this;
14681     },
14682
14683     /**
14684      * Returns the TabPanel component (creates it if it doesn't exist).
14685      * Note: If you wish to simply check for the existence of tabs without creating them,
14686      * check for a null 'tabs' property.
14687      * @return {Roo.TabPanel} The tabs component
14688      */
14689     getTabs : function(){
14690         if(!this.tabs){
14691             this.el.addClass("x-dlg-auto-tabs");
14692             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14693             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14694         }
14695         return this.tabs;
14696     },
14697
14698     /**
14699      * Adds a button to the footer section of the dialog.
14700      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14701      * object or a valid Roo.DomHelper element config
14702      * @param {Function} handler The function called when the button is clicked
14703      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14704      * @return {Roo.Button} The new button
14705      */
14706     addButton : function(config, handler, scope){
14707         var dh = Roo.DomHelper;
14708         if(!this.footer){
14709             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14710         }
14711         if(!this.btnContainer){
14712             var tb = this.footer.createChild({
14713
14714                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14715                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14716             }, null, true);
14717             this.btnContainer = tb.firstChild.firstChild.firstChild;
14718         }
14719         var bconfig = {
14720             handler: handler,
14721             scope: scope,
14722             minWidth: this.minButtonWidth,
14723             hideParent:true
14724         };
14725         if(typeof config == "string"){
14726             bconfig.text = config;
14727         }else{
14728             if(config.tag){
14729                 bconfig.dhconfig = config;
14730             }else{
14731                 Roo.apply(bconfig, config);
14732             }
14733         }
14734         var fc = false;
14735         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14736             bconfig.position = Math.max(0, bconfig.position);
14737             fc = this.btnContainer.childNodes[bconfig.position];
14738         }
14739          
14740         var btn = new Roo.Button(
14741             fc ? 
14742                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14743                 : this.btnContainer.appendChild(document.createElement("td")),
14744             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14745             bconfig
14746         );
14747         this.syncBodyHeight();
14748         if(!this.buttons){
14749             /**
14750              * Array of all the buttons that have been added to this dialog via addButton
14751              * @type Array
14752              */
14753             this.buttons = [];
14754         }
14755         this.buttons.push(btn);
14756         return btn;
14757     },
14758
14759     /**
14760      * Sets the default button to be focused when the dialog is displayed.
14761      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14762      * @return {Roo.BasicDialog} this
14763      */
14764     setDefaultButton : function(btn){
14765         this.defaultButton = btn;
14766         return this;
14767     },
14768
14769     // private
14770     getHeaderFooterHeight : function(safe){
14771         var height = 0;
14772         if(this.header){
14773            height += this.header.getHeight();
14774         }
14775         if(this.footer){
14776            var fm = this.footer.getMargins();
14777             height += (this.footer.getHeight()+fm.top+fm.bottom);
14778         }
14779         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14780         height += this.centerBg.getPadding("tb");
14781         return height;
14782     },
14783
14784     // private
14785     syncBodyHeight : function(){
14786         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
14787         var height = this.size.height - this.getHeaderFooterHeight(false);
14788         bd.setHeight(height-bd.getMargins("tb"));
14789         var hh = this.header.getHeight();
14790         var h = this.size.height-hh;
14791         cb.setHeight(h);
14792         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14793         bw.setHeight(h-cb.getPadding("tb"));
14794         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14795         bd.setWidth(bw.getWidth(true));
14796         if(this.tabs){
14797             this.tabs.syncHeight();
14798             if(Roo.isIE){
14799                 this.tabs.el.repaint();
14800             }
14801         }
14802     },
14803
14804     /**
14805      * Restores the previous state of the dialog if Roo.state is configured.
14806      * @return {Roo.BasicDialog} this
14807      */
14808     restoreState : function(){
14809         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14810         if(box && box.width){
14811             this.xy = [box.x, box.y];
14812             this.resizeTo(box.width, box.height);
14813         }
14814         return this;
14815     },
14816
14817     // private
14818     beforeShow : function(){
14819         this.expand();
14820         if(this.fixedcenter){
14821             this.xy = this.el.getCenterXY(true);
14822         }
14823         if(this.modal){
14824             Roo.get(document.body).addClass("x-body-masked");
14825             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14826             this.mask.show();
14827         }
14828         this.constrainXY();
14829     },
14830
14831     // private
14832     animShow : function(){
14833         var b = Roo.get(this.animateTarget).getBox();
14834         this.proxy.setSize(b.width, b.height);
14835         this.proxy.setLocation(b.x, b.y);
14836         this.proxy.show();
14837         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14838                     true, .35, this.showEl.createDelegate(this));
14839     },
14840
14841     /**
14842      * Shows the dialog.
14843      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14844      * @return {Roo.BasicDialog} this
14845      */
14846     show : function(animateTarget){
14847         if (this.fireEvent("beforeshow", this) === false){
14848             return;
14849         }
14850         if(this.syncHeightBeforeShow){
14851             this.syncBodyHeight();
14852         }else if(this.firstShow){
14853             this.firstShow = false;
14854             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14855         }
14856         this.animateTarget = animateTarget || this.animateTarget;
14857         if(!this.el.isVisible()){
14858             this.beforeShow();
14859             if(this.animateTarget && Roo.get(this.animateTarget)){
14860                 this.animShow();
14861             }else{
14862                 this.showEl();
14863             }
14864         }
14865         return this;
14866     },
14867
14868     // private
14869     showEl : function(){
14870         this.proxy.hide();
14871         this.el.setXY(this.xy);
14872         this.el.show();
14873         this.adjustAssets(true);
14874         this.toFront();
14875         this.focus();
14876         // IE peekaboo bug - fix found by Dave Fenwick
14877         if(Roo.isIE){
14878             this.el.repaint();
14879         }
14880         this.fireEvent("show", this);
14881     },
14882
14883     /**
14884      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14885      * dialog itself will receive focus.
14886      */
14887     focus : function(){
14888         if(this.defaultButton){
14889             this.defaultButton.focus();
14890         }else{
14891             this.focusEl.focus();
14892         }
14893     },
14894
14895     // private
14896     constrainXY : function(){
14897         if(this.constraintoviewport !== false){
14898             if(!this.viewSize){
14899                 if(this.container){
14900                     var s = this.container.getSize();
14901                     this.viewSize = [s.width, s.height];
14902                 }else{
14903                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14904                 }
14905             }
14906             var s = Roo.get(this.container||document).getScroll();
14907
14908             var x = this.xy[0], y = this.xy[1];
14909             var w = this.size.width, h = this.size.height;
14910             var vw = this.viewSize[0], vh = this.viewSize[1];
14911             // only move it if it needs it
14912             var moved = false;
14913             // first validate right/bottom
14914             if(x + w > vw+s.left){
14915                 x = vw - w;
14916                 moved = true;
14917             }
14918             if(y + h > vh+s.top){
14919                 y = vh - h;
14920                 moved = true;
14921             }
14922             // then make sure top/left isn't negative
14923             if(x < s.left){
14924                 x = s.left;
14925                 moved = true;
14926             }
14927             if(y < s.top){
14928                 y = s.top;
14929                 moved = true;
14930             }
14931             if(moved){
14932                 // cache xy
14933                 this.xy = [x, y];
14934                 if(this.isVisible()){
14935                     this.el.setLocation(x, y);
14936                     this.adjustAssets();
14937                 }
14938             }
14939         }
14940     },
14941
14942     // private
14943     onDrag : function(){
14944         if(!this.proxyDrag){
14945             this.xy = this.el.getXY();
14946             this.adjustAssets();
14947         }
14948     },
14949
14950     // private
14951     adjustAssets : function(doShow){
14952         var x = this.xy[0], y = this.xy[1];
14953         var w = this.size.width, h = this.size.height;
14954         if(doShow === true){
14955             if(this.shadow){
14956                 this.shadow.show(this.el);
14957             }
14958             if(this.shim){
14959                 this.shim.show();
14960             }
14961         }
14962         if(this.shadow && this.shadow.isVisible()){
14963             this.shadow.show(this.el);
14964         }
14965         if(this.shim && this.shim.isVisible()){
14966             this.shim.setBounds(x, y, w, h);
14967         }
14968     },
14969
14970     // private
14971     adjustViewport : function(w, h){
14972         if(!w || !h){
14973             w = Roo.lib.Dom.getViewWidth();
14974             h = Roo.lib.Dom.getViewHeight();
14975         }
14976         // cache the size
14977         this.viewSize = [w, h];
14978         if(this.modal && this.mask.isVisible()){
14979             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14980             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14981         }
14982         if(this.isVisible()){
14983             this.constrainXY();
14984         }
14985     },
14986
14987     /**
14988      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14989      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14990      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14991      */
14992     destroy : function(removeEl){
14993         if(this.isVisible()){
14994             this.animateTarget = null;
14995             this.hide();
14996         }
14997         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14998         if(this.tabs){
14999             this.tabs.destroy(removeEl);
15000         }
15001         Roo.destroy(
15002              this.shim,
15003              this.proxy,
15004              this.resizer,
15005              this.close,
15006              this.mask
15007         );
15008         if(this.dd){
15009             this.dd.unreg();
15010         }
15011         if(this.buttons){
15012            for(var i = 0, len = this.buttons.length; i < len; i++){
15013                this.buttons[i].destroy();
15014            }
15015         }
15016         this.el.removeAllListeners();
15017         if(removeEl === true){
15018             this.el.update("");
15019             this.el.remove();
15020         }
15021         Roo.DialogManager.unregister(this);
15022     },
15023
15024     // private
15025     startMove : function(){
15026         if(this.proxyDrag){
15027             this.proxy.show();
15028         }
15029         if(this.constraintoviewport !== false){
15030             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
15031         }
15032     },
15033
15034     // private
15035     endMove : function(){
15036         if(!this.proxyDrag){
15037             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
15038         }else{
15039             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
15040             this.proxy.hide();
15041         }
15042         this.refreshSize();
15043         this.adjustAssets();
15044         this.focus();
15045         this.fireEvent("move", this, this.xy[0], this.xy[1]);
15046     },
15047
15048     /**
15049      * Brings this dialog to the front of any other visible dialogs
15050      * @return {Roo.BasicDialog} this
15051      */
15052     toFront : function(){
15053         Roo.DialogManager.bringToFront(this);
15054         return this;
15055     },
15056
15057     /**
15058      * Sends this dialog to the back (under) of any other visible dialogs
15059      * @return {Roo.BasicDialog} this
15060      */
15061     toBack : function(){
15062         Roo.DialogManager.sendToBack(this);
15063         return this;
15064     },
15065
15066     /**
15067      * Centers this dialog in the viewport
15068      * @return {Roo.BasicDialog} this
15069      */
15070     center : function(){
15071         var xy = this.el.getCenterXY(true);
15072         this.moveTo(xy[0], xy[1]);
15073         return this;
15074     },
15075
15076     /**
15077      * Moves the dialog's top-left corner to the specified point
15078      * @param {Number} x
15079      * @param {Number} y
15080      * @return {Roo.BasicDialog} this
15081      */
15082     moveTo : function(x, y){
15083         this.xy = [x,y];
15084         if(this.isVisible()){
15085             this.el.setXY(this.xy);
15086             this.adjustAssets();
15087         }
15088         return this;
15089     },
15090
15091     /**
15092      * Aligns the dialog to the specified element
15093      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15094      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
15095      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15096      * @return {Roo.BasicDialog} this
15097      */
15098     alignTo : function(element, position, offsets){
15099         this.xy = this.el.getAlignToXY(element, position, offsets);
15100         if(this.isVisible()){
15101             this.el.setXY(this.xy);
15102             this.adjustAssets();
15103         }
15104         return this;
15105     },
15106
15107     /**
15108      * Anchors an element to another element and realigns it when the window is resized.
15109      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15110      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
15111      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15112      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
15113      * is a number, it is used as the buffer delay (defaults to 50ms).
15114      * @return {Roo.BasicDialog} this
15115      */
15116     anchorTo : function(el, alignment, offsets, monitorScroll){
15117         var action = function(){
15118             this.alignTo(el, alignment, offsets);
15119         };
15120         Roo.EventManager.onWindowResize(action, this);
15121         var tm = typeof monitorScroll;
15122         if(tm != 'undefined'){
15123             Roo.EventManager.on(window, 'scroll', action, this,
15124                 {buffer: tm == 'number' ? monitorScroll : 50});
15125         }
15126         action.call(this);
15127         return this;
15128     },
15129
15130     /**
15131      * Returns true if the dialog is visible
15132      * @return {Boolean}
15133      */
15134     isVisible : function(){
15135         return this.el.isVisible();
15136     },
15137
15138     // private
15139     animHide : function(callback){
15140         var b = Roo.get(this.animateTarget).getBox();
15141         this.proxy.show();
15142         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15143         this.el.hide();
15144         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15145                     this.hideEl.createDelegate(this, [callback]));
15146     },
15147
15148     /**
15149      * Hides the dialog.
15150      * @param {Function} callback (optional) Function to call when the dialog is hidden
15151      * @return {Roo.BasicDialog} this
15152      */
15153     hide : function(callback){
15154         if (this.fireEvent("beforehide", this) === false){
15155             return;
15156         }
15157         if(this.shadow){
15158             this.shadow.hide();
15159         }
15160         if(this.shim) {
15161           this.shim.hide();
15162         }
15163         // sometimes animateTarget seems to get set.. causing problems...
15164         // this just double checks..
15165         if(this.animateTarget && Roo.get(this.animateTarget)) {
15166            this.animHide(callback);
15167         }else{
15168             this.el.hide();
15169             this.hideEl(callback);
15170         }
15171         return this;
15172     },
15173
15174     // private
15175     hideEl : function(callback){
15176         this.proxy.hide();
15177         if(this.modal){
15178             this.mask.hide();
15179             Roo.get(document.body).removeClass("x-body-masked");
15180         }
15181         this.fireEvent("hide", this);
15182         if(typeof callback == "function"){
15183             callback();
15184         }
15185     },
15186
15187     // private
15188     hideAction : function(){
15189         this.setLeft("-10000px");
15190         this.setTop("-10000px");
15191         this.setStyle("visibility", "hidden");
15192     },
15193
15194     // private
15195     refreshSize : function(){
15196         this.size = this.el.getSize();
15197         this.xy = this.el.getXY();
15198         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15199     },
15200
15201     // private
15202     // z-index is managed by the DialogManager and may be overwritten at any time
15203     setZIndex : function(index){
15204         if(this.modal){
15205             this.mask.setStyle("z-index", index);
15206         }
15207         if(this.shim){
15208             this.shim.setStyle("z-index", ++index);
15209         }
15210         if(this.shadow){
15211             this.shadow.setZIndex(++index);
15212         }
15213         this.el.setStyle("z-index", ++index);
15214         if(this.proxy){
15215             this.proxy.setStyle("z-index", ++index);
15216         }
15217         if(this.resizer){
15218             this.resizer.proxy.setStyle("z-index", ++index);
15219         }
15220
15221         this.lastZIndex = index;
15222     },
15223
15224     /**
15225      * Returns the element for this dialog
15226      * @return {Roo.Element} The underlying dialog Element
15227      */
15228     getEl : function(){
15229         return this.el;
15230     }
15231 });
15232
15233 /**
15234  * @class Roo.DialogManager
15235  * Provides global access to BasicDialogs that have been created and
15236  * support for z-indexing (layering) multiple open dialogs.
15237  */
15238 Roo.DialogManager = function(){
15239     var list = {};
15240     var accessList = [];
15241     var front = null;
15242
15243     // private
15244     var sortDialogs = function(d1, d2){
15245         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15246     };
15247
15248     // private
15249     var orderDialogs = function(){
15250         accessList.sort(sortDialogs);
15251         var seed = Roo.DialogManager.zseed;
15252         for(var i = 0, len = accessList.length; i < len; i++){
15253             var dlg = accessList[i];
15254             if(dlg){
15255                 dlg.setZIndex(seed + (i*10));
15256             }
15257         }
15258     };
15259
15260     return {
15261         /**
15262          * The starting z-index for BasicDialogs (defaults to 9000)
15263          * @type Number The z-index value
15264          */
15265         zseed : 9000,
15266
15267         // private
15268         register : function(dlg){
15269             list[dlg.id] = dlg;
15270             accessList.push(dlg);
15271         },
15272
15273         // private
15274         unregister : function(dlg){
15275             delete list[dlg.id];
15276             var i=0;
15277             var len=0;
15278             if(!accessList.indexOf){
15279                 for(  i = 0, len = accessList.length; i < len; i++){
15280                     if(accessList[i] == dlg){
15281                         accessList.splice(i, 1);
15282                         return;
15283                     }
15284                 }
15285             }else{
15286                  i = accessList.indexOf(dlg);
15287                 if(i != -1){
15288                     accessList.splice(i, 1);
15289                 }
15290             }
15291         },
15292
15293         /**
15294          * Gets a registered dialog by id
15295          * @param {String/Object} id The id of the dialog or a dialog
15296          * @return {Roo.BasicDialog} this
15297          */
15298         get : function(id){
15299             return typeof id == "object" ? id : list[id];
15300         },
15301
15302         /**
15303          * Brings the specified dialog to the front
15304          * @param {String/Object} dlg The id of the dialog or a dialog
15305          * @return {Roo.BasicDialog} this
15306          */
15307         bringToFront : function(dlg){
15308             dlg = this.get(dlg);
15309             if(dlg != front){
15310                 front = dlg;
15311                 dlg._lastAccess = new Date().getTime();
15312                 orderDialogs();
15313             }
15314             return dlg;
15315         },
15316
15317         /**
15318          * Sends the specified dialog to the back
15319          * @param {String/Object} dlg The id of the dialog or a dialog
15320          * @return {Roo.BasicDialog} this
15321          */
15322         sendToBack : function(dlg){
15323             dlg = this.get(dlg);
15324             dlg._lastAccess = -(new Date().getTime());
15325             orderDialogs();
15326             return dlg;
15327         },
15328
15329         /**
15330          * Hides all dialogs
15331          */
15332         hideAll : function(){
15333             for(var id in list){
15334                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15335                     list[id].hide();
15336                 }
15337             }
15338         }
15339     };
15340 }();
15341
15342 /**
15343  * @class Roo.LayoutDialog
15344  * @extends Roo.BasicDialog
15345  * Dialog which provides adjustments for working with a layout in a Dialog.
15346  * Add your necessary layout config options to the dialog's config.<br>
15347  * Example usage (including a nested layout):
15348  * <pre><code>
15349 if(!dialog){
15350     dialog = new Roo.LayoutDialog("download-dlg", {
15351         modal: true,
15352         width:600,
15353         height:450,
15354         shadow:true,
15355         minWidth:500,
15356         minHeight:350,
15357         autoTabs:true,
15358         proxyDrag:true,
15359         // layout config merges with the dialog config
15360         center:{
15361             tabPosition: "top",
15362             alwaysShowTabs: true
15363         }
15364     });
15365     dialog.addKeyListener(27, dialog.hide, dialog);
15366     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15367     dialog.addButton("Build It!", this.getDownload, this);
15368
15369     // we can even add nested layouts
15370     var innerLayout = new Roo.BorderLayout("dl-inner", {
15371         east: {
15372             initialSize: 200,
15373             autoScroll:true,
15374             split:true
15375         },
15376         center: {
15377             autoScroll:true
15378         }
15379     });
15380     innerLayout.beginUpdate();
15381     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15382     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15383     innerLayout.endUpdate(true);
15384
15385     var layout = dialog.getLayout();
15386     layout.beginUpdate();
15387     layout.add("center", new Roo.ContentPanel("standard-panel",
15388                         {title: "Download the Source", fitToFrame:true}));
15389     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15390                {title: "Build your own roo.js"}));
15391     layout.getRegion("center").showPanel(sp);
15392     layout.endUpdate();
15393 }
15394 </code></pre>
15395     * @constructor
15396     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15397     * @param {Object} config configuration options
15398   */
15399 Roo.LayoutDialog = function(el, cfg){
15400     
15401     var config=  cfg;
15402     if (typeof(cfg) == 'undefined') {
15403         config = Roo.apply({}, el);
15404         // not sure why we use documentElement here.. - it should always be body.
15405         // IE7 borks horribly if we use documentElement.
15406         // webkit also does not like documentElement - it creates a body element...
15407         el = Roo.get( document.body || document.documentElement ).createChild();
15408         //config.autoCreate = true;
15409     }
15410     
15411     
15412     config.autoTabs = false;
15413     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15414     this.body.setStyle({overflow:"hidden", position:"relative"});
15415     this.layout = new Roo.BorderLayout(this.body.dom, config);
15416     this.layout.monitorWindowResize = false;
15417     this.el.addClass("x-dlg-auto-layout");
15418     // fix case when center region overwrites center function
15419     this.center = Roo.BasicDialog.prototype.center;
15420     this.on("show", this.layout.layout, this.layout, true);
15421     if (config.items) {
15422         var xitems = config.items;
15423         delete config.items;
15424         Roo.each(xitems, this.addxtype, this);
15425     }
15426     
15427     
15428 };
15429 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15430     /**
15431      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15432      * @deprecated
15433      */
15434     endUpdate : function(){
15435         this.layout.endUpdate();
15436     },
15437
15438     /**
15439      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15440      *  @deprecated
15441      */
15442     beginUpdate : function(){
15443         this.layout.beginUpdate();
15444     },
15445
15446     /**
15447      * Get the BorderLayout for this dialog
15448      * @return {Roo.BorderLayout}
15449      */
15450     getLayout : function(){
15451         return this.layout;
15452     },
15453
15454     showEl : function(){
15455         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15456         if(Roo.isIE7){
15457             this.layout.layout();
15458         }
15459     },
15460
15461     // private
15462     // Use the syncHeightBeforeShow config option to control this automatically
15463     syncBodyHeight : function(){
15464         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15465         if(this.layout){this.layout.layout();}
15466     },
15467     
15468       /**
15469      * Add an xtype element (actually adds to the layout.)
15470      * @return {Object} xdata xtype object data.
15471      */
15472     
15473     addxtype : function(c) {
15474         return this.layout.addxtype(c);
15475     }
15476 });/*
15477  * Based on:
15478  * Ext JS Library 1.1.1
15479  * Copyright(c) 2006-2007, Ext JS, LLC.
15480  *
15481  * Originally Released Under LGPL - original licence link has changed is not relivant.
15482  *
15483  * Fork - LGPL
15484  * <script type="text/javascript">
15485  */
15486  
15487 /**
15488  * @class Roo.MessageBox
15489  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15490  * Example usage:
15491  *<pre><code>
15492 // Basic alert:
15493 Roo.Msg.alert('Status', 'Changes saved successfully.');
15494
15495 // Prompt for user data:
15496 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15497     if (btn == 'ok'){
15498         // process text value...
15499     }
15500 });
15501
15502 // Show a dialog using config options:
15503 Roo.Msg.show({
15504    title:'Save Changes?',
15505    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15506    buttons: Roo.Msg.YESNOCANCEL,
15507    fn: processResult,
15508    animEl: 'elId'
15509 });
15510 </code></pre>
15511  * @singleton
15512  */
15513 Roo.MessageBox = function(){
15514     var dlg, opt, mask, waitTimer;
15515     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15516     var buttons, activeTextEl, bwidth;
15517
15518     // private
15519     var handleButton = function(button){
15520         dlg.hide();
15521         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15522     };
15523
15524     // private
15525     var handleHide = function(){
15526         if(opt && opt.cls){
15527             dlg.el.removeClass(opt.cls);
15528         }
15529         if(waitTimer){
15530             Roo.TaskMgr.stop(waitTimer);
15531             waitTimer = null;
15532         }
15533     };
15534
15535     // private
15536     var updateButtons = function(b){
15537         var width = 0;
15538         if(!b){
15539             buttons["ok"].hide();
15540             buttons["cancel"].hide();
15541             buttons["yes"].hide();
15542             buttons["no"].hide();
15543             dlg.footer.dom.style.display = 'none';
15544             return width;
15545         }
15546         dlg.footer.dom.style.display = '';
15547         for(var k in buttons){
15548             if(typeof buttons[k] != "function"){
15549                 if(b[k]){
15550                     buttons[k].show();
15551                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15552                     width += buttons[k].el.getWidth()+15;
15553                 }else{
15554                     buttons[k].hide();
15555                 }
15556             }
15557         }
15558         return width;
15559     };
15560
15561     // private
15562     var handleEsc = function(d, k, e){
15563         if(opt && opt.closable !== false){
15564             dlg.hide();
15565         }
15566         if(e){
15567             e.stopEvent();
15568         }
15569     };
15570
15571     return {
15572         /**
15573          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15574          * @return {Roo.BasicDialog} The BasicDialog element
15575          */
15576         getDialog : function(){
15577            if(!dlg){
15578                 dlg = new Roo.BasicDialog("x-msg-box", {
15579                     autoCreate : true,
15580                     shadow: true,
15581                     draggable: true,
15582                     resizable:false,
15583                     constraintoviewport:false,
15584                     fixedcenter:true,
15585                     collapsible : false,
15586                     shim:true,
15587                     modal: true,
15588                     width:400, height:100,
15589                     buttonAlign:"center",
15590                     closeClick : function(){
15591                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15592                             handleButton("no");
15593                         }else{
15594                             handleButton("cancel");
15595                         }
15596                     }
15597                 });
15598                 dlg.on("hide", handleHide);
15599                 mask = dlg.mask;
15600                 dlg.addKeyListener(27, handleEsc);
15601                 buttons = {};
15602                 var bt = this.buttonText;
15603                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15604                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15605                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15606                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15607                 bodyEl = dlg.body.createChild({
15608
15609                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" /><textarea class="roo-mb-textarea"></textarea><div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
15610                 });
15611                 msgEl = bodyEl.dom.firstChild;
15612                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15613                 textboxEl.enableDisplayMode();
15614                 textboxEl.addKeyListener([10,13], function(){
15615                     if(dlg.isVisible() && opt && opt.buttons){
15616                         if(opt.buttons.ok){
15617                             handleButton("ok");
15618                         }else if(opt.buttons.yes){
15619                             handleButton("yes");
15620                         }
15621                     }
15622                 });
15623                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15624                 textareaEl.enableDisplayMode();
15625                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15626                 progressEl.enableDisplayMode();
15627                 var pf = progressEl.dom.firstChild;
15628                 if (pf) {
15629                     pp = Roo.get(pf.firstChild);
15630                     pp.setHeight(pf.offsetHeight);
15631                 }
15632                 
15633             }
15634             return dlg;
15635         },
15636
15637         /**
15638          * Updates the message box body text
15639          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15640          * the XHTML-compliant non-breaking space character '&amp;#160;')
15641          * @return {Roo.MessageBox} This message box
15642          */
15643         updateText : function(text){
15644             if(!dlg.isVisible() && !opt.width){
15645                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15646             }
15647             msgEl.innerHTML = text || '&#160;';
15648       
15649             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
15650             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
15651             var w = Math.max(
15652                     Math.min(opt.width || cw , this.maxWidth), 
15653                     Math.max(opt.minWidth || this.minWidth, bwidth)
15654             );
15655             if(opt.prompt){
15656                 activeTextEl.setWidth(w);
15657             }
15658             if(dlg.isVisible()){
15659                 dlg.fixedcenter = false;
15660             }
15661             // to big, make it scroll. = But as usual stupid IE does not support
15662             // !important..
15663             
15664             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15665                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15666                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
15667             } else {
15668                 bodyEl.dom.style.height = '';
15669                 bodyEl.dom.style.overflowY = '';
15670             }
15671             if (cw > w) {
15672                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
15673             } else {
15674                 bodyEl.dom.style.overflowX = '';
15675             }
15676             
15677             dlg.setContentSize(w, bodyEl.getHeight());
15678             if(dlg.isVisible()){
15679                 dlg.fixedcenter = true;
15680             }
15681             return this;
15682         },
15683
15684         /**
15685          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15686          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15687          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15688          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15689          * @return {Roo.MessageBox} This message box
15690          */
15691         updateProgress : function(value, text){
15692             if(text){
15693                 this.updateText(text);
15694             }
15695             if (pp) { // weird bug on my firefox - for some reason this is not defined
15696                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15697             }
15698             return this;
15699         },        
15700
15701         /**
15702          * Returns true if the message box is currently displayed
15703          * @return {Boolean} True if the message box is visible, else false
15704          */
15705         isVisible : function(){
15706             return dlg && dlg.isVisible();  
15707         },
15708
15709         /**
15710          * Hides the message box if it is displayed
15711          */
15712         hide : function(){
15713             if(this.isVisible()){
15714                 dlg.hide();
15715             }  
15716         },
15717
15718         /**
15719          * Displays a new message box, or reinitializes an existing message box, based on the config options
15720          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15721          * The following config object properties are supported:
15722          * <pre>
15723 Property    Type             Description
15724 ----------  ---------------  ------------------------------------------------------------------------------------
15725 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15726                                    closes (defaults to undefined)
15727 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15728                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15729 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15730                                    progress and wait dialogs will ignore this property and always hide the
15731                                    close button as they can only be closed programmatically.
15732 cls               String           A custom CSS class to apply to the message box element
15733 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15734                                    displayed (defaults to 75)
15735 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15736                                    function will be btn (the name of the button that was clicked, if applicable,
15737                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15738                                    Progress and wait dialogs will ignore this option since they do not respond to
15739                                    user actions and can only be closed programmatically, so any required function
15740                                    should be called by the same code after it closes the dialog.
15741 icon              String           A CSS class that provides a background image to be used as an icon for
15742                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15743 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15744 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15745 modal             Boolean          False to allow user interaction with the page while the message box is
15746                                    displayed (defaults to true)
15747 msg               String           A string that will replace the existing message box body text (defaults
15748                                    to the XHTML-compliant non-breaking space character '&#160;')
15749 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15750 progress          Boolean          True to display a progress bar (defaults to false)
15751 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15752 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15753 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15754 title             String           The title text
15755 value             String           The string value to set into the active textbox element if displayed
15756 wait              Boolean          True to display a progress bar (defaults to false)
15757 width             Number           The width of the dialog in pixels
15758 </pre>
15759          *
15760          * Example usage:
15761          * <pre><code>
15762 Roo.Msg.show({
15763    title: 'Address',
15764    msg: 'Please enter your address:',
15765    width: 300,
15766    buttons: Roo.MessageBox.OKCANCEL,
15767    multiline: true,
15768    fn: saveAddress,
15769    animEl: 'addAddressBtn'
15770 });
15771 </code></pre>
15772          * @param {Object} config Configuration options
15773          * @return {Roo.MessageBox} This message box
15774          */
15775         show : function(options)
15776         {
15777             
15778             // this causes nightmares if you show one dialog after another
15779             // especially on callbacks..
15780              
15781             if(this.isVisible()){
15782                 
15783                 this.hide();
15784                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
15785                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
15786                 Roo.log("New Dialog Message:" +  options.msg )
15787                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15788                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15789                 
15790             }
15791             var d = this.getDialog();
15792             opt = options;
15793             d.setTitle(opt.title || "&#160;");
15794             d.close.setDisplayed(opt.closable !== false);
15795             activeTextEl = textboxEl;
15796             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15797             if(opt.prompt){
15798                 if(opt.multiline){
15799                     textboxEl.hide();
15800                     textareaEl.show();
15801                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15802                         opt.multiline : this.defaultTextHeight);
15803                     activeTextEl = textareaEl;
15804                 }else{
15805                     textboxEl.show();
15806                     textareaEl.hide();
15807                 }
15808             }else{
15809                 textboxEl.hide();
15810                 textareaEl.hide();
15811             }
15812             progressEl.setDisplayed(opt.progress === true);
15813             this.updateProgress(0);
15814             activeTextEl.dom.value = opt.value || "";
15815             if(opt.prompt){
15816                 dlg.setDefaultButton(activeTextEl);
15817             }else{
15818                 var bs = opt.buttons;
15819                 var db = null;
15820                 if(bs && bs.ok){
15821                     db = buttons["ok"];
15822                 }else if(bs && bs.yes){
15823                     db = buttons["yes"];
15824                 }
15825                 dlg.setDefaultButton(db);
15826             }
15827             bwidth = updateButtons(opt.buttons);
15828             this.updateText(opt.msg);
15829             if(opt.cls){
15830                 d.el.addClass(opt.cls);
15831             }
15832             d.proxyDrag = opt.proxyDrag === true;
15833             d.modal = opt.modal !== false;
15834             d.mask = opt.modal !== false ? mask : false;
15835             if(!d.isVisible()){
15836                 // force it to the end of the z-index stack so it gets a cursor in FF
15837                 document.body.appendChild(dlg.el.dom);
15838                 d.animateTarget = null;
15839                 d.show(options.animEl);
15840             }
15841             return this;
15842         },
15843
15844         /**
15845          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15846          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15847          * and closing the message box when the process is complete.
15848          * @param {String} title The title bar text
15849          * @param {String} msg The message box body text
15850          * @return {Roo.MessageBox} This message box
15851          */
15852         progress : function(title, msg){
15853             this.show({
15854                 title : title,
15855                 msg : msg,
15856                 buttons: false,
15857                 progress:true,
15858                 closable:false,
15859                 minWidth: this.minProgressWidth,
15860                 modal : true
15861             });
15862             return this;
15863         },
15864
15865         /**
15866          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15867          * If a callback function is passed it will be called after the user clicks the button, and the
15868          * id of the button that was clicked will be passed as the only parameter to the callback
15869          * (could also be the top-right close button).
15870          * @param {String} title The title bar text
15871          * @param {String} msg The message box body text
15872          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15873          * @param {Object} scope (optional) The scope of the callback function
15874          * @return {Roo.MessageBox} This message box
15875          */
15876         alert : function(title, msg, fn, scope){
15877             this.show({
15878                 title : title,
15879                 msg : msg,
15880                 buttons: this.OK,
15881                 fn: fn,
15882                 scope : scope,
15883                 modal : true
15884             });
15885             return this;
15886         },
15887
15888         /**
15889          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15890          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15891          * You are responsible for closing the message box when the process is complete.
15892          * @param {String} msg The message box body text
15893          * @param {String} title (optional) The title bar text
15894          * @return {Roo.MessageBox} This message box
15895          */
15896         wait : function(msg, title){
15897             this.show({
15898                 title : title,
15899                 msg : msg,
15900                 buttons: false,
15901                 closable:false,
15902                 progress:true,
15903                 modal:true,
15904                 width:300,
15905                 wait:true
15906             });
15907             waitTimer = Roo.TaskMgr.start({
15908                 run: function(i){
15909                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15910                 },
15911                 interval: 1000
15912             });
15913             return this;
15914         },
15915
15916         /**
15917          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15918          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15919          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15920          * @param {String} title The title bar text
15921          * @param {String} msg The message box body text
15922          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15923          * @param {Object} scope (optional) The scope of the callback function
15924          * @return {Roo.MessageBox} This message box
15925          */
15926         confirm : function(title, msg, fn, scope){
15927             this.show({
15928                 title : title,
15929                 msg : msg,
15930                 buttons: this.YESNO,
15931                 fn: fn,
15932                 scope : scope,
15933                 modal : true
15934             });
15935             return this;
15936         },
15937
15938         /**
15939          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15940          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15941          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15942          * (could also be the top-right close button) and the text that was entered will be passed as the two
15943          * parameters to the callback.
15944          * @param {String} title The title bar text
15945          * @param {String} msg The message box body text
15946          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15947          * @param {Object} scope (optional) The scope of the callback function
15948          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15949          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15950          * @return {Roo.MessageBox} This message box
15951          */
15952         prompt : function(title, msg, fn, scope, multiline){
15953             this.show({
15954                 title : title,
15955                 msg : msg,
15956                 buttons: this.OKCANCEL,
15957                 fn: fn,
15958                 minWidth:250,
15959                 scope : scope,
15960                 prompt:true,
15961                 multiline: multiline,
15962                 modal : true
15963             });
15964             return this;
15965         },
15966
15967         /**
15968          * Button config that displays a single OK button
15969          * @type Object
15970          */
15971         OK : {ok:true},
15972         /**
15973          * Button config that displays Yes and No buttons
15974          * @type Object
15975          */
15976         YESNO : {yes:true, no:true},
15977         /**
15978          * Button config that displays OK and Cancel buttons
15979          * @type Object
15980          */
15981         OKCANCEL : {ok:true, cancel:true},
15982         /**
15983          * Button config that displays Yes, No and Cancel buttons
15984          * @type Object
15985          */
15986         YESNOCANCEL : {yes:true, no:true, cancel:true},
15987
15988         /**
15989          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15990          * @type Number
15991          */
15992         defaultTextHeight : 75,
15993         /**
15994          * The maximum width in pixels of the message box (defaults to 600)
15995          * @type Number
15996          */
15997         maxWidth : 600,
15998         /**
15999          * The minimum width in pixels of the message box (defaults to 100)
16000          * @type Number
16001          */
16002         minWidth : 100,
16003         /**
16004          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
16005          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
16006          * @type Number
16007          */
16008         minProgressWidth : 250,
16009         /**
16010          * An object containing the default button text strings that can be overriden for localized language support.
16011          * Supported properties are: ok, cancel, yes and no.
16012          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
16013          * @type Object
16014          */
16015         buttonText : {
16016             ok : "OK",
16017             cancel : "Cancel",
16018             yes : "Yes",
16019             no : "No"
16020         }
16021     };
16022 }();
16023
16024 /**
16025  * Shorthand for {@link Roo.MessageBox}
16026  */
16027 Roo.Msg = Roo.MessageBox;/*
16028  * Based on:
16029  * Ext JS Library 1.1.1
16030  * Copyright(c) 2006-2007, Ext JS, LLC.
16031  *
16032  * Originally Released Under LGPL - original licence link has changed is not relivant.
16033  *
16034  * Fork - LGPL
16035  * <script type="text/javascript">
16036  */
16037 /**
16038  * @class Roo.QuickTips
16039  * Provides attractive and customizable tooltips for any element.
16040  * @singleton
16041  */
16042 Roo.QuickTips = function(){
16043     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
16044     var ce, bd, xy, dd;
16045     var visible = false, disabled = true, inited = false;
16046     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
16047     
16048     var onOver = function(e){
16049         if(disabled){
16050             return;
16051         }
16052         var t = e.getTarget();
16053         if(!t || t.nodeType !== 1 || t == document || t == document.body){
16054             return;
16055         }
16056         if(ce && t == ce.el){
16057             clearTimeout(hideProc);
16058             return;
16059         }
16060         if(t && tagEls[t.id]){
16061             tagEls[t.id].el = t;
16062             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
16063             return;
16064         }
16065         var ttp, et = Roo.fly(t);
16066         var ns = cfg.namespace;
16067         if(tm.interceptTitles && t.title){
16068             ttp = t.title;
16069             t.qtip = ttp;
16070             t.removeAttribute("title");
16071             e.preventDefault();
16072         }else{
16073             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
16074         }
16075         if(ttp){
16076             showProc = show.defer(tm.showDelay, tm, [{
16077                 el: t, 
16078                 text: ttp, 
16079                 width: et.getAttributeNS(ns, cfg.width),
16080                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
16081                 title: et.getAttributeNS(ns, cfg.title),
16082                     cls: et.getAttributeNS(ns, cfg.cls)
16083             }]);
16084         }
16085     };
16086     
16087     var onOut = function(e){
16088         clearTimeout(showProc);
16089         var t = e.getTarget();
16090         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
16091             hideProc = setTimeout(hide, tm.hideDelay);
16092         }
16093     };
16094     
16095     var onMove = function(e){
16096         if(disabled){
16097             return;
16098         }
16099         xy = e.getXY();
16100         xy[1] += 18;
16101         if(tm.trackMouse && ce){
16102             el.setXY(xy);
16103         }
16104     };
16105     
16106     var onDown = function(e){
16107         clearTimeout(showProc);
16108         clearTimeout(hideProc);
16109         if(!e.within(el)){
16110             if(tm.hideOnClick){
16111                 hide();
16112                 tm.disable();
16113                 tm.enable.defer(100, tm);
16114             }
16115         }
16116     };
16117     
16118     var getPad = function(){
16119         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
16120     };
16121
16122     var show = function(o){
16123         if(disabled){
16124             return;
16125         }
16126         clearTimeout(dismissProc);
16127         ce = o;
16128         if(removeCls){ // in case manually hidden
16129             el.removeClass(removeCls);
16130             removeCls = null;
16131         }
16132         if(ce.cls){
16133             el.addClass(ce.cls);
16134             removeCls = ce.cls;
16135         }
16136         if(ce.title){
16137             tipTitle.update(ce.title);
16138             tipTitle.show();
16139         }else{
16140             tipTitle.update('');
16141             tipTitle.hide();
16142         }
16143         el.dom.style.width  = tm.maxWidth+'px';
16144         //tipBody.dom.style.width = '';
16145         tipBodyText.update(o.text);
16146         var p = getPad(), w = ce.width;
16147         if(!w){
16148             var td = tipBodyText.dom;
16149             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
16150             if(aw > tm.maxWidth){
16151                 w = tm.maxWidth;
16152             }else if(aw < tm.minWidth){
16153                 w = tm.minWidth;
16154             }else{
16155                 w = aw;
16156             }
16157         }
16158         //tipBody.setWidth(w);
16159         el.setWidth(parseInt(w, 10) + p);
16160         if(ce.autoHide === false){
16161             close.setDisplayed(true);
16162             if(dd){
16163                 dd.unlock();
16164             }
16165         }else{
16166             close.setDisplayed(false);
16167             if(dd){
16168                 dd.lock();
16169             }
16170         }
16171         if(xy){
16172             el.avoidY = xy[1]-18;
16173             el.setXY(xy);
16174         }
16175         if(tm.animate){
16176             el.setOpacity(.1);
16177             el.setStyle("visibility", "visible");
16178             el.fadeIn({callback: afterShow});
16179         }else{
16180             afterShow();
16181         }
16182     };
16183     
16184     var afterShow = function(){
16185         if(ce){
16186             el.show();
16187             esc.enable();
16188             if(tm.autoDismiss && ce.autoHide !== false){
16189                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16190             }
16191         }
16192     };
16193     
16194     var hide = function(noanim){
16195         clearTimeout(dismissProc);
16196         clearTimeout(hideProc);
16197         ce = null;
16198         if(el.isVisible()){
16199             esc.disable();
16200             if(noanim !== true && tm.animate){
16201                 el.fadeOut({callback: afterHide});
16202             }else{
16203                 afterHide();
16204             } 
16205         }
16206     };
16207     
16208     var afterHide = function(){
16209         el.hide();
16210         if(removeCls){
16211             el.removeClass(removeCls);
16212             removeCls = null;
16213         }
16214     };
16215     
16216     return {
16217         /**
16218         * @cfg {Number} minWidth
16219         * The minimum width of the quick tip (defaults to 40)
16220         */
16221        minWidth : 40,
16222         /**
16223         * @cfg {Number} maxWidth
16224         * The maximum width of the quick tip (defaults to 300)
16225         */
16226        maxWidth : 300,
16227         /**
16228         * @cfg {Boolean} interceptTitles
16229         * True to automatically use the element's DOM title value if available (defaults to false)
16230         */
16231        interceptTitles : false,
16232         /**
16233         * @cfg {Boolean} trackMouse
16234         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16235         */
16236        trackMouse : false,
16237         /**
16238         * @cfg {Boolean} hideOnClick
16239         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16240         */
16241        hideOnClick : true,
16242         /**
16243         * @cfg {Number} showDelay
16244         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16245         */
16246        showDelay : 500,
16247         /**
16248         * @cfg {Number} hideDelay
16249         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16250         */
16251        hideDelay : 200,
16252         /**
16253         * @cfg {Boolean} autoHide
16254         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16255         * Used in conjunction with hideDelay.
16256         */
16257        autoHide : true,
16258         /**
16259         * @cfg {Boolean}
16260         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16261         * (defaults to true).  Used in conjunction with autoDismissDelay.
16262         */
16263        autoDismiss : true,
16264         /**
16265         * @cfg {Number}
16266         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16267         */
16268        autoDismissDelay : 5000,
16269        /**
16270         * @cfg {Boolean} animate
16271         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16272         */
16273        animate : false,
16274
16275        /**
16276         * @cfg {String} title
16277         * Title text to display (defaults to '').  This can be any valid HTML markup.
16278         */
16279         title: '',
16280        /**
16281         * @cfg {String} text
16282         * Body text to display (defaults to '').  This can be any valid HTML markup.
16283         */
16284         text : '',
16285        /**
16286         * @cfg {String} cls
16287         * A CSS class to apply to the base quick tip element (defaults to '').
16288         */
16289         cls : '',
16290        /**
16291         * @cfg {Number} width
16292         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16293         * minWidth or maxWidth.
16294         */
16295         width : null,
16296
16297     /**
16298      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16299      * or display QuickTips in a page.
16300      */
16301        init : function(){
16302           tm = Roo.QuickTips;
16303           cfg = tm.tagConfig;
16304           if(!inited){
16305               if(!Roo.isReady){ // allow calling of init() before onReady
16306                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16307                   return;
16308               }
16309               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16310               el.fxDefaults = {stopFx: true};
16311               // maximum custom styling
16312               //el.update('<div class="x-tip-top-left"><div class="x-tip-top-right"><div class="x-tip-top"></div></div></div><div class="x-tip-bd-left"><div class="x-tip-bd-right"><div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div></div></div><div class="x-tip-ft-left"><div class="x-tip-ft-right"><div class="x-tip-ft"></div></div></div>');
16313               el.update('<div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div>');              
16314               tipTitle = el.child('h3');
16315               tipTitle.enableDisplayMode("block");
16316               tipBody = el.child('div.x-tip-bd');
16317               tipBodyText = el.child('div.x-tip-bd-inner');
16318               //bdLeft = el.child('div.x-tip-bd-left');
16319               //bdRight = el.child('div.x-tip-bd-right');
16320               close = el.child('div.x-tip-close');
16321               close.enableDisplayMode("block");
16322               close.on("click", hide);
16323               var d = Roo.get(document);
16324               d.on("mousedown", onDown);
16325               d.on("mouseover", onOver);
16326               d.on("mouseout", onOut);
16327               d.on("mousemove", onMove);
16328               esc = d.addKeyListener(27, hide);
16329               esc.disable();
16330               if(Roo.dd.DD){
16331                   dd = el.initDD("default", null, {
16332                       onDrag : function(){
16333                           el.sync();  
16334                       }
16335                   });
16336                   dd.setHandleElId(tipTitle.id);
16337                   dd.lock();
16338               }
16339               inited = true;
16340           }
16341           this.enable(); 
16342        },
16343
16344     /**
16345      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16346      * are supported:
16347      * <pre>
16348 Property    Type                   Description
16349 ----------  ---------------------  ------------------------------------------------------------------------
16350 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16351      * </ul>
16352      * @param {Object} config The config object
16353      */
16354        register : function(config){
16355            var cs = config instanceof Array ? config : arguments;
16356            for(var i = 0, len = cs.length; i < len; i++) {
16357                var c = cs[i];
16358                var target = c.target;
16359                if(target){
16360                    if(target instanceof Array){
16361                        for(var j = 0, jlen = target.length; j < jlen; j++){
16362                            tagEls[target[j]] = c;
16363                        }
16364                    }else{
16365                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16366                    }
16367                }
16368            }
16369        },
16370
16371     /**
16372      * Removes this quick tip from its element and destroys it.
16373      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16374      */
16375        unregister : function(el){
16376            delete tagEls[Roo.id(el)];
16377        },
16378
16379     /**
16380      * Enable this quick tip.
16381      */
16382        enable : function(){
16383            if(inited && disabled){
16384                locks.pop();
16385                if(locks.length < 1){
16386                    disabled = false;
16387                }
16388            }
16389        },
16390
16391     /**
16392      * Disable this quick tip.
16393      */
16394        disable : function(){
16395           disabled = true;
16396           clearTimeout(showProc);
16397           clearTimeout(hideProc);
16398           clearTimeout(dismissProc);
16399           if(ce){
16400               hide(true);
16401           }
16402           locks.push(1);
16403        },
16404
16405     /**
16406      * Returns true if the quick tip is enabled, else false.
16407      */
16408        isEnabled : function(){
16409             return !disabled;
16410        },
16411
16412         // private
16413        tagConfig : {
16414            namespace : "ext",
16415            attribute : "qtip",
16416            width : "width",
16417            target : "target",
16418            title : "qtitle",
16419            hide : "hide",
16420            cls : "qclass"
16421        }
16422    };
16423 }();
16424
16425 // backwards compat
16426 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16427  * Based on:
16428  * Ext JS Library 1.1.1
16429  * Copyright(c) 2006-2007, Ext JS, LLC.
16430  *
16431  * Originally Released Under LGPL - original licence link has changed is not relivant.
16432  *
16433  * Fork - LGPL
16434  * <script type="text/javascript">
16435  */
16436  
16437
16438 /**
16439  * @class Roo.tree.TreePanel
16440  * @extends Roo.data.Tree
16441
16442  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16443  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16444  * @cfg {Boolean} enableDD true to enable drag and drop
16445  * @cfg {Boolean} enableDrag true to enable just drag
16446  * @cfg {Boolean} enableDrop true to enable just drop
16447  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16448  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16449  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16450  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16451  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16452  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16453  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16454  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16455  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16456  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16457  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16458  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16459  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
16460  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16461  * @cfg {Function} renderer DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
16462  * @cfg {Function} rendererTip DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
16463  * 
16464  * @constructor
16465  * @param {String/HTMLElement/Element} el The container element
16466  * @param {Object} config
16467  */
16468 Roo.tree.TreePanel = function(el, config){
16469     var root = false;
16470     var loader = false;
16471     if (config.root) {
16472         root = config.root;
16473         delete config.root;
16474     }
16475     if (config.loader) {
16476         loader = config.loader;
16477         delete config.loader;
16478     }
16479     
16480     Roo.apply(this, config);
16481     Roo.tree.TreePanel.superclass.constructor.call(this);
16482     this.el = Roo.get(el);
16483     this.el.addClass('x-tree');
16484     //console.log(root);
16485     if (root) {
16486         this.setRootNode( Roo.factory(root, Roo.tree));
16487     }
16488     if (loader) {
16489         this.loader = Roo.factory(loader, Roo.tree);
16490     }
16491    /**
16492     * Read-only. The id of the container element becomes this TreePanel's id.
16493     */
16494     this.id = this.el.id;
16495     this.addEvents({
16496         /**
16497         * @event beforeload
16498         * Fires before a node is loaded, return false to cancel
16499         * @param {Node} node The node being loaded
16500         */
16501         "beforeload" : true,
16502         /**
16503         * @event load
16504         * Fires when a node is loaded
16505         * @param {Node} node The node that was loaded
16506         */
16507         "load" : true,
16508         /**
16509         * @event textchange
16510         * Fires when the text for a node is changed
16511         * @param {Node} node The node
16512         * @param {String} text The new text
16513         * @param {String} oldText The old text
16514         */
16515         "textchange" : true,
16516         /**
16517         * @event beforeexpand
16518         * Fires before a node is expanded, return false to cancel.
16519         * @param {Node} node The node
16520         * @param {Boolean} deep
16521         * @param {Boolean} anim
16522         */
16523         "beforeexpand" : true,
16524         /**
16525         * @event beforecollapse
16526         * Fires before a node is collapsed, return false to cancel.
16527         * @param {Node} node The node
16528         * @param {Boolean} deep
16529         * @param {Boolean} anim
16530         */
16531         "beforecollapse" : true,
16532         /**
16533         * @event expand
16534         * Fires when a node is expanded
16535         * @param {Node} node The node
16536         */
16537         "expand" : true,
16538         /**
16539         * @event disabledchange
16540         * Fires when the disabled status of a node changes
16541         * @param {Node} node The node
16542         * @param {Boolean} disabled
16543         */
16544         "disabledchange" : true,
16545         /**
16546         * @event collapse
16547         * Fires when a node is collapsed
16548         * @param {Node} node The node
16549         */
16550         "collapse" : true,
16551         /**
16552         * @event beforeclick
16553         * Fires before click processing on a node. Return false to cancel the default action.
16554         * @param {Node} node The node
16555         * @param {Roo.EventObject} e The event object
16556         */
16557         "beforeclick":true,
16558         /**
16559         * @event checkchange
16560         * Fires when a node with a checkbox's checked property changes
16561         * @param {Node} this This node
16562         * @param {Boolean} checked
16563         */
16564         "checkchange":true,
16565         /**
16566         * @event click
16567         * Fires when a node is clicked
16568         * @param {Node} node The node
16569         * @param {Roo.EventObject} e The event object
16570         */
16571         "click":true,
16572         /**
16573         * @event dblclick
16574         * Fires when a node is double clicked
16575         * @param {Node} node The node
16576         * @param {Roo.EventObject} e The event object
16577         */
16578         "dblclick":true,
16579         /**
16580         * @event contextmenu
16581         * Fires when a node is right clicked
16582         * @param {Node} node The node
16583         * @param {Roo.EventObject} e The event object
16584         */
16585         "contextmenu":true,
16586         /**
16587         * @event beforechildrenrendered
16588         * Fires right before the child nodes for a node are rendered
16589         * @param {Node} node The node
16590         */
16591         "beforechildrenrendered":true,
16592         /**
16593         * @event startdrag
16594         * Fires when a node starts being dragged
16595         * @param {Roo.tree.TreePanel} this
16596         * @param {Roo.tree.TreeNode} node
16597         * @param {event} e The raw browser event
16598         */ 
16599        "startdrag" : true,
16600        /**
16601         * @event enddrag
16602         * Fires when a drag operation is complete
16603         * @param {Roo.tree.TreePanel} this
16604         * @param {Roo.tree.TreeNode} node
16605         * @param {event} e The raw browser event
16606         */
16607        "enddrag" : true,
16608        /**
16609         * @event dragdrop
16610         * Fires when a dragged node is dropped on a valid DD target
16611         * @param {Roo.tree.TreePanel} this
16612         * @param {Roo.tree.TreeNode} node
16613         * @param {DD} dd The dd it was dropped on
16614         * @param {event} e The raw browser event
16615         */
16616        "dragdrop" : true,
16617        /**
16618         * @event beforenodedrop
16619         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16620         * passed to handlers has the following properties:<br />
16621         * <ul style="padding:5px;padding-left:16px;">
16622         * <li>tree - The TreePanel</li>
16623         * <li>target - The node being targeted for the drop</li>
16624         * <li>data - The drag data from the drag source</li>
16625         * <li>point - The point of the drop - append, above or below</li>
16626         * <li>source - The drag source</li>
16627         * <li>rawEvent - Raw mouse event</li>
16628         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16629         * to be inserted by setting them on this object.</li>
16630         * <li>cancel - Set this to true to cancel the drop.</li>
16631         * </ul>
16632         * @param {Object} dropEvent
16633         */
16634        "beforenodedrop" : true,
16635        /**
16636         * @event nodedrop
16637         * Fires after a DD object is dropped on a node in this tree. The dropEvent
16638         * passed to handlers has the following properties:<br />
16639         * <ul style="padding:5px;padding-left:16px;">
16640         * <li>tree - The TreePanel</li>
16641         * <li>target - The node being targeted for the drop</li>
16642         * <li>data - The drag data from the drag source</li>
16643         * <li>point - The point of the drop - append, above or below</li>
16644         * <li>source - The drag source</li>
16645         * <li>rawEvent - Raw mouse event</li>
16646         * <li>dropNode - Dropped node(s).</li>
16647         * </ul>
16648         * @param {Object} dropEvent
16649         */
16650        "nodedrop" : true,
16651         /**
16652         * @event nodedragover
16653         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16654         * passed to handlers has the following properties:<br />
16655         * <ul style="padding:5px;padding-left:16px;">
16656         * <li>tree - The TreePanel</li>
16657         * <li>target - The node being targeted for the drop</li>
16658         * <li>data - The drag data from the drag source</li>
16659         * <li>point - The point of the drop - append, above or below</li>
16660         * <li>source - The drag source</li>
16661         * <li>rawEvent - Raw mouse event</li>
16662         * <li>dropNode - Drop node(s) provided by the source.</li>
16663         * <li>cancel - Set this to true to signal drop not allowed.</li>
16664         * </ul>
16665         * @param {Object} dragOverEvent
16666         */
16667        "nodedragover" : true
16668         
16669     });
16670     if(this.singleExpand){
16671        this.on("beforeexpand", this.restrictExpand, this);
16672     }
16673     if (this.editor) {
16674         this.editor.tree = this;
16675         this.editor = Roo.factory(this.editor, Roo.tree);
16676     }
16677     
16678     if (this.selModel) {
16679         this.selModel = Roo.factory(this.selModel, Roo.tree);
16680     }
16681    
16682 };
16683 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16684     rootVisible : true,
16685     animate: Roo.enableFx,
16686     lines : true,
16687     enableDD : false,
16688     hlDrop : Roo.enableFx,
16689   
16690     renderer: false,
16691     
16692     rendererTip: false,
16693     // private
16694     restrictExpand : function(node){
16695         var p = node.parentNode;
16696         if(p){
16697             if(p.expandedChild && p.expandedChild.parentNode == p){
16698                 p.expandedChild.collapse();
16699             }
16700             p.expandedChild = node;
16701         }
16702     },
16703
16704     // private override
16705     setRootNode : function(node){
16706         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16707         if(!this.rootVisible){
16708             node.ui = new Roo.tree.RootTreeNodeUI(node);
16709         }
16710         return node;
16711     },
16712
16713     /**
16714      * Returns the container element for this TreePanel
16715      */
16716     getEl : function(){
16717         return this.el;
16718     },
16719
16720     /**
16721      * Returns the default TreeLoader for this TreePanel
16722      */
16723     getLoader : function(){
16724         return this.loader;
16725     },
16726
16727     /**
16728      * Expand all nodes
16729      */
16730     expandAll : function(){
16731         this.root.expand(true);
16732     },
16733
16734     /**
16735      * Collapse all nodes
16736      */
16737     collapseAll : function(){
16738         this.root.collapse(true);
16739     },
16740
16741     /**
16742      * Returns the selection model used by this TreePanel
16743      */
16744     getSelectionModel : function(){
16745         if(!this.selModel){
16746             this.selModel = new Roo.tree.DefaultSelectionModel();
16747         }
16748         return this.selModel;
16749     },
16750
16751     /**
16752      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16753      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16754      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16755      * @return {Array}
16756      */
16757     getChecked : function(a, startNode){
16758         startNode = startNode || this.root;
16759         var r = [];
16760         var f = function(){
16761             if(this.attributes.checked){
16762                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16763             }
16764         }
16765         startNode.cascade(f);
16766         return r;
16767     },
16768
16769     /**
16770      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16771      * @param {String} path
16772      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16773      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16774      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16775      */
16776     expandPath : function(path, attr, callback){
16777         attr = attr || "id";
16778         var keys = path.split(this.pathSeparator);
16779         var curNode = this.root;
16780         if(curNode.attributes[attr] != keys[1]){ // invalid root
16781             if(callback){
16782                 callback(false, null);
16783             }
16784             return;
16785         }
16786         var index = 1;
16787         var f = function(){
16788             if(++index == keys.length){
16789                 if(callback){
16790                     callback(true, curNode);
16791                 }
16792                 return;
16793             }
16794             var c = curNode.findChild(attr, keys[index]);
16795             if(!c){
16796                 if(callback){
16797                     callback(false, curNode);
16798                 }
16799                 return;
16800             }
16801             curNode = c;
16802             c.expand(false, false, f);
16803         };
16804         curNode.expand(false, false, f);
16805     },
16806
16807     /**
16808      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16809      * @param {String} path
16810      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16811      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16812      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16813      */
16814     selectPath : function(path, attr, callback){
16815         attr = attr || "id";
16816         var keys = path.split(this.pathSeparator);
16817         var v = keys.pop();
16818         if(keys.length > 0){
16819             var f = function(success, node){
16820                 if(success && node){
16821                     var n = node.findChild(attr, v);
16822                     if(n){
16823                         n.select();
16824                         if(callback){
16825                             callback(true, n);
16826                         }
16827                     }else if(callback){
16828                         callback(false, n);
16829                     }
16830                 }else{
16831                     if(callback){
16832                         callback(false, n);
16833                     }
16834                 }
16835             };
16836             this.expandPath(keys.join(this.pathSeparator), attr, f);
16837         }else{
16838             this.root.select();
16839             if(callback){
16840                 callback(true, this.root);
16841             }
16842         }
16843     },
16844
16845     getTreeEl : function(){
16846         return this.el;
16847     },
16848
16849     /**
16850      * Trigger rendering of this TreePanel
16851      */
16852     render : function(){
16853         if (this.innerCt) {
16854             return this; // stop it rendering more than once!!
16855         }
16856         
16857         this.innerCt = this.el.createChild({tag:"ul",
16858                cls:"x-tree-root-ct " +
16859                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16860
16861         if(this.containerScroll){
16862             Roo.dd.ScrollManager.register(this.el);
16863         }
16864         if((this.enableDD || this.enableDrop) && !this.dropZone){
16865            /**
16866             * The dropZone used by this tree if drop is enabled
16867             * @type Roo.tree.TreeDropZone
16868             */
16869              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16870                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16871            });
16872         }
16873         if((this.enableDD || this.enableDrag) && !this.dragZone){
16874            /**
16875             * The dragZone used by this tree if drag is enabled
16876             * @type Roo.tree.TreeDragZone
16877             */
16878             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16879                ddGroup: this.ddGroup || "TreeDD",
16880                scroll: this.ddScroll
16881            });
16882         }
16883         this.getSelectionModel().init(this);
16884         if (!this.root) {
16885             console.log("ROOT not set in tree");
16886             return;
16887         }
16888         this.root.render();
16889         if(!this.rootVisible){
16890             this.root.renderChildren();
16891         }
16892         return this;
16893     }
16894 });/*
16895  * Based on:
16896  * Ext JS Library 1.1.1
16897  * Copyright(c) 2006-2007, Ext JS, LLC.
16898  *
16899  * Originally Released Under LGPL - original licence link has changed is not relivant.
16900  *
16901  * Fork - LGPL
16902  * <script type="text/javascript">
16903  */
16904  
16905
16906 /**
16907  * @class Roo.tree.DefaultSelectionModel
16908  * @extends Roo.util.Observable
16909  * The default single selection for a TreePanel.
16910  * @param {Object} cfg Configuration
16911  */
16912 Roo.tree.DefaultSelectionModel = function(cfg){
16913    this.selNode = null;
16914    
16915    
16916    
16917    this.addEvents({
16918        /**
16919         * @event selectionchange
16920         * Fires when the selected node changes
16921         * @param {DefaultSelectionModel} this
16922         * @param {TreeNode} node the new selection
16923         */
16924        "selectionchange" : true,
16925
16926        /**
16927         * @event beforeselect
16928         * Fires before the selected node changes, return false to cancel the change
16929         * @param {DefaultSelectionModel} this
16930         * @param {TreeNode} node the new selection
16931         * @param {TreeNode} node the old selection
16932         */
16933        "beforeselect" : true
16934    });
16935    
16936     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
16937 };
16938
16939 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16940     init : function(tree){
16941         this.tree = tree;
16942         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16943         tree.on("click", this.onNodeClick, this);
16944     },
16945     
16946     onNodeClick : function(node, e){
16947         if (e.ctrlKey && this.selNode == node)  {
16948             this.unselect(node);
16949             return;
16950         }
16951         this.select(node);
16952     },
16953     
16954     /**
16955      * Select a node.
16956      * @param {TreeNode} node The node to select
16957      * @return {TreeNode} The selected node
16958      */
16959     select : function(node){
16960         var last = this.selNode;
16961         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16962             if(last){
16963                 last.ui.onSelectedChange(false);
16964             }
16965             this.selNode = node;
16966             node.ui.onSelectedChange(true);
16967             this.fireEvent("selectionchange", this, node, last);
16968         }
16969         return node;
16970     },
16971     
16972     /**
16973      * Deselect a node.
16974      * @param {TreeNode} node The node to unselect
16975      */
16976     unselect : function(node){
16977         if(this.selNode == node){
16978             this.clearSelections();
16979         }    
16980     },
16981     
16982     /**
16983      * Clear all selections
16984      */
16985     clearSelections : function(){
16986         var n = this.selNode;
16987         if(n){
16988             n.ui.onSelectedChange(false);
16989             this.selNode = null;
16990             this.fireEvent("selectionchange", this, null);
16991         }
16992         return n;
16993     },
16994     
16995     /**
16996      * Get the selected node
16997      * @return {TreeNode} The selected node
16998      */
16999     getSelectedNode : function(){
17000         return this.selNode;    
17001     },
17002     
17003     /**
17004      * Returns true if the node is selected
17005      * @param {TreeNode} node The node to check
17006      * @return {Boolean}
17007      */
17008     isSelected : function(node){
17009         return this.selNode == node;  
17010     },
17011
17012     /**
17013      * Selects the node above the selected node in the tree, intelligently walking the nodes
17014      * @return TreeNode The new selection
17015      */
17016     selectPrevious : function(){
17017         var s = this.selNode || this.lastSelNode;
17018         if(!s){
17019             return null;
17020         }
17021         var ps = s.previousSibling;
17022         if(ps){
17023             if(!ps.isExpanded() || ps.childNodes.length < 1){
17024                 return this.select(ps);
17025             } else{
17026                 var lc = ps.lastChild;
17027                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
17028                     lc = lc.lastChild;
17029                 }
17030                 return this.select(lc);
17031             }
17032         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
17033             return this.select(s.parentNode);
17034         }
17035         return null;
17036     },
17037
17038     /**
17039      * Selects the node above the selected node in the tree, intelligently walking the nodes
17040      * @return TreeNode The new selection
17041      */
17042     selectNext : function(){
17043         var s = this.selNode || this.lastSelNode;
17044         if(!s){
17045             return null;
17046         }
17047         if(s.firstChild && s.isExpanded()){
17048              return this.select(s.firstChild);
17049          }else if(s.nextSibling){
17050              return this.select(s.nextSibling);
17051          }else if(s.parentNode){
17052             var newS = null;
17053             s.parentNode.bubble(function(){
17054                 if(this.nextSibling){
17055                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
17056                     return false;
17057                 }
17058             });
17059             return newS;
17060          }
17061         return null;
17062     },
17063
17064     onKeyDown : function(e){
17065         var s = this.selNode || this.lastSelNode;
17066         // undesirable, but required
17067         var sm = this;
17068         if(!s){
17069             return;
17070         }
17071         var k = e.getKey();
17072         switch(k){
17073              case e.DOWN:
17074                  e.stopEvent();
17075                  this.selectNext();
17076              break;
17077              case e.UP:
17078                  e.stopEvent();
17079                  this.selectPrevious();
17080              break;
17081              case e.RIGHT:
17082                  e.preventDefault();
17083                  if(s.hasChildNodes()){
17084                      if(!s.isExpanded()){
17085                          s.expand();
17086                      }else if(s.firstChild){
17087                          this.select(s.firstChild, e);
17088                      }
17089                  }
17090              break;
17091              case e.LEFT:
17092                  e.preventDefault();
17093                  if(s.hasChildNodes() && s.isExpanded()){
17094                      s.collapse();
17095                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
17096                      this.select(s.parentNode, e);
17097                  }
17098              break;
17099         };
17100     }
17101 });
17102
17103 /**
17104  * @class Roo.tree.MultiSelectionModel
17105  * @extends Roo.util.Observable
17106  * Multi selection for a TreePanel.
17107  * @param {Object} cfg Configuration
17108  */
17109 Roo.tree.MultiSelectionModel = function(){
17110    this.selNodes = [];
17111    this.selMap = {};
17112    this.addEvents({
17113        /**
17114         * @event selectionchange
17115         * Fires when the selected nodes change
17116         * @param {MultiSelectionModel} this
17117         * @param {Array} nodes Array of the selected nodes
17118         */
17119        "selectionchange" : true
17120    });
17121    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
17122    
17123 };
17124
17125 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
17126     init : function(tree){
17127         this.tree = tree;
17128         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17129         tree.on("click", this.onNodeClick, this);
17130     },
17131     
17132     onNodeClick : function(node, e){
17133         this.select(node, e, e.ctrlKey);
17134     },
17135     
17136     /**
17137      * Select a node.
17138      * @param {TreeNode} node The node to select
17139      * @param {EventObject} e (optional) An event associated with the selection
17140      * @param {Boolean} keepExisting True to retain existing selections
17141      * @return {TreeNode} The selected node
17142      */
17143     select : function(node, e, keepExisting){
17144         if(keepExisting !== true){
17145             this.clearSelections(true);
17146         }
17147         if(this.isSelected(node)){
17148             this.lastSelNode = node;
17149             return node;
17150         }
17151         this.selNodes.push(node);
17152         this.selMap[node.id] = node;
17153         this.lastSelNode = node;
17154         node.ui.onSelectedChange(true);
17155         this.fireEvent("selectionchange", this, this.selNodes);
17156         return node;
17157     },
17158     
17159     /**
17160      * Deselect a node.
17161      * @param {TreeNode} node The node to unselect
17162      */
17163     unselect : function(node){
17164         if(this.selMap[node.id]){
17165             node.ui.onSelectedChange(false);
17166             var sn = this.selNodes;
17167             var index = -1;
17168             if(sn.indexOf){
17169                 index = sn.indexOf(node);
17170             }else{
17171                 for(var i = 0, len = sn.length; i < len; i++){
17172                     if(sn[i] == node){
17173                         index = i;
17174                         break;
17175                     }
17176                 }
17177             }
17178             if(index != -1){
17179                 this.selNodes.splice(index, 1);
17180             }
17181             delete this.selMap[node.id];
17182             this.fireEvent("selectionchange", this, this.selNodes);
17183         }
17184     },
17185     
17186     /**
17187      * Clear all selections
17188      */
17189     clearSelections : function(suppressEvent){
17190         var sn = this.selNodes;
17191         if(sn.length > 0){
17192             for(var i = 0, len = sn.length; i < len; i++){
17193                 sn[i].ui.onSelectedChange(false);
17194             }
17195             this.selNodes = [];
17196             this.selMap = {};
17197             if(suppressEvent !== true){
17198                 this.fireEvent("selectionchange", this, this.selNodes);
17199             }
17200         }
17201     },
17202     
17203     /**
17204      * Returns true if the node is selected
17205      * @param {TreeNode} node The node to check
17206      * @return {Boolean}
17207      */
17208     isSelected : function(node){
17209         return this.selMap[node.id] ? true : false;  
17210     },
17211     
17212     /**
17213      * Returns an array of the selected nodes
17214      * @return {Array}
17215      */
17216     getSelectedNodes : function(){
17217         return this.selNodes;    
17218     },
17219
17220     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17221
17222     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17223
17224     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17225 });/*
17226  * Based on:
17227  * Ext JS Library 1.1.1
17228  * Copyright(c) 2006-2007, Ext JS, LLC.
17229  *
17230  * Originally Released Under LGPL - original licence link has changed is not relivant.
17231  *
17232  * Fork - LGPL
17233  * <script type="text/javascript">
17234  */
17235  
17236 /**
17237  * @class Roo.tree.TreeNode
17238  * @extends Roo.data.Node
17239  * @cfg {String} text The text for this node
17240  * @cfg {Boolean} expanded true to start the node expanded
17241  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17242  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17243  * @cfg {Boolean} disabled true to start the node disabled
17244  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17245  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17246  * @cfg {String} cls A css class to be added to the node
17247  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17248  * @cfg {String} href URL of the link used for the node (defaults to #)
17249  * @cfg {String} hrefTarget target frame for the link
17250  * @cfg {String} qtip An Ext QuickTip for the node
17251  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17252  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17253  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17254  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17255  * (defaults to undefined with no checkbox rendered)
17256  * @constructor
17257  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17258  */
17259 Roo.tree.TreeNode = function(attributes){
17260     attributes = attributes || {};
17261     if(typeof attributes == "string"){
17262         attributes = {text: attributes};
17263     }
17264     this.childrenRendered = false;
17265     this.rendered = false;
17266     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17267     this.expanded = attributes.expanded === true;
17268     this.isTarget = attributes.isTarget !== false;
17269     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17270     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17271
17272     /**
17273      * Read-only. The text for this node. To change it use setText().
17274      * @type String
17275      */
17276     this.text = attributes.text;
17277     /**
17278      * True if this node is disabled.
17279      * @type Boolean
17280      */
17281     this.disabled = attributes.disabled === true;
17282
17283     this.addEvents({
17284         /**
17285         * @event textchange
17286         * Fires when the text for this node is changed
17287         * @param {Node} this This node
17288         * @param {String} text The new text
17289         * @param {String} oldText The old text
17290         */
17291         "textchange" : true,
17292         /**
17293         * @event beforeexpand
17294         * Fires before this node is expanded, return false to cancel.
17295         * @param {Node} this This node
17296         * @param {Boolean} deep
17297         * @param {Boolean} anim
17298         */
17299         "beforeexpand" : true,
17300         /**
17301         * @event beforecollapse
17302         * Fires before this node is collapsed, return false to cancel.
17303         * @param {Node} this This node
17304         * @param {Boolean} deep
17305         * @param {Boolean} anim
17306         */
17307         "beforecollapse" : true,
17308         /**
17309         * @event expand
17310         * Fires when this node is expanded
17311         * @param {Node} this This node
17312         */
17313         "expand" : true,
17314         /**
17315         * @event disabledchange
17316         * Fires when the disabled status of this node changes
17317         * @param {Node} this This node
17318         * @param {Boolean} disabled
17319         */
17320         "disabledchange" : true,
17321         /**
17322         * @event collapse
17323         * Fires when this node is collapsed
17324         * @param {Node} this This node
17325         */
17326         "collapse" : true,
17327         /**
17328         * @event beforeclick
17329         * Fires before click processing. Return false to cancel the default action.
17330         * @param {Node} this This node
17331         * @param {Roo.EventObject} e The event object
17332         */
17333         "beforeclick":true,
17334         /**
17335         * @event checkchange
17336         * Fires when a node with a checkbox's checked property changes
17337         * @param {Node} this This node
17338         * @param {Boolean} checked
17339         */
17340         "checkchange":true,
17341         /**
17342         * @event click
17343         * Fires when this node is clicked
17344         * @param {Node} this This node
17345         * @param {Roo.EventObject} e The event object
17346         */
17347         "click":true,
17348         /**
17349         * @event dblclick
17350         * Fires when this node is double clicked
17351         * @param {Node} this This node
17352         * @param {Roo.EventObject} e The event object
17353         */
17354         "dblclick":true,
17355         /**
17356         * @event contextmenu
17357         * Fires when this node is right clicked
17358         * @param {Node} this This node
17359         * @param {Roo.EventObject} e The event object
17360         */
17361         "contextmenu":true,
17362         /**
17363         * @event beforechildrenrendered
17364         * Fires right before the child nodes for this node are rendered
17365         * @param {Node} this This node
17366         */
17367         "beforechildrenrendered":true
17368     });
17369
17370     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17371
17372     /**
17373      * Read-only. The UI for this node
17374      * @type TreeNodeUI
17375      */
17376     this.ui = new uiClass(this);
17377     
17378     // finally support items[]
17379     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
17380         return;
17381     }
17382     
17383     
17384     Roo.each(this.attributes.items, function(c) {
17385         this.appendChild(Roo.factory(c,Roo.Tree));
17386     }, this);
17387     delete this.attributes.items;
17388     
17389     
17390     
17391 };
17392 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17393     preventHScroll: true,
17394     /**
17395      * Returns true if this node is expanded
17396      * @return {Boolean}
17397      */
17398     isExpanded : function(){
17399         return this.expanded;
17400     },
17401
17402     /**
17403      * Returns the UI object for this node
17404      * @return {TreeNodeUI}
17405      */
17406     getUI : function(){
17407         return this.ui;
17408     },
17409
17410     // private override
17411     setFirstChild : function(node){
17412         var of = this.firstChild;
17413         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17414         if(this.childrenRendered && of && node != of){
17415             of.renderIndent(true, true);
17416         }
17417         if(this.rendered){
17418             this.renderIndent(true, true);
17419         }
17420     },
17421
17422     // private override
17423     setLastChild : function(node){
17424         var ol = this.lastChild;
17425         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17426         if(this.childrenRendered && ol && node != ol){
17427             ol.renderIndent(true, true);
17428         }
17429         if(this.rendered){
17430             this.renderIndent(true, true);
17431         }
17432     },
17433
17434     // these methods are overridden to provide lazy rendering support
17435     // private override
17436     appendChild : function()
17437     {
17438         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17439         if(node && this.childrenRendered){
17440             node.render();
17441         }
17442         this.ui.updateExpandIcon();
17443         return node;
17444     },
17445
17446     // private override
17447     removeChild : function(node){
17448         this.ownerTree.getSelectionModel().unselect(node);
17449         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17450         // if it's been rendered remove dom node
17451         if(this.childrenRendered){
17452             node.ui.remove();
17453         }
17454         if(this.childNodes.length < 1){
17455             this.collapse(false, false);
17456         }else{
17457             this.ui.updateExpandIcon();
17458         }
17459         if(!this.firstChild) {
17460             this.childrenRendered = false;
17461         }
17462         return node;
17463     },
17464
17465     // private override
17466     insertBefore : function(node, refNode){
17467         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17468         if(newNode && refNode && this.childrenRendered){
17469             node.render();
17470         }
17471         this.ui.updateExpandIcon();
17472         return newNode;
17473     },
17474
17475     /**
17476      * Sets the text for this node
17477      * @param {String} text
17478      */
17479     setText : function(text){
17480         var oldText = this.text;
17481         this.text = text;
17482         this.attributes.text = text;
17483         if(this.rendered){ // event without subscribing
17484             this.ui.onTextChange(this, text, oldText);
17485         }
17486         this.fireEvent("textchange", this, text, oldText);
17487     },
17488
17489     /**
17490      * Triggers selection of this node
17491      */
17492     select : function(){
17493         this.getOwnerTree().getSelectionModel().select(this);
17494     },
17495
17496     /**
17497      * Triggers deselection of this node
17498      */
17499     unselect : function(){
17500         this.getOwnerTree().getSelectionModel().unselect(this);
17501     },
17502
17503     /**
17504      * Returns true if this node is selected
17505      * @return {Boolean}
17506      */
17507     isSelected : function(){
17508         return this.getOwnerTree().getSelectionModel().isSelected(this);
17509     },
17510
17511     /**
17512      * Expand this node.
17513      * @param {Boolean} deep (optional) True to expand all children as well
17514      * @param {Boolean} anim (optional) false to cancel the default animation
17515      * @param {Function} callback (optional) A callback to be called when
17516      * expanding this node completes (does not wait for deep expand to complete).
17517      * Called with 1 parameter, this node.
17518      */
17519     expand : function(deep, anim, callback){
17520         if(!this.expanded){
17521             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17522                 return;
17523             }
17524             if(!this.childrenRendered){
17525                 this.renderChildren();
17526             }
17527             this.expanded = true;
17528             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17529                 this.ui.animExpand(function(){
17530                     this.fireEvent("expand", this);
17531                     if(typeof callback == "function"){
17532                         callback(this);
17533                     }
17534                     if(deep === true){
17535                         this.expandChildNodes(true);
17536                     }
17537                 }.createDelegate(this));
17538                 return;
17539             }else{
17540                 this.ui.expand();
17541                 this.fireEvent("expand", this);
17542                 if(typeof callback == "function"){
17543                     callback(this);
17544                 }
17545             }
17546         }else{
17547            if(typeof callback == "function"){
17548                callback(this);
17549            }
17550         }
17551         if(deep === true){
17552             this.expandChildNodes(true);
17553         }
17554     },
17555
17556     isHiddenRoot : function(){
17557         return this.isRoot && !this.getOwnerTree().rootVisible;
17558     },
17559
17560     /**
17561      * Collapse this node.
17562      * @param {Boolean} deep (optional) True to collapse all children as well
17563      * @param {Boolean} anim (optional) false to cancel the default animation
17564      */
17565     collapse : function(deep, anim){
17566         if(this.expanded && !this.isHiddenRoot()){
17567             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17568                 return;
17569             }
17570             this.expanded = false;
17571             if((this.getOwnerTree().animate && anim !== false) || anim){
17572                 this.ui.animCollapse(function(){
17573                     this.fireEvent("collapse", this);
17574                     if(deep === true){
17575                         this.collapseChildNodes(true);
17576                     }
17577                 }.createDelegate(this));
17578                 return;
17579             }else{
17580                 this.ui.collapse();
17581                 this.fireEvent("collapse", this);
17582             }
17583         }
17584         if(deep === true){
17585             var cs = this.childNodes;
17586             for(var i = 0, len = cs.length; i < len; i++) {
17587                 cs[i].collapse(true, false);
17588             }
17589         }
17590     },
17591
17592     // private
17593     delayedExpand : function(delay){
17594         if(!this.expandProcId){
17595             this.expandProcId = this.expand.defer(delay, this);
17596         }
17597     },
17598
17599     // private
17600     cancelExpand : function(){
17601         if(this.expandProcId){
17602             clearTimeout(this.expandProcId);
17603         }
17604         this.expandProcId = false;
17605     },
17606
17607     /**
17608      * Toggles expanded/collapsed state of the node
17609      */
17610     toggle : function(){
17611         if(this.expanded){
17612             this.collapse();
17613         }else{
17614             this.expand();
17615         }
17616     },
17617
17618     /**
17619      * Ensures all parent nodes are expanded
17620      */
17621     ensureVisible : function(callback){
17622         var tree = this.getOwnerTree();
17623         tree.expandPath(this.parentNode.getPath(), false, function(){
17624             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17625             Roo.callback(callback);
17626         }.createDelegate(this));
17627     },
17628
17629     /**
17630      * Expand all child nodes
17631      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17632      */
17633     expandChildNodes : function(deep){
17634         var cs = this.childNodes;
17635         for(var i = 0, len = cs.length; i < len; i++) {
17636                 cs[i].expand(deep);
17637         }
17638     },
17639
17640     /**
17641      * Collapse all child nodes
17642      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17643      */
17644     collapseChildNodes : function(deep){
17645         var cs = this.childNodes;
17646         for(var i = 0, len = cs.length; i < len; i++) {
17647                 cs[i].collapse(deep);
17648         }
17649     },
17650
17651     /**
17652      * Disables this node
17653      */
17654     disable : function(){
17655         this.disabled = true;
17656         this.unselect();
17657         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17658             this.ui.onDisableChange(this, true);
17659         }
17660         this.fireEvent("disabledchange", this, true);
17661     },
17662
17663     /**
17664      * Enables this node
17665      */
17666     enable : function(){
17667         this.disabled = false;
17668         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17669             this.ui.onDisableChange(this, false);
17670         }
17671         this.fireEvent("disabledchange", this, false);
17672     },
17673
17674     // private
17675     renderChildren : function(suppressEvent){
17676         if(suppressEvent !== false){
17677             this.fireEvent("beforechildrenrendered", this);
17678         }
17679         var cs = this.childNodes;
17680         for(var i = 0, len = cs.length; i < len; i++){
17681             cs[i].render(true);
17682         }
17683         this.childrenRendered = true;
17684     },
17685
17686     // private
17687     sort : function(fn, scope){
17688         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17689         if(this.childrenRendered){
17690             var cs = this.childNodes;
17691             for(var i = 0, len = cs.length; i < len; i++){
17692                 cs[i].render(true);
17693             }
17694         }
17695     },
17696
17697     // private
17698     render : function(bulkRender){
17699         this.ui.render(bulkRender);
17700         if(!this.rendered){
17701             this.rendered = true;
17702             if(this.expanded){
17703                 this.expanded = false;
17704                 this.expand(false, false);
17705             }
17706         }
17707     },
17708
17709     // private
17710     renderIndent : function(deep, refresh){
17711         if(refresh){
17712             this.ui.childIndent = null;
17713         }
17714         this.ui.renderIndent();
17715         if(deep === true && this.childrenRendered){
17716             var cs = this.childNodes;
17717             for(var i = 0, len = cs.length; i < len; i++){
17718                 cs[i].renderIndent(true, refresh);
17719             }
17720         }
17721     }
17722 });/*
17723  * Based on:
17724  * Ext JS Library 1.1.1
17725  * Copyright(c) 2006-2007, Ext JS, LLC.
17726  *
17727  * Originally Released Under LGPL - original licence link has changed is not relivant.
17728  *
17729  * Fork - LGPL
17730  * <script type="text/javascript">
17731  */
17732  
17733 /**
17734  * @class Roo.tree.AsyncTreeNode
17735  * @extends Roo.tree.TreeNode
17736  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17737  * @constructor
17738  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17739  */
17740  Roo.tree.AsyncTreeNode = function(config){
17741     this.loaded = false;
17742     this.loading = false;
17743     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17744     /**
17745     * @event beforeload
17746     * Fires before this node is loaded, return false to cancel
17747     * @param {Node} this This node
17748     */
17749     this.addEvents({'beforeload':true, 'load': true});
17750     /**
17751     * @event load
17752     * Fires when this node is loaded
17753     * @param {Node} this This node
17754     */
17755     /**
17756      * The loader used by this node (defaults to using the tree's defined loader)
17757      * @type TreeLoader
17758      * @property loader
17759      */
17760 };
17761 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17762     expand : function(deep, anim, callback){
17763         if(this.loading){ // if an async load is already running, waiting til it's done
17764             var timer;
17765             var f = function(){
17766                 if(!this.loading){ // done loading
17767                     clearInterval(timer);
17768                     this.expand(deep, anim, callback);
17769                 }
17770             }.createDelegate(this);
17771             timer = setInterval(f, 200);
17772             return;
17773         }
17774         if(!this.loaded){
17775             if(this.fireEvent("beforeload", this) === false){
17776                 return;
17777             }
17778             this.loading = true;
17779             this.ui.beforeLoad(this);
17780             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17781             if(loader){
17782                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17783                 return;
17784             }
17785         }
17786         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17787     },
17788     
17789     /**
17790      * Returns true if this node is currently loading
17791      * @return {Boolean}
17792      */
17793     isLoading : function(){
17794         return this.loading;  
17795     },
17796     
17797     loadComplete : function(deep, anim, callback){
17798         this.loading = false;
17799         this.loaded = true;
17800         this.ui.afterLoad(this);
17801         this.fireEvent("load", this);
17802         this.expand(deep, anim, callback);
17803     },
17804     
17805     /**
17806      * Returns true if this node has been loaded
17807      * @return {Boolean}
17808      */
17809     isLoaded : function(){
17810         return this.loaded;
17811     },
17812     
17813     hasChildNodes : function(){
17814         if(!this.isLeaf() && !this.loaded){
17815             return true;
17816         }else{
17817             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17818         }
17819     },
17820
17821     /**
17822      * Trigger a reload for this node
17823      * @param {Function} callback
17824      */
17825     reload : function(callback){
17826         this.collapse(false, false);
17827         while(this.firstChild){
17828             this.removeChild(this.firstChild);
17829         }
17830         this.childrenRendered = false;
17831         this.loaded = false;
17832         if(this.isHiddenRoot()){
17833             this.expanded = false;
17834         }
17835         this.expand(false, false, callback);
17836     }
17837 });/*
17838  * Based on:
17839  * Ext JS Library 1.1.1
17840  * Copyright(c) 2006-2007, Ext JS, LLC.
17841  *
17842  * Originally Released Under LGPL - original licence link has changed is not relivant.
17843  *
17844  * Fork - LGPL
17845  * <script type="text/javascript">
17846  */
17847  
17848 /**
17849  * @class Roo.tree.TreeNodeUI
17850  * @constructor
17851  * @param {Object} node The node to render
17852  * The TreeNode UI implementation is separate from the
17853  * tree implementation. Unless you are customizing the tree UI,
17854  * you should never have to use this directly.
17855  */
17856 Roo.tree.TreeNodeUI = function(node){
17857     this.node = node;
17858     this.rendered = false;
17859     this.animating = false;
17860     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17861 };
17862
17863 Roo.tree.TreeNodeUI.prototype = {
17864     removeChild : function(node){
17865         if(this.rendered){
17866             this.ctNode.removeChild(node.ui.getEl());
17867         }
17868     },
17869
17870     beforeLoad : function(){
17871          this.addClass("x-tree-node-loading");
17872     },
17873
17874     afterLoad : function(){
17875          this.removeClass("x-tree-node-loading");
17876     },
17877
17878     onTextChange : function(node, text, oldText){
17879         if(this.rendered){
17880             this.textNode.innerHTML = text;
17881         }
17882     },
17883
17884     onDisableChange : function(node, state){
17885         this.disabled = state;
17886         if(state){
17887             this.addClass("x-tree-node-disabled");
17888         }else{
17889             this.removeClass("x-tree-node-disabled");
17890         }
17891     },
17892
17893     onSelectedChange : function(state){
17894         if(state){
17895             this.focus();
17896             this.addClass("x-tree-selected");
17897         }else{
17898             //this.blur();
17899             this.removeClass("x-tree-selected");
17900         }
17901     },
17902
17903     onMove : function(tree, node, oldParent, newParent, index, refNode){
17904         this.childIndent = null;
17905         if(this.rendered){
17906             var targetNode = newParent.ui.getContainer();
17907             if(!targetNode){//target not rendered
17908                 this.holder = document.createElement("div");
17909                 this.holder.appendChild(this.wrap);
17910                 return;
17911             }
17912             var insertBefore = refNode ? refNode.ui.getEl() : null;
17913             if(insertBefore){
17914                 targetNode.insertBefore(this.wrap, insertBefore);
17915             }else{
17916                 targetNode.appendChild(this.wrap);
17917             }
17918             this.node.renderIndent(true);
17919         }
17920     },
17921
17922     addClass : function(cls){
17923         if(this.elNode){
17924             Roo.fly(this.elNode).addClass(cls);
17925         }
17926     },
17927
17928     removeClass : function(cls){
17929         if(this.elNode){
17930             Roo.fly(this.elNode).removeClass(cls);
17931         }
17932     },
17933
17934     remove : function(){
17935         if(this.rendered){
17936             this.holder = document.createElement("div");
17937             this.holder.appendChild(this.wrap);
17938         }
17939     },
17940
17941     fireEvent : function(){
17942         return this.node.fireEvent.apply(this.node, arguments);
17943     },
17944
17945     initEvents : function(){
17946         this.node.on("move", this.onMove, this);
17947         var E = Roo.EventManager;
17948         var a = this.anchor;
17949
17950         var el = Roo.fly(a, '_treeui');
17951
17952         if(Roo.isOpera){ // opera render bug ignores the CSS
17953             el.setStyle("text-decoration", "none");
17954         }
17955
17956         el.on("click", this.onClick, this);
17957         el.on("dblclick", this.onDblClick, this);
17958
17959         if(this.checkbox){
17960             Roo.EventManager.on(this.checkbox,
17961                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17962         }
17963
17964         el.on("contextmenu", this.onContextMenu, this);
17965
17966         var icon = Roo.fly(this.iconNode);
17967         icon.on("click", this.onClick, this);
17968         icon.on("dblclick", this.onDblClick, this);
17969         icon.on("contextmenu", this.onContextMenu, this);
17970         E.on(this.ecNode, "click", this.ecClick, this, true);
17971
17972         if(this.node.disabled){
17973             this.addClass("x-tree-node-disabled");
17974         }
17975         if(this.node.hidden){
17976             this.addClass("x-tree-node-disabled");
17977         }
17978         var ot = this.node.getOwnerTree();
17979         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17980         if(dd && (!this.node.isRoot || ot.rootVisible)){
17981             Roo.dd.Registry.register(this.elNode, {
17982                 node: this.node,
17983                 handles: this.getDDHandles(),
17984                 isHandle: false
17985             });
17986         }
17987     },
17988
17989     getDDHandles : function(){
17990         return [this.iconNode, this.textNode];
17991     },
17992
17993     hide : function(){
17994         if(this.rendered){
17995             this.wrap.style.display = "none";
17996         }
17997     },
17998
17999     show : function(){
18000         if(this.rendered){
18001             this.wrap.style.display = "";
18002         }
18003     },
18004
18005     onContextMenu : function(e){
18006         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
18007             e.preventDefault();
18008             this.focus();
18009             this.fireEvent("contextmenu", this.node, e);
18010         }
18011     },
18012
18013     onClick : function(e){
18014         if(this.dropping){
18015             e.stopEvent();
18016             return;
18017         }
18018         if(this.fireEvent("beforeclick", this.node, e) !== false){
18019             if(!this.disabled && this.node.attributes.href){
18020                 this.fireEvent("click", this.node, e);
18021                 return;
18022             }
18023             e.preventDefault();
18024             if(this.disabled){
18025                 return;
18026             }
18027
18028             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
18029                 this.node.toggle();
18030             }
18031
18032             this.fireEvent("click", this.node, e);
18033         }else{
18034             e.stopEvent();
18035         }
18036     },
18037
18038     onDblClick : function(e){
18039         e.preventDefault();
18040         if(this.disabled){
18041             return;
18042         }
18043         if(this.checkbox){
18044             this.toggleCheck();
18045         }
18046         if(!this.animating && this.node.hasChildNodes()){
18047             this.node.toggle();
18048         }
18049         this.fireEvent("dblclick", this.node, e);
18050     },
18051
18052     onCheckChange : function(){
18053         var checked = this.checkbox.checked;
18054         this.node.attributes.checked = checked;
18055         this.fireEvent('checkchange', this.node, checked);
18056     },
18057
18058     ecClick : function(e){
18059         if(!this.animating && this.node.hasChildNodes()){
18060             this.node.toggle();
18061         }
18062     },
18063
18064     startDrop : function(){
18065         this.dropping = true;
18066     },
18067
18068     // delayed drop so the click event doesn't get fired on a drop
18069     endDrop : function(){
18070        setTimeout(function(){
18071            this.dropping = false;
18072        }.createDelegate(this), 50);
18073     },
18074
18075     expand : function(){
18076         this.updateExpandIcon();
18077         this.ctNode.style.display = "";
18078     },
18079
18080     focus : function(){
18081         if(!this.node.preventHScroll){
18082             try{this.anchor.focus();
18083             }catch(e){}
18084         }else if(!Roo.isIE){
18085             try{
18086                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
18087                 var l = noscroll.scrollLeft;
18088                 this.anchor.focus();
18089                 noscroll.scrollLeft = l;
18090             }catch(e){}
18091         }
18092     },
18093
18094     toggleCheck : function(value){
18095         var cb = this.checkbox;
18096         if(cb){
18097             cb.checked = (value === undefined ? !cb.checked : value);
18098         }
18099     },
18100
18101     blur : function(){
18102         try{
18103             this.anchor.blur();
18104         }catch(e){}
18105     },
18106
18107     animExpand : function(callback){
18108         var ct = Roo.get(this.ctNode);
18109         ct.stopFx();
18110         if(!this.node.hasChildNodes()){
18111             this.updateExpandIcon();
18112             this.ctNode.style.display = "";
18113             Roo.callback(callback);
18114             return;
18115         }
18116         this.animating = true;
18117         this.updateExpandIcon();
18118
18119         ct.slideIn('t', {
18120            callback : function(){
18121                this.animating = false;
18122                Roo.callback(callback);
18123             },
18124             scope: this,
18125             duration: this.node.ownerTree.duration || .25
18126         });
18127     },
18128
18129     highlight : function(){
18130         var tree = this.node.getOwnerTree();
18131         Roo.fly(this.wrap).highlight(
18132             tree.hlColor || "C3DAF9",
18133             {endColor: tree.hlBaseColor}
18134         );
18135     },
18136
18137     collapse : function(){
18138         this.updateExpandIcon();
18139         this.ctNode.style.display = "none";
18140     },
18141
18142     animCollapse : function(callback){
18143         var ct = Roo.get(this.ctNode);
18144         ct.enableDisplayMode('block');
18145         ct.stopFx();
18146
18147         this.animating = true;
18148         this.updateExpandIcon();
18149
18150         ct.slideOut('t', {
18151             callback : function(){
18152                this.animating = false;
18153                Roo.callback(callback);
18154             },
18155             scope: this,
18156             duration: this.node.ownerTree.duration || .25
18157         });
18158     },
18159
18160     getContainer : function(){
18161         return this.ctNode;
18162     },
18163
18164     getEl : function(){
18165         return this.wrap;
18166     },
18167
18168     appendDDGhost : function(ghostNode){
18169         ghostNode.appendChild(this.elNode.cloneNode(true));
18170     },
18171
18172     getDDRepairXY : function(){
18173         return Roo.lib.Dom.getXY(this.iconNode);
18174     },
18175
18176     onRender : function(){
18177         this.render();
18178     },
18179
18180     render : function(bulkRender){
18181         var n = this.node, a = n.attributes;
18182         var targetNode = n.parentNode ?
18183               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
18184
18185         if(!this.rendered){
18186             this.rendered = true;
18187
18188             this.renderElements(n, a, targetNode, bulkRender);
18189
18190             if(a.qtip){
18191                if(this.textNode.setAttributeNS){
18192                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
18193                    if(a.qtipTitle){
18194                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
18195                    }
18196                }else{
18197                    this.textNode.setAttribute("ext:qtip", a.qtip);
18198                    if(a.qtipTitle){
18199                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
18200                    }
18201                }
18202             }else if(a.qtipCfg){
18203                 a.qtipCfg.target = Roo.id(this.textNode);
18204                 Roo.QuickTips.register(a.qtipCfg);
18205             }
18206             this.initEvents();
18207             if(!this.node.expanded){
18208                 this.updateExpandIcon();
18209             }
18210         }else{
18211             if(bulkRender === true) {
18212                 targetNode.appendChild(this.wrap);
18213             }
18214         }
18215     },
18216
18217     renderElements : function(n, a, targetNode, bulkRender)
18218     {
18219         // add some indent caching, this helps performance when rendering a large tree
18220         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18221         var t = n.getOwnerTree();
18222         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18223         if (typeof(n.attributes.html) != 'undefined') {
18224             txt = n.attributes.html;
18225         }
18226         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18227         var cb = typeof a.checked == 'boolean';
18228         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18229         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18230             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18231             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18232             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18233             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18234             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18235              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18236                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18237             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18238             "</li>"];
18239
18240         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18241             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18242                                 n.nextSibling.ui.getEl(), buf.join(""));
18243         }else{
18244             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18245         }
18246
18247         this.elNode = this.wrap.childNodes[0];
18248         this.ctNode = this.wrap.childNodes[1];
18249         var cs = this.elNode.childNodes;
18250         this.indentNode = cs[0];
18251         this.ecNode = cs[1];
18252         this.iconNode = cs[2];
18253         var index = 3;
18254         if(cb){
18255             this.checkbox = cs[3];
18256             index++;
18257         }
18258         this.anchor = cs[index];
18259         this.textNode = cs[index].firstChild;
18260     },
18261
18262     getAnchor : function(){
18263         return this.anchor;
18264     },
18265
18266     getTextEl : function(){
18267         return this.textNode;
18268     },
18269
18270     getIconEl : function(){
18271         return this.iconNode;
18272     },
18273
18274     isChecked : function(){
18275         return this.checkbox ? this.checkbox.checked : false;
18276     },
18277
18278     updateExpandIcon : function(){
18279         if(this.rendered){
18280             var n = this.node, c1, c2;
18281             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18282             var hasChild = n.hasChildNodes();
18283             if(hasChild){
18284                 if(n.expanded){
18285                     cls += "-minus";
18286                     c1 = "x-tree-node-collapsed";
18287                     c2 = "x-tree-node-expanded";
18288                 }else{
18289                     cls += "-plus";
18290                     c1 = "x-tree-node-expanded";
18291                     c2 = "x-tree-node-collapsed";
18292                 }
18293                 if(this.wasLeaf){
18294                     this.removeClass("x-tree-node-leaf");
18295                     this.wasLeaf = false;
18296                 }
18297                 if(this.c1 != c1 || this.c2 != c2){
18298                     Roo.fly(this.elNode).replaceClass(c1, c2);
18299                     this.c1 = c1; this.c2 = c2;
18300                 }
18301             }else{
18302                 // this changes non-leafs into leafs if they have no children.
18303                 // it's not very rational behaviour..
18304                 
18305                 if(!this.wasLeaf && this.node.leaf){
18306                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18307                     delete this.c1;
18308                     delete this.c2;
18309                     this.wasLeaf = true;
18310                 }
18311             }
18312             var ecc = "x-tree-ec-icon "+cls;
18313             if(this.ecc != ecc){
18314                 this.ecNode.className = ecc;
18315                 this.ecc = ecc;
18316             }
18317         }
18318     },
18319
18320     getChildIndent : function(){
18321         if(!this.childIndent){
18322             var buf = [];
18323             var p = this.node;
18324             while(p){
18325                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18326                     if(!p.isLast()) {
18327                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18328                     } else {
18329                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18330                     }
18331                 }
18332                 p = p.parentNode;
18333             }
18334             this.childIndent = buf.join("");
18335         }
18336         return this.childIndent;
18337     },
18338
18339     renderIndent : function(){
18340         if(this.rendered){
18341             var indent = "";
18342             var p = this.node.parentNode;
18343             if(p){
18344                 indent = p.ui.getChildIndent();
18345             }
18346             if(this.indentMarkup != indent){ // don't rerender if not required
18347                 this.indentNode.innerHTML = indent;
18348                 this.indentMarkup = indent;
18349             }
18350             this.updateExpandIcon();
18351         }
18352     }
18353 };
18354
18355 Roo.tree.RootTreeNodeUI = function(){
18356     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18357 };
18358 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18359     render : function(){
18360         if(!this.rendered){
18361             var targetNode = this.node.ownerTree.innerCt.dom;
18362             this.node.expanded = true;
18363             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18364             this.wrap = this.ctNode = targetNode.firstChild;
18365         }
18366     },
18367     collapse : function(){
18368     },
18369     expand : function(){
18370     }
18371 });/*
18372  * Based on:
18373  * Ext JS Library 1.1.1
18374  * Copyright(c) 2006-2007, Ext JS, LLC.
18375  *
18376  * Originally Released Under LGPL - original licence link has changed is not relivant.
18377  *
18378  * Fork - LGPL
18379  * <script type="text/javascript">
18380  */
18381 /**
18382  * @class Roo.tree.TreeLoader
18383  * @extends Roo.util.Observable
18384  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18385  * nodes from a specified URL. The response must be a javascript Array definition
18386  * who's elements are node definition objects. eg:
18387  * <pre><code>
18388 {  success : true,
18389    data :      [
18390    
18391     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
18392     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
18393     ]
18394 }
18395
18396
18397 </code></pre>
18398  * <br><br>
18399  * The old style respose with just an array is still supported, but not recommended.
18400  * <br><br>
18401  *
18402  * A server request is sent, and child nodes are loaded only when a node is expanded.
18403  * The loading node's id is passed to the server under the parameter name "node" to
18404  * enable the server to produce the correct child nodes.
18405  * <br><br>
18406  * To pass extra parameters, an event handler may be attached to the "beforeload"
18407  * event, and the parameters specified in the TreeLoader's baseParams property:
18408  * <pre><code>
18409     myTreeLoader.on("beforeload", function(treeLoader, node) {
18410         this.baseParams.category = node.attributes.category;
18411     }, this);
18412 </code></pre><
18413  * This would pass an HTTP parameter called "category" to the server containing
18414  * the value of the Node's "category" attribute.
18415  * @constructor
18416  * Creates a new Treeloader.
18417  * @param {Object} config A config object containing config properties.
18418  */
18419 Roo.tree.TreeLoader = function(config){
18420     this.baseParams = {};
18421     this.requestMethod = "POST";
18422     Roo.apply(this, config);
18423
18424     this.addEvents({
18425     
18426         /**
18427          * @event beforeload
18428          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18429          * @param {Object} This TreeLoader object.
18430          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18431          * @param {Object} callback The callback function specified in the {@link #load} call.
18432          */
18433         beforeload : true,
18434         /**
18435          * @event load
18436          * Fires when the node has been successfuly loaded.
18437          * @param {Object} This TreeLoader object.
18438          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18439          * @param {Object} response The response object containing the data from the server.
18440          */
18441         load : true,
18442         /**
18443          * @event loadexception
18444          * Fires if the network request failed.
18445          * @param {Object} This TreeLoader object.
18446          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18447          * @param {Object} response The response object containing the data from the server.
18448          */
18449         loadexception : true,
18450         /**
18451          * @event create
18452          * Fires before a node is created, enabling you to return custom Node types 
18453          * @param {Object} This TreeLoader object.
18454          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18455          */
18456         create : true
18457     });
18458
18459     Roo.tree.TreeLoader.superclass.constructor.call(this);
18460 };
18461
18462 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18463     /**
18464     * @cfg {String} dataUrl The URL from which to request a Json string which
18465     * specifies an array of node definition object representing the child nodes
18466     * to be loaded.
18467     */
18468     /**
18469     * @cfg {Object} baseParams (optional) An object containing properties which
18470     * specify HTTP parameters to be passed to each request for child nodes.
18471     */
18472     /**
18473     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18474     * created by this loader. If the attributes sent by the server have an attribute in this object,
18475     * they take priority.
18476     */
18477     /**
18478     * @cfg {Object} uiProviders (optional) An object containing properties which
18479     * 
18480     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
18481     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18482     * <i>uiProvider</i> attribute of a returned child node is a string rather
18483     * than a reference to a TreeNodeUI implementation, this that string value
18484     * is used as a property name in the uiProviders object. You can define the provider named
18485     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18486     */
18487     uiProviders : {},
18488
18489     /**
18490     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18491     * child nodes before loading.
18492     */
18493     clearOnLoad : true,
18494
18495     /**
18496     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18497     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18498     * Grid query { data : [ .....] }
18499     */
18500     
18501     root : false,
18502      /**
18503     * @cfg {String} queryParam (optional) 
18504     * Name of the query as it will be passed on the querystring (defaults to 'node')
18505     * eg. the request will be ?node=[id]
18506     */
18507     
18508     
18509     queryParam: false,
18510     
18511     /**
18512      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18513      * This is called automatically when a node is expanded, but may be used to reload
18514      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18515      * @param {Roo.tree.TreeNode} node
18516      * @param {Function} callback
18517      */
18518     load : function(node, callback){
18519         if(this.clearOnLoad){
18520             while(node.firstChild){
18521                 node.removeChild(node.firstChild);
18522             }
18523         }
18524         if(node.attributes.children){ // preloaded json children
18525             var cs = node.attributes.children;
18526             for(var i = 0, len = cs.length; i < len; i++){
18527                 node.appendChild(this.createNode(cs[i]));
18528             }
18529             if(typeof callback == "function"){
18530                 callback();
18531             }
18532         }else if(this.dataUrl){
18533             this.requestData(node, callback);
18534         }
18535     },
18536
18537     getParams: function(node){
18538         var buf = [], bp = this.baseParams;
18539         for(var key in bp){
18540             if(typeof bp[key] != "function"){
18541                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18542             }
18543         }
18544         var n = this.queryParam === false ? 'node' : this.queryParam;
18545         buf.push(n + "=", encodeURIComponent(node.id));
18546         return buf.join("");
18547     },
18548
18549     requestData : function(node, callback){
18550         if(this.fireEvent("beforeload", this, node, callback) !== false){
18551             this.transId = Roo.Ajax.request({
18552                 method:this.requestMethod,
18553                 url: this.dataUrl||this.url,
18554                 success: this.handleResponse,
18555                 failure: this.handleFailure,
18556                 scope: this,
18557                 argument: {callback: callback, node: node},
18558                 params: this.getParams(node)
18559             });
18560         }else{
18561             // if the load is cancelled, make sure we notify
18562             // the node that we are done
18563             if(typeof callback == "function"){
18564                 callback();
18565             }
18566         }
18567     },
18568
18569     isLoading : function(){
18570         return this.transId ? true : false;
18571     },
18572
18573     abort : function(){
18574         if(this.isLoading()){
18575             Roo.Ajax.abort(this.transId);
18576         }
18577     },
18578
18579     // private
18580     createNode : function(attr)
18581     {
18582         // apply baseAttrs, nice idea Corey!
18583         if(this.baseAttrs){
18584             Roo.applyIf(attr, this.baseAttrs);
18585         }
18586         if(this.applyLoader !== false){
18587             attr.loader = this;
18588         }
18589         // uiProvider = depreciated..
18590         
18591         if(typeof(attr.uiProvider) == 'string'){
18592            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18593                 /**  eval:var:attr */ eval(attr.uiProvider);
18594         }
18595         if(typeof(this.uiProviders['default']) != 'undefined') {
18596             attr.uiProvider = this.uiProviders['default'];
18597         }
18598         
18599         this.fireEvent('create', this, attr);
18600         
18601         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18602         return(attr.leaf ?
18603                         new Roo.tree.TreeNode(attr) :
18604                         new Roo.tree.AsyncTreeNode(attr));
18605     },
18606
18607     processResponse : function(response, node, callback)
18608     {
18609         var json = response.responseText;
18610         try {
18611             
18612             var o = Roo.decode(json);
18613             
18614             if (this.root === false && typeof(o.success) != undefined) {
18615                 this.root = 'data'; // the default behaviour for list like data..
18616                 }
18617                 
18618             if (this.root !== false &&  !o.success) {
18619                 // it's a failure condition.
18620                 var a = response.argument;
18621                 this.fireEvent("loadexception", this, a.node, response);
18622                 Roo.log("Load failed - should have a handler really");
18623                 return;
18624             }
18625             
18626             
18627             
18628             if (this.root !== false) {
18629                  o = o[this.root];
18630             }
18631             
18632             for(var i = 0, len = o.length; i < len; i++){
18633                 var n = this.createNode(o[i]);
18634                 if(n){
18635                     node.appendChild(n);
18636                 }
18637             }
18638             if(typeof callback == "function"){
18639                 callback(this, node);
18640             }
18641         }catch(e){
18642             this.handleFailure(response);
18643         }
18644     },
18645
18646     handleResponse : function(response){
18647         this.transId = false;
18648         var a = response.argument;
18649         this.processResponse(response, a.node, a.callback);
18650         this.fireEvent("load", this, a.node, response);
18651     },
18652
18653     handleFailure : function(response)
18654     {
18655         // should handle failure better..
18656         this.transId = false;
18657         var a = response.argument;
18658         this.fireEvent("loadexception", this, a.node, response);
18659         if(typeof a.callback == "function"){
18660             a.callback(this, a.node);
18661         }
18662     }
18663 });/*
18664  * Based on:
18665  * Ext JS Library 1.1.1
18666  * Copyright(c) 2006-2007, Ext JS, LLC.
18667  *
18668  * Originally Released Under LGPL - original licence link has changed is not relivant.
18669  *
18670  * Fork - LGPL
18671  * <script type="text/javascript">
18672  */
18673
18674 /**
18675 * @class Roo.tree.TreeFilter
18676 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18677 * @param {TreePanel} tree
18678 * @param {Object} config (optional)
18679  */
18680 Roo.tree.TreeFilter = function(tree, config){
18681     this.tree = tree;
18682     this.filtered = {};
18683     Roo.apply(this, config);
18684 };
18685
18686 Roo.tree.TreeFilter.prototype = {
18687     clearBlank:false,
18688     reverse:false,
18689     autoClear:false,
18690     remove:false,
18691
18692      /**
18693      * Filter the data by a specific attribute.
18694      * @param {String/RegExp} value Either string that the attribute value
18695      * should start with or a RegExp to test against the attribute
18696      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18697      * @param {TreeNode} startNode (optional) The node to start the filter at.
18698      */
18699     filter : function(value, attr, startNode){
18700         attr = attr || "text";
18701         var f;
18702         if(typeof value == "string"){
18703             var vlen = value.length;
18704             // auto clear empty filter
18705             if(vlen == 0 && this.clearBlank){
18706                 this.clear();
18707                 return;
18708             }
18709             value = value.toLowerCase();
18710             f = function(n){
18711                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18712             };
18713         }else if(value.exec){ // regex?
18714             f = function(n){
18715                 return value.test(n.attributes[attr]);
18716             };
18717         }else{
18718             throw 'Illegal filter type, must be string or regex';
18719         }
18720         this.filterBy(f, null, startNode);
18721         },
18722
18723     /**
18724      * Filter by a function. The passed function will be called with each
18725      * node in the tree (or from the startNode). If the function returns true, the node is kept
18726      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18727      * @param {Function} fn The filter function
18728      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18729      */
18730     filterBy : function(fn, scope, startNode){
18731         startNode = startNode || this.tree.root;
18732         if(this.autoClear){
18733             this.clear();
18734         }
18735         var af = this.filtered, rv = this.reverse;
18736         var f = function(n){
18737             if(n == startNode){
18738                 return true;
18739             }
18740             if(af[n.id]){
18741                 return false;
18742             }
18743             var m = fn.call(scope || n, n);
18744             if(!m || rv){
18745                 af[n.id] = n;
18746                 n.ui.hide();
18747                 return false;
18748             }
18749             return true;
18750         };
18751         startNode.cascade(f);
18752         if(this.remove){
18753            for(var id in af){
18754                if(typeof id != "function"){
18755                    var n = af[id];
18756                    if(n && n.parentNode){
18757                        n.parentNode.removeChild(n);
18758                    }
18759                }
18760            }
18761         }
18762     },
18763
18764     /**
18765      * Clears the current filter. Note: with the "remove" option
18766      * set a filter cannot be cleared.
18767      */
18768     clear : function(){
18769         var t = this.tree;
18770         var af = this.filtered;
18771         for(var id in af){
18772             if(typeof id != "function"){
18773                 var n = af[id];
18774                 if(n){
18775                     n.ui.show();
18776                 }
18777             }
18778         }
18779         this.filtered = {};
18780     }
18781 };
18782 /*
18783  * Based on:
18784  * Ext JS Library 1.1.1
18785  * Copyright(c) 2006-2007, Ext JS, LLC.
18786  *
18787  * Originally Released Under LGPL - original licence link has changed is not relivant.
18788  *
18789  * Fork - LGPL
18790  * <script type="text/javascript">
18791  */
18792  
18793
18794 /**
18795  * @class Roo.tree.TreeSorter
18796  * Provides sorting of nodes in a TreePanel
18797  * 
18798  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18799  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18800  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18801  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18802  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18803  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18804  * @constructor
18805  * @param {TreePanel} tree
18806  * @param {Object} config
18807  */
18808 Roo.tree.TreeSorter = function(tree, config){
18809     Roo.apply(this, config);
18810     tree.on("beforechildrenrendered", this.doSort, this);
18811     tree.on("append", this.updateSort, this);
18812     tree.on("insert", this.updateSort, this);
18813     
18814     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18815     var p = this.property || "text";
18816     var sortType = this.sortType;
18817     var fs = this.folderSort;
18818     var cs = this.caseSensitive === true;
18819     var leafAttr = this.leafAttr || 'leaf';
18820
18821     this.sortFn = function(n1, n2){
18822         if(fs){
18823             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18824                 return 1;
18825             }
18826             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18827                 return -1;
18828             }
18829         }
18830         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18831         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18832         if(v1 < v2){
18833                         return dsc ? +1 : -1;
18834                 }else if(v1 > v2){
18835                         return dsc ? -1 : +1;
18836         }else{
18837                 return 0;
18838         }
18839     };
18840 };
18841
18842 Roo.tree.TreeSorter.prototype = {
18843     doSort : function(node){
18844         node.sort(this.sortFn);
18845     },
18846     
18847     compareNodes : function(n1, n2){
18848         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18849     },
18850     
18851     updateSort : function(tree, node){
18852         if(node.childrenRendered){
18853             this.doSort.defer(1, this, [node]);
18854         }
18855     }
18856 };/*
18857  * Based on:
18858  * Ext JS Library 1.1.1
18859  * Copyright(c) 2006-2007, Ext JS, LLC.
18860  *
18861  * Originally Released Under LGPL - original licence link has changed is not relivant.
18862  *
18863  * Fork - LGPL
18864  * <script type="text/javascript">
18865  */
18866
18867 if(Roo.dd.DropZone){
18868     
18869 Roo.tree.TreeDropZone = function(tree, config){
18870     this.allowParentInsert = false;
18871     this.allowContainerDrop = false;
18872     this.appendOnly = false;
18873     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18874     this.tree = tree;
18875     this.lastInsertClass = "x-tree-no-status";
18876     this.dragOverData = {};
18877 };
18878
18879 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18880     ddGroup : "TreeDD",
18881     
18882     expandDelay : 1000,
18883     
18884     expandNode : function(node){
18885         if(node.hasChildNodes() && !node.isExpanded()){
18886             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18887         }
18888     },
18889     
18890     queueExpand : function(node){
18891         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18892     },
18893     
18894     cancelExpand : function(){
18895         if(this.expandProcId){
18896             clearTimeout(this.expandProcId);
18897             this.expandProcId = false;
18898         }
18899     },
18900     
18901     isValidDropPoint : function(n, pt, dd, e, data){
18902         if(!n || !data){ return false; }
18903         var targetNode = n.node;
18904         var dropNode = data.node;
18905         // default drop rules
18906         if(!(targetNode && targetNode.isTarget && pt)){
18907             return false;
18908         }
18909         if(pt == "append" && targetNode.allowChildren === false){
18910             return false;
18911         }
18912         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18913             return false;
18914         }
18915         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18916             return false;
18917         }
18918         // reuse the object
18919         var overEvent = this.dragOverData;
18920         overEvent.tree = this.tree;
18921         overEvent.target = targetNode;
18922         overEvent.data = data;
18923         overEvent.point = pt;
18924         overEvent.source = dd;
18925         overEvent.rawEvent = e;
18926         overEvent.dropNode = dropNode;
18927         overEvent.cancel = false;  
18928         var result = this.tree.fireEvent("nodedragover", overEvent);
18929         return overEvent.cancel === false && result !== false;
18930     },
18931     
18932     getDropPoint : function(e, n, dd){
18933         var tn = n.node;
18934         if(tn.isRoot){
18935             return tn.allowChildren !== false ? "append" : false; // always append for root
18936         }
18937         var dragEl = n.ddel;
18938         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18939         var y = Roo.lib.Event.getPageY(e);
18940         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18941         
18942         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18943         var noAppend = tn.allowChildren === false;
18944         if(this.appendOnly || tn.parentNode.allowChildren === false){
18945             return noAppend ? false : "append";
18946         }
18947         var noBelow = false;
18948         if(!this.allowParentInsert){
18949             noBelow = tn.hasChildNodes() && tn.isExpanded();
18950         }
18951         var q = (b - t) / (noAppend ? 2 : 3);
18952         if(y >= t && y < (t + q)){
18953             return "above";
18954         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18955             return "below";
18956         }else{
18957             return "append";
18958         }
18959     },
18960     
18961     onNodeEnter : function(n, dd, e, data){
18962         this.cancelExpand();
18963     },
18964     
18965     onNodeOver : function(n, dd, e, data){
18966         var pt = this.getDropPoint(e, n, dd);
18967         var node = n.node;
18968         
18969         // auto node expand check
18970         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18971             this.queueExpand(node);
18972         }else if(pt != "append"){
18973             this.cancelExpand();
18974         }
18975         
18976         // set the insert point style on the target node
18977         var returnCls = this.dropNotAllowed;
18978         if(this.isValidDropPoint(n, pt, dd, e, data)){
18979            if(pt){
18980                var el = n.ddel;
18981                var cls;
18982                if(pt == "above"){
18983                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18984                    cls = "x-tree-drag-insert-above";
18985                }else if(pt == "below"){
18986                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18987                    cls = "x-tree-drag-insert-below";
18988                }else{
18989                    returnCls = "x-tree-drop-ok-append";
18990                    cls = "x-tree-drag-append";
18991                }
18992                if(this.lastInsertClass != cls){
18993                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18994                    this.lastInsertClass = cls;
18995                }
18996            }
18997        }
18998        return returnCls;
18999     },
19000     
19001     onNodeOut : function(n, dd, e, data){
19002         this.cancelExpand();
19003         this.removeDropIndicators(n);
19004     },
19005     
19006     onNodeDrop : function(n, dd, e, data){
19007         var point = this.getDropPoint(e, n, dd);
19008         var targetNode = n.node;
19009         targetNode.ui.startDrop();
19010         if(!this.isValidDropPoint(n, point, dd, e, data)){
19011             targetNode.ui.endDrop();
19012             return false;
19013         }
19014         // first try to find the drop node
19015         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
19016         var dropEvent = {
19017             tree : this.tree,
19018             target: targetNode,
19019             data: data,
19020             point: point,
19021             source: dd,
19022             rawEvent: e,
19023             dropNode: dropNode,
19024             cancel: !dropNode   
19025         };
19026         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
19027         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
19028             targetNode.ui.endDrop();
19029             return false;
19030         }
19031         // allow target changing
19032         targetNode = dropEvent.target;
19033         if(point == "append" && !targetNode.isExpanded()){
19034             targetNode.expand(false, null, function(){
19035                 this.completeDrop(dropEvent);
19036             }.createDelegate(this));
19037         }else{
19038             this.completeDrop(dropEvent);
19039         }
19040         return true;
19041     },
19042     
19043     completeDrop : function(de){
19044         var ns = de.dropNode, p = de.point, t = de.target;
19045         if(!(ns instanceof Array)){
19046             ns = [ns];
19047         }
19048         var n;
19049         for(var i = 0, len = ns.length; i < len; i++){
19050             n = ns[i];
19051             if(p == "above"){
19052                 t.parentNode.insertBefore(n, t);
19053             }else if(p == "below"){
19054                 t.parentNode.insertBefore(n, t.nextSibling);
19055             }else{
19056                 t.appendChild(n);
19057             }
19058         }
19059         n.ui.focus();
19060         if(this.tree.hlDrop){
19061             n.ui.highlight();
19062         }
19063         t.ui.endDrop();
19064         this.tree.fireEvent("nodedrop", de);
19065     },
19066     
19067     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
19068         if(this.tree.hlDrop){
19069             dropNode.ui.focus();
19070             dropNode.ui.highlight();
19071         }
19072         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
19073     },
19074     
19075     getTree : function(){
19076         return this.tree;
19077     },
19078     
19079     removeDropIndicators : function(n){
19080         if(n && n.ddel){
19081             var el = n.ddel;
19082             Roo.fly(el).removeClass([
19083                     "x-tree-drag-insert-above",
19084                     "x-tree-drag-insert-below",
19085                     "x-tree-drag-append"]);
19086             this.lastInsertClass = "_noclass";
19087         }
19088     },
19089     
19090     beforeDragDrop : function(target, e, id){
19091         this.cancelExpand();
19092         return true;
19093     },
19094     
19095     afterRepair : function(data){
19096         if(data && Roo.enableFx){
19097             data.node.ui.highlight();
19098         }
19099         this.hideProxy();
19100     }    
19101 });
19102
19103 }
19104 /*
19105  * Based on:
19106  * Ext JS Library 1.1.1
19107  * Copyright(c) 2006-2007, Ext JS, LLC.
19108  *
19109  * Originally Released Under LGPL - original licence link has changed is not relivant.
19110  *
19111  * Fork - LGPL
19112  * <script type="text/javascript">
19113  */
19114  
19115
19116 if(Roo.dd.DragZone){
19117 Roo.tree.TreeDragZone = function(tree, config){
19118     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
19119     this.tree = tree;
19120 };
19121
19122 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
19123     ddGroup : "TreeDD",
19124     
19125     onBeforeDrag : function(data, e){
19126         var n = data.node;
19127         return n && n.draggable && !n.disabled;
19128     },
19129     
19130     onInitDrag : function(e){
19131         var data = this.dragData;
19132         this.tree.getSelectionModel().select(data.node);
19133         this.proxy.update("");
19134         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
19135         this.tree.fireEvent("startdrag", this.tree, data.node, e);
19136     },
19137     
19138     getRepairXY : function(e, data){
19139         return data.node.ui.getDDRepairXY();
19140     },
19141     
19142     onEndDrag : function(data, e){
19143         this.tree.fireEvent("enddrag", this.tree, data.node, e);
19144     },
19145     
19146     onValidDrop : function(dd, e, id){
19147         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
19148         this.hideProxy();
19149     },
19150     
19151     beforeInvalidDrop : function(e, id){
19152         // this scrolls the original position back into view
19153         var sm = this.tree.getSelectionModel();
19154         sm.clearSelections();
19155         sm.select(this.dragData.node);
19156     }
19157 });
19158 }/*
19159  * Based on:
19160  * Ext JS Library 1.1.1
19161  * Copyright(c) 2006-2007, Ext JS, LLC.
19162  *
19163  * Originally Released Under LGPL - original licence link has changed is not relivant.
19164  *
19165  * Fork - LGPL
19166  * <script type="text/javascript">
19167  */
19168 /**
19169  * @class Roo.tree.TreeEditor
19170  * @extends Roo.Editor
19171  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
19172  * as the editor field.
19173  * @constructor
19174  * @param {Object} config (used to be the tree panel.)
19175  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
19176  * 
19177  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
19178  * @cfg {Roo.form.TextField|Object} field The field configuration
19179  *
19180  * 
19181  */
19182 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
19183     var tree = config;
19184     var field;
19185     if (oldconfig) { // old style..
19186         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
19187     } else {
19188         // new style..
19189         tree = config.tree;
19190         config.field = config.field  || {};
19191         config.field.xtype = 'TextField';
19192         field = Roo.factory(config.field, Roo.form);
19193     }
19194     config = config || {};
19195     
19196     
19197     this.addEvents({
19198         /**
19199          * @event beforenodeedit
19200          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
19201          * false from the handler of this event.
19202          * @param {Editor} this
19203          * @param {Roo.tree.Node} node 
19204          */
19205         "beforenodeedit" : true
19206     });
19207     
19208     //Roo.log(config);
19209     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
19210
19211     this.tree = tree;
19212
19213     tree.on('beforeclick', this.beforeNodeClick, this);
19214     tree.getTreeEl().on('mousedown', this.hide, this);
19215     this.on('complete', this.updateNode, this);
19216     this.on('beforestartedit', this.fitToTree, this);
19217     this.on('startedit', this.bindScroll, this, {delay:10});
19218     this.on('specialkey', this.onSpecialKey, this);
19219 };
19220
19221 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
19222     /**
19223      * @cfg {String} alignment
19224      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
19225      */
19226     alignment: "l-l",
19227     // inherit
19228     autoSize: false,
19229     /**
19230      * @cfg {Boolean} hideEl
19231      * True to hide the bound element while the editor is displayed (defaults to false)
19232      */
19233     hideEl : false,
19234     /**
19235      * @cfg {String} cls
19236      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
19237      */
19238     cls: "x-small-editor x-tree-editor",
19239     /**
19240      * @cfg {Boolean} shim
19241      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
19242      */
19243     shim:false,
19244     // inherit
19245     shadow:"frame",
19246     /**
19247      * @cfg {Number} maxWidth
19248      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
19249      * the containing tree element's size, it will be automatically limited for you to the container width, taking
19250      * scroll and client offsets into account prior to each edit.
19251      */
19252     maxWidth: 250,
19253
19254     editDelay : 350,
19255
19256     // private
19257     fitToTree : function(ed, el){
19258         var td = this.tree.getTreeEl().dom, nd = el.dom;
19259         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
19260             td.scrollLeft = nd.offsetLeft;
19261         }
19262         var w = Math.min(
19263                 this.maxWidth,
19264                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
19265         this.setSize(w, '');
19266         
19267         return this.fireEvent('beforenodeedit', this, this.editNode);
19268         
19269     },
19270
19271     // private
19272     triggerEdit : function(node){
19273         this.completeEdit();
19274         this.editNode = node;
19275         this.startEdit(node.ui.textNode, node.text);
19276     },
19277
19278     // private
19279     bindScroll : function(){
19280         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19281     },
19282
19283     // private
19284     beforeNodeClick : function(node, e){
19285         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19286         this.lastClick = new Date();
19287         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19288             e.stopEvent();
19289             this.triggerEdit(node);
19290             return false;
19291         }
19292         return true;
19293     },
19294
19295     // private
19296     updateNode : function(ed, value){
19297         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19298         this.editNode.setText(value);
19299     },
19300
19301     // private
19302     onHide : function(){
19303         Roo.tree.TreeEditor.superclass.onHide.call(this);
19304         if(this.editNode){
19305             this.editNode.ui.focus();
19306         }
19307     },
19308
19309     // private
19310     onSpecialKey : function(field, e){
19311         var k = e.getKey();
19312         if(k == e.ESC){
19313             e.stopEvent();
19314             this.cancelEdit();
19315         }else if(k == e.ENTER && !e.hasModifier()){
19316             e.stopEvent();
19317             this.completeEdit();
19318         }
19319     }
19320 });//<Script type="text/javascript">
19321 /*
19322  * Based on:
19323  * Ext JS Library 1.1.1
19324  * Copyright(c) 2006-2007, Ext JS, LLC.
19325  *
19326  * Originally Released Under LGPL - original licence link has changed is not relivant.
19327  *
19328  * Fork - LGPL
19329  * <script type="text/javascript">
19330  */
19331  
19332 /**
19333  * Not documented??? - probably should be...
19334  */
19335
19336 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19337     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19338     
19339     renderElements : function(n, a, targetNode, bulkRender){
19340         //consel.log("renderElements?");
19341         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19342
19343         var t = n.getOwnerTree();
19344         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19345         
19346         var cols = t.columns;
19347         var bw = t.borderWidth;
19348         var c = cols[0];
19349         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19350          var cb = typeof a.checked == "boolean";
19351         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19352         var colcls = 'x-t-' + tid + '-c0';
19353         var buf = [
19354             '<li class="x-tree-node">',
19355             
19356                 
19357                 '<div class="x-tree-node-el ', a.cls,'">',
19358                     // extran...
19359                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19360                 
19361                 
19362                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19363                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19364                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19365                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19366                            (a.iconCls ? ' '+a.iconCls : ''),
19367                            '" unselectable="on" />',
19368                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19369                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19370                              
19371                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19372                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19373                             '<span unselectable="on" qtip="' + tx + '">',
19374                              tx,
19375                              '</span></a>' ,
19376                     '</div>',
19377                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19378                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19379                  ];
19380         for(var i = 1, len = cols.length; i < len; i++){
19381             c = cols[i];
19382             colcls = 'x-t-' + tid + '-c' +i;
19383             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19384             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19385                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19386                       "</div>");
19387          }
19388          
19389          buf.push(
19390             '</a>',
19391             '<div class="x-clear"></div></div>',
19392             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19393             "</li>");
19394         
19395         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19396             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19397                                 n.nextSibling.ui.getEl(), buf.join(""));
19398         }else{
19399             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19400         }
19401         var el = this.wrap.firstChild;
19402         this.elRow = el;
19403         this.elNode = el.firstChild;
19404         this.ranchor = el.childNodes[1];
19405         this.ctNode = this.wrap.childNodes[1];
19406         var cs = el.firstChild.childNodes;
19407         this.indentNode = cs[0];
19408         this.ecNode = cs[1];
19409         this.iconNode = cs[2];
19410         var index = 3;
19411         if(cb){
19412             this.checkbox = cs[3];
19413             index++;
19414         }
19415         this.anchor = cs[index];
19416         
19417         this.textNode = cs[index].firstChild;
19418         
19419         //el.on("click", this.onClick, this);
19420         //el.on("dblclick", this.onDblClick, this);
19421         
19422         
19423        // console.log(this);
19424     },
19425     initEvents : function(){
19426         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19427         
19428             
19429         var a = this.ranchor;
19430
19431         var el = Roo.get(a);
19432
19433         if(Roo.isOpera){ // opera render bug ignores the CSS
19434             el.setStyle("text-decoration", "none");
19435         }
19436
19437         el.on("click", this.onClick, this);
19438         el.on("dblclick", this.onDblClick, this);
19439         el.on("contextmenu", this.onContextMenu, this);
19440         
19441     },
19442     
19443     /*onSelectedChange : function(state){
19444         if(state){
19445             this.focus();
19446             this.addClass("x-tree-selected");
19447         }else{
19448             //this.blur();
19449             this.removeClass("x-tree-selected");
19450         }
19451     },*/
19452     addClass : function(cls){
19453         if(this.elRow){
19454             Roo.fly(this.elRow).addClass(cls);
19455         }
19456         
19457     },
19458     
19459     
19460     removeClass : function(cls){
19461         if(this.elRow){
19462             Roo.fly(this.elRow).removeClass(cls);
19463         }
19464     }
19465
19466     
19467     
19468 });//<Script type="text/javascript">
19469
19470 /*
19471  * Based on:
19472  * Ext JS Library 1.1.1
19473  * Copyright(c) 2006-2007, Ext JS, LLC.
19474  *
19475  * Originally Released Under LGPL - original licence link has changed is not relivant.
19476  *
19477  * Fork - LGPL
19478  * <script type="text/javascript">
19479  */
19480  
19481
19482 /**
19483  * @class Roo.tree.ColumnTree
19484  * @extends Roo.data.TreePanel
19485  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19486  * @cfg {int} borderWidth  compined right/left border allowance
19487  * @constructor
19488  * @param {String/HTMLElement/Element} el The container element
19489  * @param {Object} config
19490  */
19491 Roo.tree.ColumnTree =  function(el, config)
19492 {
19493    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19494    this.addEvents({
19495         /**
19496         * @event resize
19497         * Fire this event on a container when it resizes
19498         * @param {int} w Width
19499         * @param {int} h Height
19500         */
19501        "resize" : true
19502     });
19503     this.on('resize', this.onResize, this);
19504 };
19505
19506 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19507     //lines:false,
19508     
19509     
19510     borderWidth: Roo.isBorderBox ? 0 : 2, 
19511     headEls : false,
19512     
19513     render : function(){
19514         // add the header.....
19515        
19516         Roo.tree.ColumnTree.superclass.render.apply(this);
19517         
19518         this.el.addClass('x-column-tree');
19519         
19520         this.headers = this.el.createChild(
19521             {cls:'x-tree-headers'},this.innerCt.dom);
19522    
19523         var cols = this.columns, c;
19524         var totalWidth = 0;
19525         this.headEls = [];
19526         var  len = cols.length;
19527         for(var i = 0; i < len; i++){
19528              c = cols[i];
19529              totalWidth += c.width;
19530             this.headEls.push(this.headers.createChild({
19531                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19532                  cn: {
19533                      cls:'x-tree-hd-text',
19534                      html: c.header
19535                  },
19536                  style:'width:'+(c.width-this.borderWidth)+'px;'
19537              }));
19538         }
19539         this.headers.createChild({cls:'x-clear'});
19540         // prevent floats from wrapping when clipped
19541         this.headers.setWidth(totalWidth);
19542         //this.innerCt.setWidth(totalWidth);
19543         this.innerCt.setStyle({ overflow: 'auto' });
19544         this.onResize(this.width, this.height);
19545              
19546         
19547     },
19548     onResize : function(w,h)
19549     {
19550         this.height = h;
19551         this.width = w;
19552         // resize cols..
19553         this.innerCt.setWidth(this.width);
19554         this.innerCt.setHeight(this.height-20);
19555         
19556         // headers...
19557         var cols = this.columns, c;
19558         var totalWidth = 0;
19559         var expEl = false;
19560         var len = cols.length;
19561         for(var i = 0; i < len; i++){
19562             c = cols[i];
19563             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19564                 // it's the expander..
19565                 expEl  = this.headEls[i];
19566                 continue;
19567             }
19568             totalWidth += c.width;
19569             
19570         }
19571         if (expEl) {
19572             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19573         }
19574         this.headers.setWidth(w-20);
19575
19576         
19577         
19578         
19579     }
19580 });
19581 /*
19582  * Based on:
19583  * Ext JS Library 1.1.1
19584  * Copyright(c) 2006-2007, Ext JS, LLC.
19585  *
19586  * Originally Released Under LGPL - original licence link has changed is not relivant.
19587  *
19588  * Fork - LGPL
19589  * <script type="text/javascript">
19590  */
19591  
19592 /**
19593  * @class Roo.menu.Menu
19594  * @extends Roo.util.Observable
19595  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19596  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19597  * @constructor
19598  * Creates a new Menu
19599  * @param {Object} config Configuration options
19600  */
19601 Roo.menu.Menu = function(config){
19602     Roo.apply(this, config);
19603     this.id = this.id || Roo.id();
19604     this.addEvents({
19605         /**
19606          * @event beforeshow
19607          * Fires before this menu is displayed
19608          * @param {Roo.menu.Menu} this
19609          */
19610         beforeshow : true,
19611         /**
19612          * @event beforehide
19613          * Fires before this menu is hidden
19614          * @param {Roo.menu.Menu} this
19615          */
19616         beforehide : true,
19617         /**
19618          * @event show
19619          * Fires after this menu is displayed
19620          * @param {Roo.menu.Menu} this
19621          */
19622         show : true,
19623         /**
19624          * @event hide
19625          * Fires after this menu is hidden
19626          * @param {Roo.menu.Menu} this
19627          */
19628         hide : true,
19629         /**
19630          * @event click
19631          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19632          * @param {Roo.menu.Menu} this
19633          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19634          * @param {Roo.EventObject} e
19635          */
19636         click : true,
19637         /**
19638          * @event mouseover
19639          * Fires when the mouse is hovering over this menu
19640          * @param {Roo.menu.Menu} this
19641          * @param {Roo.EventObject} e
19642          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19643          */
19644         mouseover : true,
19645         /**
19646          * @event mouseout
19647          * Fires when the mouse exits this menu
19648          * @param {Roo.menu.Menu} this
19649          * @param {Roo.EventObject} e
19650          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19651          */
19652         mouseout : true,
19653         /**
19654          * @event itemclick
19655          * Fires when a menu item contained in this menu is clicked
19656          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19657          * @param {Roo.EventObject} e
19658          */
19659         itemclick: true
19660     });
19661     if (this.registerMenu) {
19662         Roo.menu.MenuMgr.register(this);
19663     }
19664     
19665     var mis = this.items;
19666     this.items = new Roo.util.MixedCollection();
19667     if(mis){
19668         this.add.apply(this, mis);
19669     }
19670 };
19671
19672 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19673     /**
19674      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19675      */
19676     minWidth : 120,
19677     /**
19678      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19679      * for bottom-right shadow (defaults to "sides")
19680      */
19681     shadow : "sides",
19682     /**
19683      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19684      * this menu (defaults to "tl-tr?")
19685      */
19686     subMenuAlign : "tl-tr?",
19687     /**
19688      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19689      * relative to its element of origin (defaults to "tl-bl?")
19690      */
19691     defaultAlign : "tl-bl?",
19692     /**
19693      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19694      */
19695     allowOtherMenus : false,
19696     /**
19697      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19698      */
19699     registerMenu : true,
19700
19701     hidden:true,
19702
19703     // private
19704     render : function(){
19705         if(this.el){
19706             return;
19707         }
19708         var el = this.el = new Roo.Layer({
19709             cls: "x-menu",
19710             shadow:this.shadow,
19711             constrain: false,
19712             parentEl: this.parentEl || document.body,
19713             zindex:15000
19714         });
19715
19716         this.keyNav = new Roo.menu.MenuNav(this);
19717
19718         if(this.plain){
19719             el.addClass("x-menu-plain");
19720         }
19721         if(this.cls){
19722             el.addClass(this.cls);
19723         }
19724         // generic focus element
19725         this.focusEl = el.createChild({
19726             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19727         });
19728         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19729         ul.on("click", this.onClick, this);
19730         ul.on("mouseover", this.onMouseOver, this);
19731         ul.on("mouseout", this.onMouseOut, this);
19732         this.items.each(function(item){
19733             var li = document.createElement("li");
19734             li.className = "x-menu-list-item";
19735             ul.dom.appendChild(li);
19736             item.render(li, this);
19737         }, this);
19738         this.ul = ul;
19739         this.autoWidth();
19740     },
19741
19742     // private
19743     autoWidth : function(){
19744         var el = this.el, ul = this.ul;
19745         if(!el){
19746             return;
19747         }
19748         var w = this.width;
19749         if(w){
19750             el.setWidth(w);
19751         }else if(Roo.isIE){
19752             el.setWidth(this.minWidth);
19753             var t = el.dom.offsetWidth; // force recalc
19754             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19755         }
19756     },
19757
19758     // private
19759     delayAutoWidth : function(){
19760         if(this.rendered){
19761             if(!this.awTask){
19762                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19763             }
19764             this.awTask.delay(20);
19765         }
19766     },
19767
19768     // private
19769     findTargetItem : function(e){
19770         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19771         if(t && t.menuItemId){
19772             return this.items.get(t.menuItemId);
19773         }
19774     },
19775
19776     // private
19777     onClick : function(e){
19778         var t;
19779         if(t = this.findTargetItem(e)){
19780             t.onClick(e);
19781             this.fireEvent("click", this, t, e);
19782         }
19783     },
19784
19785     // private
19786     setActiveItem : function(item, autoExpand){
19787         if(item != this.activeItem){
19788             if(this.activeItem){
19789                 this.activeItem.deactivate();
19790             }
19791             this.activeItem = item;
19792             item.activate(autoExpand);
19793         }else if(autoExpand){
19794             item.expandMenu();
19795         }
19796     },
19797
19798     // private
19799     tryActivate : function(start, step){
19800         var items = this.items;
19801         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19802             var item = items.get(i);
19803             if(!item.disabled && item.canActivate){
19804                 this.setActiveItem(item, false);
19805                 return item;
19806             }
19807         }
19808         return false;
19809     },
19810
19811     // private
19812     onMouseOver : function(e){
19813         var t;
19814         if(t = this.findTargetItem(e)){
19815             if(t.canActivate && !t.disabled){
19816                 this.setActiveItem(t, true);
19817             }
19818         }
19819         this.fireEvent("mouseover", this, e, t);
19820     },
19821
19822     // private
19823     onMouseOut : function(e){
19824         var t;
19825         if(t = this.findTargetItem(e)){
19826             if(t == this.activeItem && t.shouldDeactivate(e)){
19827                 this.activeItem.deactivate();
19828                 delete this.activeItem;
19829             }
19830         }
19831         this.fireEvent("mouseout", this, e, t);
19832     },
19833
19834     /**
19835      * Read-only.  Returns true if the menu is currently displayed, else false.
19836      * @type Boolean
19837      */
19838     isVisible : function(){
19839         return this.el && !this.hidden;
19840     },
19841
19842     /**
19843      * Displays this menu relative to another element
19844      * @param {String/HTMLElement/Roo.Element} element The element to align to
19845      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19846      * the element (defaults to this.defaultAlign)
19847      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19848      */
19849     show : function(el, pos, parentMenu){
19850         this.parentMenu = parentMenu;
19851         if(!this.el){
19852             this.render();
19853         }
19854         this.fireEvent("beforeshow", this);
19855         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19856     },
19857
19858     /**
19859      * Displays this menu at a specific xy position
19860      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19861      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19862      */
19863     showAt : function(xy, parentMenu, /* private: */_e){
19864         this.parentMenu = parentMenu;
19865         if(!this.el){
19866             this.render();
19867         }
19868         if(_e !== false){
19869             this.fireEvent("beforeshow", this);
19870             xy = this.el.adjustForConstraints(xy);
19871         }
19872         this.el.setXY(xy);
19873         this.el.show();
19874         this.hidden = false;
19875         this.focus();
19876         this.fireEvent("show", this);
19877     },
19878
19879     focus : function(){
19880         if(!this.hidden){
19881             this.doFocus.defer(50, this);
19882         }
19883     },
19884
19885     doFocus : function(){
19886         if(!this.hidden){
19887             this.focusEl.focus();
19888         }
19889     },
19890
19891     /**
19892      * Hides this menu and optionally all parent menus
19893      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19894      */
19895     hide : function(deep){
19896         if(this.el && this.isVisible()){
19897             this.fireEvent("beforehide", this);
19898             if(this.activeItem){
19899                 this.activeItem.deactivate();
19900                 this.activeItem = null;
19901             }
19902             this.el.hide();
19903             this.hidden = true;
19904             this.fireEvent("hide", this);
19905         }
19906         if(deep === true && this.parentMenu){
19907             this.parentMenu.hide(true);
19908         }
19909     },
19910
19911     /**
19912      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19913      * Any of the following are valid:
19914      * <ul>
19915      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19916      * <li>An HTMLElement object which will be converted to a menu item</li>
19917      * <li>A menu item config object that will be created as a new menu item</li>
19918      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19919      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19920      * </ul>
19921      * Usage:
19922      * <pre><code>
19923 // Create the menu
19924 var menu = new Roo.menu.Menu();
19925
19926 // Create a menu item to add by reference
19927 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19928
19929 // Add a bunch of items at once using different methods.
19930 // Only the last item added will be returned.
19931 var item = menu.add(
19932     menuItem,                // add existing item by ref
19933     'Dynamic Item',          // new TextItem
19934     '-',                     // new separator
19935     { text: 'Config Item' }  // new item by config
19936 );
19937 </code></pre>
19938      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19939      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19940      */
19941     add : function(){
19942         var a = arguments, l = a.length, item;
19943         for(var i = 0; i < l; i++){
19944             var el = a[i];
19945             if ((typeof(el) == "object") && el.xtype && el.xns) {
19946                 el = Roo.factory(el, Roo.menu);
19947             }
19948             
19949             if(el.render){ // some kind of Item
19950                 item = this.addItem(el);
19951             }else if(typeof el == "string"){ // string
19952                 if(el == "separator" || el == "-"){
19953                     item = this.addSeparator();
19954                 }else{
19955                     item = this.addText(el);
19956                 }
19957             }else if(el.tagName || el.el){ // element
19958                 item = this.addElement(el);
19959             }else if(typeof el == "object"){ // must be menu item config?
19960                 item = this.addMenuItem(el);
19961             }
19962         }
19963         return item;
19964     },
19965
19966     /**
19967      * Returns this menu's underlying {@link Roo.Element} object
19968      * @return {Roo.Element} The element
19969      */
19970     getEl : function(){
19971         if(!this.el){
19972             this.render();
19973         }
19974         return this.el;
19975     },
19976
19977     /**
19978      * Adds a separator bar to the menu
19979      * @return {Roo.menu.Item} The menu item that was added
19980      */
19981     addSeparator : function(){
19982         return this.addItem(new Roo.menu.Separator());
19983     },
19984
19985     /**
19986      * Adds an {@link Roo.Element} object to the menu
19987      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19988      * @return {Roo.menu.Item} The menu item that was added
19989      */
19990     addElement : function(el){
19991         return this.addItem(new Roo.menu.BaseItem(el));
19992     },
19993
19994     /**
19995      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19996      * @param {Roo.menu.Item} item The menu item to add
19997      * @return {Roo.menu.Item} The menu item that was added
19998      */
19999     addItem : function(item){
20000         this.items.add(item);
20001         if(this.ul){
20002             var li = document.createElement("li");
20003             li.className = "x-menu-list-item";
20004             this.ul.dom.appendChild(li);
20005             item.render(li, this);
20006             this.delayAutoWidth();
20007         }
20008         return item;
20009     },
20010
20011     /**
20012      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
20013      * @param {Object} config A MenuItem config object
20014      * @return {Roo.menu.Item} The menu item that was added
20015      */
20016     addMenuItem : function(config){
20017         if(!(config instanceof Roo.menu.Item)){
20018             if(typeof config.checked == "boolean"){ // must be check menu item config?
20019                 config = new Roo.menu.CheckItem(config);
20020             }else{
20021                 config = new Roo.menu.Item(config);
20022             }
20023         }
20024         return this.addItem(config);
20025     },
20026
20027     /**
20028      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
20029      * @param {String} text The text to display in the menu item
20030      * @return {Roo.menu.Item} The menu item that was added
20031      */
20032     addText : function(text){
20033         return this.addItem(new Roo.menu.TextItem({ text : text }));
20034     },
20035
20036     /**
20037      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
20038      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
20039      * @param {Roo.menu.Item} item The menu item to add
20040      * @return {Roo.menu.Item} The menu item that was added
20041      */
20042     insert : function(index, item){
20043         this.items.insert(index, item);
20044         if(this.ul){
20045             var li = document.createElement("li");
20046             li.className = "x-menu-list-item";
20047             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
20048             item.render(li, this);
20049             this.delayAutoWidth();
20050         }
20051         return item;
20052     },
20053
20054     /**
20055      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
20056      * @param {Roo.menu.Item} item The menu item to remove
20057      */
20058     remove : function(item){
20059         this.items.removeKey(item.id);
20060         item.destroy();
20061     },
20062
20063     /**
20064      * Removes and destroys all items in the menu
20065      */
20066     removeAll : function(){
20067         var f;
20068         while(f = this.items.first()){
20069             this.remove(f);
20070         }
20071     }
20072 });
20073
20074 // MenuNav is a private utility class used internally by the Menu
20075 Roo.menu.MenuNav = function(menu){
20076     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
20077     this.scope = this.menu = menu;
20078 };
20079
20080 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
20081     doRelay : function(e, h){
20082         var k = e.getKey();
20083         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
20084             this.menu.tryActivate(0, 1);
20085             return false;
20086         }
20087         return h.call(this.scope || this, e, this.menu);
20088     },
20089
20090     up : function(e, m){
20091         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
20092             m.tryActivate(m.items.length-1, -1);
20093         }
20094     },
20095
20096     down : function(e, m){
20097         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
20098             m.tryActivate(0, 1);
20099         }
20100     },
20101
20102     right : function(e, m){
20103         if(m.activeItem){
20104             m.activeItem.expandMenu(true);
20105         }
20106     },
20107
20108     left : function(e, m){
20109         m.hide();
20110         if(m.parentMenu && m.parentMenu.activeItem){
20111             m.parentMenu.activeItem.activate();
20112         }
20113     },
20114
20115     enter : function(e, m){
20116         if(m.activeItem){
20117             e.stopPropagation();
20118             m.activeItem.onClick(e);
20119             m.fireEvent("click", this, m.activeItem);
20120             return true;
20121         }
20122     }
20123 });/*
20124  * Based on:
20125  * Ext JS Library 1.1.1
20126  * Copyright(c) 2006-2007, Ext JS, LLC.
20127  *
20128  * Originally Released Under LGPL - original licence link has changed is not relivant.
20129  *
20130  * Fork - LGPL
20131  * <script type="text/javascript">
20132  */
20133  
20134 /**
20135  * @class Roo.menu.MenuMgr
20136  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
20137  * @singleton
20138  */
20139 Roo.menu.MenuMgr = function(){
20140    var menus, active, groups = {}, attached = false, lastShow = new Date();
20141
20142    // private - called when first menu is created
20143    function init(){
20144        menus = {};
20145        active = new Roo.util.MixedCollection();
20146        Roo.get(document).addKeyListener(27, function(){
20147            if(active.length > 0){
20148                hideAll();
20149            }
20150        });
20151    }
20152
20153    // private
20154    function hideAll(){
20155        if(active && active.length > 0){
20156            var c = active.clone();
20157            c.each(function(m){
20158                m.hide();
20159            });
20160        }
20161    }
20162
20163    // private
20164    function onHide(m){
20165        active.remove(m);
20166        if(active.length < 1){
20167            Roo.get(document).un("mousedown", onMouseDown);
20168            attached = false;
20169        }
20170    }
20171
20172    // private
20173    function onShow(m){
20174        var last = active.last();
20175        lastShow = new Date();
20176        active.add(m);
20177        if(!attached){
20178            Roo.get(document).on("mousedown", onMouseDown);
20179            attached = true;
20180        }
20181        if(m.parentMenu){
20182           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
20183           m.parentMenu.activeChild = m;
20184        }else if(last && last.isVisible()){
20185           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
20186        }
20187    }
20188
20189    // private
20190    function onBeforeHide(m){
20191        if(m.activeChild){
20192            m.activeChild.hide();
20193        }
20194        if(m.autoHideTimer){
20195            clearTimeout(m.autoHideTimer);
20196            delete m.autoHideTimer;
20197        }
20198    }
20199
20200    // private
20201    function onBeforeShow(m){
20202        var pm = m.parentMenu;
20203        if(!pm && !m.allowOtherMenus){
20204            hideAll();
20205        }else if(pm && pm.activeChild && active != m){
20206            pm.activeChild.hide();
20207        }
20208    }
20209
20210    // private
20211    function onMouseDown(e){
20212        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
20213            hideAll();
20214        }
20215    }
20216
20217    // private
20218    function onBeforeCheck(mi, state){
20219        if(state){
20220            var g = groups[mi.group];
20221            for(var i = 0, l = g.length; i < l; i++){
20222                if(g[i] != mi){
20223                    g[i].setChecked(false);
20224                }
20225            }
20226        }
20227    }
20228
20229    return {
20230
20231        /**
20232         * Hides all menus that are currently visible
20233         */
20234        hideAll : function(){
20235             hideAll();  
20236        },
20237
20238        // private
20239        register : function(menu){
20240            if(!menus){
20241                init();
20242            }
20243            menus[menu.id] = menu;
20244            menu.on("beforehide", onBeforeHide);
20245            menu.on("hide", onHide);
20246            menu.on("beforeshow", onBeforeShow);
20247            menu.on("show", onShow);
20248            var g = menu.group;
20249            if(g && menu.events["checkchange"]){
20250                if(!groups[g]){
20251                    groups[g] = [];
20252                }
20253                groups[g].push(menu);
20254                menu.on("checkchange", onCheck);
20255            }
20256        },
20257
20258         /**
20259          * Returns a {@link Roo.menu.Menu} object
20260          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
20261          * be used to generate and return a new Menu instance.
20262          */
20263        get : function(menu){
20264            if(typeof menu == "string"){ // menu id
20265                return menus[menu];
20266            }else if(menu.events){  // menu instance
20267                return menu;
20268            }else if(typeof menu.length == 'number'){ // array of menu items?
20269                return new Roo.menu.Menu({items:menu});
20270            }else{ // otherwise, must be a config
20271                return new Roo.menu.Menu(menu);
20272            }
20273        },
20274
20275        // private
20276        unregister : function(menu){
20277            delete menus[menu.id];
20278            menu.un("beforehide", onBeforeHide);
20279            menu.un("hide", onHide);
20280            menu.un("beforeshow", onBeforeShow);
20281            menu.un("show", onShow);
20282            var g = menu.group;
20283            if(g && menu.events["checkchange"]){
20284                groups[g].remove(menu);
20285                menu.un("checkchange", onCheck);
20286            }
20287        },
20288
20289        // private
20290        registerCheckable : function(menuItem){
20291            var g = menuItem.group;
20292            if(g){
20293                if(!groups[g]){
20294                    groups[g] = [];
20295                }
20296                groups[g].push(menuItem);
20297                menuItem.on("beforecheckchange", onBeforeCheck);
20298            }
20299        },
20300
20301        // private
20302        unregisterCheckable : function(menuItem){
20303            var g = menuItem.group;
20304            if(g){
20305                groups[g].remove(menuItem);
20306                menuItem.un("beforecheckchange", onBeforeCheck);
20307            }
20308        }
20309    };
20310 }();/*
20311  * Based on:
20312  * Ext JS Library 1.1.1
20313  * Copyright(c) 2006-2007, Ext JS, LLC.
20314  *
20315  * Originally Released Under LGPL - original licence link has changed is not relivant.
20316  *
20317  * Fork - LGPL
20318  * <script type="text/javascript">
20319  */
20320  
20321
20322 /**
20323  * @class Roo.menu.BaseItem
20324  * @extends Roo.Component
20325  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20326  * management and base configuration options shared by all menu components.
20327  * @constructor
20328  * Creates a new BaseItem
20329  * @param {Object} config Configuration options
20330  */
20331 Roo.menu.BaseItem = function(config){
20332     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20333
20334     this.addEvents({
20335         /**
20336          * @event click
20337          * Fires when this item is clicked
20338          * @param {Roo.menu.BaseItem} this
20339          * @param {Roo.EventObject} e
20340          */
20341         click: true,
20342         /**
20343          * @event activate
20344          * Fires when this item is activated
20345          * @param {Roo.menu.BaseItem} this
20346          */
20347         activate : true,
20348         /**
20349          * @event deactivate
20350          * Fires when this item is deactivated
20351          * @param {Roo.menu.BaseItem} this
20352          */
20353         deactivate : true
20354     });
20355
20356     if(this.handler){
20357         this.on("click", this.handler, this.scope, true);
20358     }
20359 };
20360
20361 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20362     /**
20363      * @cfg {Function} handler
20364      * A function that will handle the click event of this menu item (defaults to undefined)
20365      */
20366     /**
20367      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20368      */
20369     canActivate : false,
20370     /**
20371      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20372      */
20373     activeClass : "x-menu-item-active",
20374     /**
20375      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20376      */
20377     hideOnClick : true,
20378     /**
20379      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20380      */
20381     hideDelay : 100,
20382
20383     // private
20384     ctype: "Roo.menu.BaseItem",
20385
20386     // private
20387     actionMode : "container",
20388
20389     // private
20390     render : function(container, parentMenu){
20391         this.parentMenu = parentMenu;
20392         Roo.menu.BaseItem.superclass.render.call(this, container);
20393         this.container.menuItemId = this.id;
20394     },
20395
20396     // private
20397     onRender : function(container, position){
20398         this.el = Roo.get(this.el);
20399         container.dom.appendChild(this.el.dom);
20400     },
20401
20402     // private
20403     onClick : function(e){
20404         if(!this.disabled && this.fireEvent("click", this, e) !== false
20405                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20406             this.handleClick(e);
20407         }else{
20408             e.stopEvent();
20409         }
20410     },
20411
20412     // private
20413     activate : function(){
20414         if(this.disabled){
20415             return false;
20416         }
20417         var li = this.container;
20418         li.addClass(this.activeClass);
20419         this.region = li.getRegion().adjust(2, 2, -2, -2);
20420         this.fireEvent("activate", this);
20421         return true;
20422     },
20423
20424     // private
20425     deactivate : function(){
20426         this.container.removeClass(this.activeClass);
20427         this.fireEvent("deactivate", this);
20428     },
20429
20430     // private
20431     shouldDeactivate : function(e){
20432         return !this.region || !this.region.contains(e.getPoint());
20433     },
20434
20435     // private
20436     handleClick : function(e){
20437         if(this.hideOnClick){
20438             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20439         }
20440     },
20441
20442     // private
20443     expandMenu : function(autoActivate){
20444         // do nothing
20445     },
20446
20447     // private
20448     hideMenu : function(){
20449         // do nothing
20450     }
20451 });/*
20452  * Based on:
20453  * Ext JS Library 1.1.1
20454  * Copyright(c) 2006-2007, Ext JS, LLC.
20455  *
20456  * Originally Released Under LGPL - original licence link has changed is not relivant.
20457  *
20458  * Fork - LGPL
20459  * <script type="text/javascript">
20460  */
20461  
20462 /**
20463  * @class Roo.menu.Adapter
20464  * @extends Roo.menu.BaseItem
20465  * A base utility class that adapts a non-menu component so that it can be wrapped by a menu item and added to a menu.
20466  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20467  * @constructor
20468  * Creates a new Adapter
20469  * @param {Object} config Configuration options
20470  */
20471 Roo.menu.Adapter = function(component, config){
20472     Roo.menu.Adapter.superclass.constructor.call(this, config);
20473     this.component = component;
20474 };
20475 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20476     // private
20477     canActivate : true,
20478
20479     // private
20480     onRender : function(container, position){
20481         this.component.render(container);
20482         this.el = this.component.getEl();
20483     },
20484
20485     // private
20486     activate : function(){
20487         if(this.disabled){
20488             return false;
20489         }
20490         this.component.focus();
20491         this.fireEvent("activate", this);
20492         return true;
20493     },
20494
20495     // private
20496     deactivate : function(){
20497         this.fireEvent("deactivate", this);
20498     },
20499
20500     // private
20501     disable : function(){
20502         this.component.disable();
20503         Roo.menu.Adapter.superclass.disable.call(this);
20504     },
20505
20506     // private
20507     enable : function(){
20508         this.component.enable();
20509         Roo.menu.Adapter.superclass.enable.call(this);
20510     }
20511 });/*
20512  * Based on:
20513  * Ext JS Library 1.1.1
20514  * Copyright(c) 2006-2007, Ext JS, LLC.
20515  *
20516  * Originally Released Under LGPL - original licence link has changed is not relivant.
20517  *
20518  * Fork - LGPL
20519  * <script type="text/javascript">
20520  */
20521
20522 /**
20523  * @class Roo.menu.TextItem
20524  * @extends Roo.menu.BaseItem
20525  * Adds a static text string to a menu, usually used as either a heading or group separator.
20526  * Note: old style constructor with text is still supported.
20527  * 
20528  * @constructor
20529  * Creates a new TextItem
20530  * @param {Object} cfg Configuration
20531  */
20532 Roo.menu.TextItem = function(cfg){
20533     if (typeof(cfg) == 'string') {
20534         this.text = cfg;
20535     } else {
20536         Roo.apply(this,cfg);
20537     }
20538     
20539     Roo.menu.TextItem.superclass.constructor.call(this);
20540 };
20541
20542 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20543     /**
20544      * @cfg {Boolean} text Text to show on item.
20545      */
20546     text : '',
20547     
20548     /**
20549      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20550      */
20551     hideOnClick : false,
20552     /**
20553      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20554      */
20555     itemCls : "x-menu-text",
20556
20557     // private
20558     onRender : function(){
20559         var s = document.createElement("span");
20560         s.className = this.itemCls;
20561         s.innerHTML = this.text;
20562         this.el = s;
20563         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20564     }
20565 });/*
20566  * Based on:
20567  * Ext JS Library 1.1.1
20568  * Copyright(c) 2006-2007, Ext JS, LLC.
20569  *
20570  * Originally Released Under LGPL - original licence link has changed is not relivant.
20571  *
20572  * Fork - LGPL
20573  * <script type="text/javascript">
20574  */
20575
20576 /**
20577  * @class Roo.menu.Separator
20578  * @extends Roo.menu.BaseItem
20579  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20580  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20581  * @constructor
20582  * @param {Object} config Configuration options
20583  */
20584 Roo.menu.Separator = function(config){
20585     Roo.menu.Separator.superclass.constructor.call(this, config);
20586 };
20587
20588 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20589     /**
20590      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20591      */
20592     itemCls : "x-menu-sep",
20593     /**
20594      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20595      */
20596     hideOnClick : false,
20597
20598     // private
20599     onRender : function(li){
20600         var s = document.createElement("span");
20601         s.className = this.itemCls;
20602         s.innerHTML = "&#160;";
20603         this.el = s;
20604         li.addClass("x-menu-sep-li");
20605         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20606     }
20607 });/*
20608  * Based on:
20609  * Ext JS Library 1.1.1
20610  * Copyright(c) 2006-2007, Ext JS, LLC.
20611  *
20612  * Originally Released Under LGPL - original licence link has changed is not relivant.
20613  *
20614  * Fork - LGPL
20615  * <script type="text/javascript">
20616  */
20617 /**
20618  * @class Roo.menu.Item
20619  * @extends Roo.menu.BaseItem
20620  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20621  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20622  * activation and click handling.
20623  * @constructor
20624  * Creates a new Item
20625  * @param {Object} config Configuration options
20626  */
20627 Roo.menu.Item = function(config){
20628     Roo.menu.Item.superclass.constructor.call(this, config);
20629     if(this.menu){
20630         this.menu = Roo.menu.MenuMgr.get(this.menu);
20631     }
20632 };
20633 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20634     
20635     /**
20636      * @cfg {String} text
20637      * The text to show on the menu item.
20638      */
20639     text: '',
20640      /**
20641      * @cfg {String} HTML to render in menu
20642      * The text to show on the menu item (HTML version).
20643      */
20644     html: '',
20645     /**
20646      * @cfg {String} icon
20647      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20648      */
20649     icon: undefined,
20650     /**
20651      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20652      */
20653     itemCls : "x-menu-item",
20654     /**
20655      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20656      */
20657     canActivate : true,
20658     /**
20659      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20660      */
20661     showDelay: 200,
20662     // doc'd in BaseItem
20663     hideDelay: 200,
20664
20665     // private
20666     ctype: "Roo.menu.Item",
20667     
20668     // private
20669     onRender : function(container, position){
20670         var el = document.createElement("a");
20671         el.hideFocus = true;
20672         el.unselectable = "on";
20673         el.href = this.href || "#";
20674         if(this.hrefTarget){
20675             el.target = this.hrefTarget;
20676         }
20677         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20678         
20679         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20680         
20681         el.innerHTML = String.format(
20682                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20683                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20684         this.el = el;
20685         Roo.menu.Item.superclass.onRender.call(this, container, position);
20686     },
20687
20688     /**
20689      * Sets the text to display in this menu item
20690      * @param {String} text The text to display
20691      * @param {Boolean} isHTML true to indicate text is pure html.
20692      */
20693     setText : function(text, isHTML){
20694         if (isHTML) {
20695             this.html = text;
20696         } else {
20697             this.text = text;
20698             this.html = '';
20699         }
20700         if(this.rendered){
20701             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20702      
20703             this.el.update(String.format(
20704                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20705                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20706             this.parentMenu.autoWidth();
20707         }
20708     },
20709
20710     // private
20711     handleClick : function(e){
20712         if(!this.href){ // if no link defined, stop the event automatically
20713             e.stopEvent();
20714         }
20715         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20716     },
20717
20718     // private
20719     activate : function(autoExpand){
20720         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20721             this.focus();
20722             if(autoExpand){
20723                 this.expandMenu();
20724             }
20725         }
20726         return true;
20727     },
20728
20729     // private
20730     shouldDeactivate : function(e){
20731         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20732             if(this.menu && this.menu.isVisible()){
20733                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20734             }
20735             return true;
20736         }
20737         return false;
20738     },
20739
20740     // private
20741     deactivate : function(){
20742         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20743         this.hideMenu();
20744     },
20745
20746     // private
20747     expandMenu : function(autoActivate){
20748         if(!this.disabled && this.menu){
20749             clearTimeout(this.hideTimer);
20750             delete this.hideTimer;
20751             if(!this.menu.isVisible() && !this.showTimer){
20752                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20753             }else if (this.menu.isVisible() && autoActivate){
20754                 this.menu.tryActivate(0, 1);
20755             }
20756         }
20757     },
20758
20759     // private
20760     deferExpand : function(autoActivate){
20761         delete this.showTimer;
20762         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20763         if(autoActivate){
20764             this.menu.tryActivate(0, 1);
20765         }
20766     },
20767
20768     // private
20769     hideMenu : function(){
20770         clearTimeout(this.showTimer);
20771         delete this.showTimer;
20772         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20773             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20774         }
20775     },
20776
20777     // private
20778     deferHide : function(){
20779         delete this.hideTimer;
20780         this.menu.hide();
20781     }
20782 });/*
20783  * Based on:
20784  * Ext JS Library 1.1.1
20785  * Copyright(c) 2006-2007, Ext JS, LLC.
20786  *
20787  * Originally Released Under LGPL - original licence link has changed is not relivant.
20788  *
20789  * Fork - LGPL
20790  * <script type="text/javascript">
20791  */
20792  
20793 /**
20794  * @class Roo.menu.CheckItem
20795  * @extends Roo.menu.Item
20796  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20797  * @constructor
20798  * Creates a new CheckItem
20799  * @param {Object} config Configuration options
20800  */
20801 Roo.menu.CheckItem = function(config){
20802     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20803     this.addEvents({
20804         /**
20805          * @event beforecheckchange
20806          * Fires before the checked value is set, providing an opportunity to cancel if needed
20807          * @param {Roo.menu.CheckItem} this
20808          * @param {Boolean} checked The new checked value that will be set
20809          */
20810         "beforecheckchange" : true,
20811         /**
20812          * @event checkchange
20813          * Fires after the checked value has been set
20814          * @param {Roo.menu.CheckItem} this
20815          * @param {Boolean} checked The checked value that was set
20816          */
20817         "checkchange" : true
20818     });
20819     if(this.checkHandler){
20820         this.on('checkchange', this.checkHandler, this.scope);
20821     }
20822 };
20823 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20824     /**
20825      * @cfg {String} group
20826      * All check items with the same group name will automatically be grouped into a single-select
20827      * radio button group (defaults to '')
20828      */
20829     /**
20830      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20831      */
20832     itemCls : "x-menu-item x-menu-check-item",
20833     /**
20834      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20835      */
20836     groupClass : "x-menu-group-item",
20837
20838     /**
20839      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20840      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20841      * initialized with checked = true will be rendered as checked.
20842      */
20843     checked: false,
20844
20845     // private
20846     ctype: "Roo.menu.CheckItem",
20847
20848     // private
20849     onRender : function(c){
20850         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20851         if(this.group){
20852             this.el.addClass(this.groupClass);
20853         }
20854         Roo.menu.MenuMgr.registerCheckable(this);
20855         if(this.checked){
20856             this.checked = false;
20857             this.setChecked(true, true);
20858         }
20859     },
20860
20861     // private
20862     destroy : function(){
20863         if(this.rendered){
20864             Roo.menu.MenuMgr.unregisterCheckable(this);
20865         }
20866         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20867     },
20868
20869     /**
20870      * Set the checked state of this item
20871      * @param {Boolean} checked The new checked value
20872      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20873      */
20874     setChecked : function(state, suppressEvent){
20875         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20876             if(this.container){
20877                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20878             }
20879             this.checked = state;
20880             if(suppressEvent !== true){
20881                 this.fireEvent("checkchange", this, state);
20882             }
20883         }
20884     },
20885
20886     // private
20887     handleClick : function(e){
20888        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20889            this.setChecked(!this.checked);
20890        }
20891        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20892     }
20893 });/*
20894  * Based on:
20895  * Ext JS Library 1.1.1
20896  * Copyright(c) 2006-2007, Ext JS, LLC.
20897  *
20898  * Originally Released Under LGPL - original licence link has changed is not relivant.
20899  *
20900  * Fork - LGPL
20901  * <script type="text/javascript">
20902  */
20903  
20904 /**
20905  * @class Roo.menu.DateItem
20906  * @extends Roo.menu.Adapter
20907  * A menu item that wraps the {@link Roo.DatPicker} component.
20908  * @constructor
20909  * Creates a new DateItem
20910  * @param {Object} config Configuration options
20911  */
20912 Roo.menu.DateItem = function(config){
20913     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20914     /** The Roo.DatePicker object @type Roo.DatePicker */
20915     this.picker = this.component;
20916     this.addEvents({select: true});
20917     
20918     this.picker.on("render", function(picker){
20919         picker.getEl().swallowEvent("click");
20920         picker.container.addClass("x-menu-date-item");
20921     });
20922
20923     this.picker.on("select", this.onSelect, this);
20924 };
20925
20926 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20927     // private
20928     onSelect : function(picker, date){
20929         this.fireEvent("select", this, date, picker);
20930         Roo.menu.DateItem.superclass.handleClick.call(this);
20931     }
20932 });/*
20933  * Based on:
20934  * Ext JS Library 1.1.1
20935  * Copyright(c) 2006-2007, Ext JS, LLC.
20936  *
20937  * Originally Released Under LGPL - original licence link has changed is not relivant.
20938  *
20939  * Fork - LGPL
20940  * <script type="text/javascript">
20941  */
20942  
20943 /**
20944  * @class Roo.menu.ColorItem
20945  * @extends Roo.menu.Adapter
20946  * A menu item that wraps the {@link Roo.ColorPalette} component.
20947  * @constructor
20948  * Creates a new ColorItem
20949  * @param {Object} config Configuration options
20950  */
20951 Roo.menu.ColorItem = function(config){
20952     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20953     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20954     this.palette = this.component;
20955     this.relayEvents(this.palette, ["select"]);
20956     if(this.selectHandler){
20957         this.on('select', this.selectHandler, this.scope);
20958     }
20959 };
20960 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20961  * Based on:
20962  * Ext JS Library 1.1.1
20963  * Copyright(c) 2006-2007, Ext JS, LLC.
20964  *
20965  * Originally Released Under LGPL - original licence link has changed is not relivant.
20966  *
20967  * Fork - LGPL
20968  * <script type="text/javascript">
20969  */
20970  
20971
20972 /**
20973  * @class Roo.menu.DateMenu
20974  * @extends Roo.menu.Menu
20975  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20976  * @constructor
20977  * Creates a new DateMenu
20978  * @param {Object} config Configuration options
20979  */
20980 Roo.menu.DateMenu = function(config){
20981     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20982     this.plain = true;
20983     var di = new Roo.menu.DateItem(config);
20984     this.add(di);
20985     /**
20986      * The {@link Roo.DatePicker} instance for this DateMenu
20987      * @type DatePicker
20988      */
20989     this.picker = di.picker;
20990     /**
20991      * @event select
20992      * @param {DatePicker} picker
20993      * @param {Date} date
20994      */
20995     this.relayEvents(di, ["select"]);
20996
20997     this.on('beforeshow', function(){
20998         if(this.picker){
20999             this.picker.hideMonthPicker(true);
21000         }
21001     }, this);
21002 };
21003 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
21004     cls:'x-date-menu'
21005 });/*
21006  * Based on:
21007  * Ext JS Library 1.1.1
21008  * Copyright(c) 2006-2007, Ext JS, LLC.
21009  *
21010  * Originally Released Under LGPL - original licence link has changed is not relivant.
21011  *
21012  * Fork - LGPL
21013  * <script type="text/javascript">
21014  */
21015  
21016
21017 /**
21018  * @class Roo.menu.ColorMenu
21019  * @extends Roo.menu.Menu
21020  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
21021  * @constructor
21022  * Creates a new ColorMenu
21023  * @param {Object} config Configuration options
21024  */
21025 Roo.menu.ColorMenu = function(config){
21026     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
21027     this.plain = true;
21028     var ci = new Roo.menu.ColorItem(config);
21029     this.add(ci);
21030     /**
21031      * The {@link Roo.ColorPalette} instance for this ColorMenu
21032      * @type ColorPalette
21033      */
21034     this.palette = ci.palette;
21035     /**
21036      * @event select
21037      * @param {ColorPalette} palette
21038      * @param {String} color
21039      */
21040     this.relayEvents(ci, ["select"]);
21041 };
21042 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
21043  * Based on:
21044  * Ext JS Library 1.1.1
21045  * Copyright(c) 2006-2007, Ext JS, LLC.
21046  *
21047  * Originally Released Under LGPL - original licence link has changed is not relivant.
21048  *
21049  * Fork - LGPL
21050  * <script type="text/javascript">
21051  */
21052  
21053 /**
21054  * @class Roo.form.Field
21055  * @extends Roo.BoxComponent
21056  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
21057  * @constructor
21058  * Creates a new Field
21059  * @param {Object} config Configuration options
21060  */
21061 Roo.form.Field = function(config){
21062     Roo.form.Field.superclass.constructor.call(this, config);
21063 };
21064
21065 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
21066     /**
21067      * @cfg {String} fieldLabel Label to use when rendering a form.
21068      */
21069        /**
21070      * @cfg {String} qtip Mouse over tip
21071      */
21072      
21073     /**
21074      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
21075      */
21076     invalidClass : "x-form-invalid",
21077     /**
21078      * @cfg {String} invalidText The error text to use when marking a field invalid and no message is provided (defaults to "The value in this field is invalid")
21079      */
21080     invalidText : "The value in this field is invalid",
21081     /**
21082      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
21083      */
21084     focusClass : "x-form-focus",
21085     /**
21086      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
21087       automatic validation (defaults to "keyup").
21088      */
21089     validationEvent : "keyup",
21090     /**
21091      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
21092      */
21093     validateOnBlur : true,
21094     /**
21095      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
21096      */
21097     validationDelay : 250,
21098     /**
21099      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21100      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
21101      */
21102     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
21103     /**
21104      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
21105      */
21106     fieldClass : "x-form-field",
21107     /**
21108      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
21109      *<pre>
21110 Value         Description
21111 -----------   ----------------------------------------------------------------------
21112 qtip          Display a quick tip when the user hovers over the field
21113 title         Display a default browser title attribute popup
21114 under         Add a block div beneath the field containing the error text
21115 side          Add an error icon to the right of the field with a popup on hover
21116 [element id]  Add the error text directly to the innerHTML of the specified element
21117 </pre>
21118      */
21119     msgTarget : 'qtip',
21120     /**
21121      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
21122      */
21123     msgFx : 'normal',
21124
21125     /**
21126      * @cfg {Boolean} readOnly True to mark the field as readOnly in HTML (defaults to false) -- Note: this only sets the element's readOnly DOM attribute.
21127      */
21128     readOnly : false,
21129
21130     /**
21131      * @cfg {Boolean} disabled True to disable the field (defaults to false).
21132      */
21133     disabled : false,
21134
21135     /**
21136      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
21137      */
21138     inputType : undefined,
21139     
21140     /**
21141      * @cfg {Number} tabIndex The tabIndex for this field. Note this only applies to fields that are rendered, not those which are built via applyTo (defaults to undefined).
21142          */
21143         tabIndex : undefined,
21144         
21145     // private
21146     isFormField : true,
21147
21148     // private
21149     hasFocus : false,
21150     /**
21151      * @property {Roo.Element} fieldEl
21152      * Element Containing the rendered Field (with label etc.)
21153      */
21154     /**
21155      * @cfg {Mixed} value A value to initialize this field with.
21156      */
21157     value : undefined,
21158
21159     /**
21160      * @cfg {String} name The field's HTML name attribute.
21161      */
21162     /**
21163      * @cfg {String} cls A CSS class to apply to the field's underlying element.
21164      */
21165
21166         // private ??
21167         initComponent : function(){
21168         Roo.form.Field.superclass.initComponent.call(this);
21169         this.addEvents({
21170             /**
21171              * @event focus
21172              * Fires when this field receives input focus.
21173              * @param {Roo.form.Field} this
21174              */
21175             focus : true,
21176             /**
21177              * @event blur
21178              * Fires when this field loses input focus.
21179              * @param {Roo.form.Field} this
21180              */
21181             blur : true,
21182             /**
21183              * @event specialkey
21184              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
21185              * {@link Roo.EventObject#getKey} to determine which key was pressed.
21186              * @param {Roo.form.Field} this
21187              * @param {Roo.EventObject} e The event object
21188              */
21189             specialkey : true,
21190             /**
21191              * @event change
21192              * Fires just before the field blurs if the field value has changed.
21193              * @param {Roo.form.Field} this
21194              * @param {Mixed} newValue The new value
21195              * @param {Mixed} oldValue The original value
21196              */
21197             change : true,
21198             /**
21199              * @event invalid
21200              * Fires after the field has been marked as invalid.
21201              * @param {Roo.form.Field} this
21202              * @param {String} msg The validation message
21203              */
21204             invalid : true,
21205             /**
21206              * @event valid
21207              * Fires after the field has been validated with no errors.
21208              * @param {Roo.form.Field} this
21209              */
21210             valid : true,
21211              /**
21212              * @event keyup
21213              * Fires after the key up
21214              * @param {Roo.form.Field} this
21215              * @param {Roo.EventObject}  e The event Object
21216              */
21217             keyup : true
21218         });
21219     },
21220
21221     /**
21222      * Returns the name attribute of the field if available
21223      * @return {String} name The field name
21224      */
21225     getName: function(){
21226          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
21227     },
21228
21229     // private
21230     onRender : function(ct, position){
21231         Roo.form.Field.superclass.onRender.call(this, ct, position);
21232         if(!this.el){
21233             var cfg = this.getAutoCreate();
21234             if(!cfg.name){
21235                 cfg.name = this.name || this.id;
21236             }
21237             if(this.inputType){
21238                 cfg.type = this.inputType;
21239             }
21240             this.el = ct.createChild(cfg, position);
21241         }
21242         var type = this.el.dom.type;
21243         if(type){
21244             if(type == 'password'){
21245                 type = 'text';
21246             }
21247             this.el.addClass('x-form-'+type);
21248         }
21249         if(this.readOnly){
21250             this.el.dom.readOnly = true;
21251         }
21252         if(this.tabIndex !== undefined){
21253             this.el.dom.setAttribute('tabIndex', this.tabIndex);
21254         }
21255
21256         this.el.addClass([this.fieldClass, this.cls]);
21257         this.initValue();
21258     },
21259
21260     /**
21261      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
21262      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
21263      * @return {Roo.form.Field} this
21264      */
21265     applyTo : function(target){
21266         this.allowDomMove = false;
21267         this.el = Roo.get(target);
21268         this.render(this.el.dom.parentNode);
21269         return this;
21270     },
21271
21272     // private
21273     initValue : function(){
21274         if(this.value !== undefined){
21275             this.setValue(this.value);
21276         }else if(this.el.dom.value.length > 0){
21277             this.setValue(this.el.dom.value);
21278         }
21279     },
21280
21281     /**
21282      * Returns true if this field has been changed since it was originally loaded and is not disabled.
21283      */
21284     isDirty : function() {
21285         if(this.disabled) {
21286             return false;
21287         }
21288         return String(this.getValue()) !== String(this.originalValue);
21289     },
21290
21291     // private
21292     afterRender : function(){
21293         Roo.form.Field.superclass.afterRender.call(this);
21294         this.initEvents();
21295     },
21296
21297     // private
21298     fireKey : function(e){
21299         //Roo.log('field ' + e.getKey());
21300         if(e.isNavKeyPress()){
21301             this.fireEvent("specialkey", this, e);
21302         }
21303     },
21304
21305     /**
21306      * Resets the current field value to the originally loaded value and clears any validation messages
21307      */
21308     reset : function(){
21309         this.setValue(this.originalValue);
21310         this.clearInvalid();
21311     },
21312
21313     // private
21314     initEvents : function(){
21315         // safari killled keypress - so keydown is now used..
21316         this.el.on("keydown" , this.fireKey,  this);
21317         this.el.on("focus", this.onFocus,  this);
21318         this.el.on("blur", this.onBlur,  this);
21319         this.el.relayEvent('keyup', this);
21320
21321         // reference to original value for reset
21322         this.originalValue = this.getValue();
21323     },
21324
21325     // private
21326     onFocus : function(){
21327         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21328             this.el.addClass(this.focusClass);
21329         }
21330         if(!this.hasFocus){
21331             this.hasFocus = true;
21332             this.startValue = this.getValue();
21333             this.fireEvent("focus", this);
21334         }
21335     },
21336
21337     beforeBlur : Roo.emptyFn,
21338
21339     // private
21340     onBlur : function(){
21341         this.beforeBlur();
21342         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21343             this.el.removeClass(this.focusClass);
21344         }
21345         this.hasFocus = false;
21346         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21347             this.validate();
21348         }
21349         var v = this.getValue();
21350         if(String(v) !== String(this.startValue)){
21351             this.fireEvent('change', this, v, this.startValue);
21352         }
21353         this.fireEvent("blur", this);
21354     },
21355
21356     /**
21357      * Returns whether or not the field value is currently valid
21358      * @param {Boolean} preventMark True to disable marking the field invalid
21359      * @return {Boolean} True if the value is valid, else false
21360      */
21361     isValid : function(preventMark){
21362         if(this.disabled){
21363             return true;
21364         }
21365         var restore = this.preventMark;
21366         this.preventMark = preventMark === true;
21367         var v = this.validateValue(this.processValue(this.getRawValue()));
21368         this.preventMark = restore;
21369         return v;
21370     },
21371
21372     /**
21373      * Validates the field value
21374      * @return {Boolean} True if the value is valid, else false
21375      */
21376     validate : function(){
21377         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21378             this.clearInvalid();
21379             return true;
21380         }
21381         return false;
21382     },
21383
21384     processValue : function(value){
21385         return value;
21386     },
21387
21388     // private
21389     // Subclasses should provide the validation implementation by overriding this
21390     validateValue : function(value){
21391         return true;
21392     },
21393
21394     /**
21395      * Mark this field as invalid
21396      * @param {String} msg The validation message
21397      */
21398     markInvalid : function(msg){
21399         if(!this.rendered || this.preventMark){ // not rendered
21400             return;
21401         }
21402         this.el.addClass(this.invalidClass);
21403         msg = msg || this.invalidText;
21404         switch(this.msgTarget){
21405             case 'qtip':
21406                 this.el.dom.qtip = msg;
21407                 this.el.dom.qclass = 'x-form-invalid-tip';
21408                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21409                     Roo.QuickTips.enable();
21410                 }
21411                 break;
21412             case 'title':
21413                 this.el.dom.title = msg;
21414                 break;
21415             case 'under':
21416                 if(!this.errorEl){
21417                     var elp = this.el.findParent('.x-form-element', 5, true);
21418                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21419                     this.errorEl.setWidth(elp.getWidth(true)-20);
21420                 }
21421                 this.errorEl.update(msg);
21422                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21423                 break;
21424             case 'side':
21425                 if(!this.errorIcon){
21426                     var elp = this.el.findParent('.x-form-element', 5, true);
21427                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21428                 }
21429                 this.alignErrorIcon();
21430                 this.errorIcon.dom.qtip = msg;
21431                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21432                 this.errorIcon.show();
21433                 this.on('resize', this.alignErrorIcon, this);
21434                 break;
21435             default:
21436                 var t = Roo.getDom(this.msgTarget);
21437                 t.innerHTML = msg;
21438                 t.style.display = this.msgDisplay;
21439                 break;
21440         }
21441         this.fireEvent('invalid', this, msg);
21442     },
21443
21444     // private
21445     alignErrorIcon : function(){
21446         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21447     },
21448
21449     /**
21450      * Clear any invalid styles/messages for this field
21451      */
21452     clearInvalid : function(){
21453         if(!this.rendered || this.preventMark){ // not rendered
21454             return;
21455         }
21456         this.el.removeClass(this.invalidClass);
21457         switch(this.msgTarget){
21458             case 'qtip':
21459                 this.el.dom.qtip = '';
21460                 break;
21461             case 'title':
21462                 this.el.dom.title = '';
21463                 break;
21464             case 'under':
21465                 if(this.errorEl){
21466                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21467                 }
21468                 break;
21469             case 'side':
21470                 if(this.errorIcon){
21471                     this.errorIcon.dom.qtip = '';
21472                     this.errorIcon.hide();
21473                     this.un('resize', this.alignErrorIcon, this);
21474                 }
21475                 break;
21476             default:
21477                 var t = Roo.getDom(this.msgTarget);
21478                 t.innerHTML = '';
21479                 t.style.display = 'none';
21480                 break;
21481         }
21482         this.fireEvent('valid', this);
21483     },
21484
21485     /**
21486      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21487      * @return {Mixed} value The field value
21488      */
21489     getRawValue : function(){
21490         var v = this.el.getValue();
21491         if(v === this.emptyText){
21492             v = '';
21493         }
21494         return v;
21495     },
21496
21497     /**
21498      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21499      * @return {Mixed} value The field value
21500      */
21501     getValue : function(){
21502         var v = this.el.getValue();
21503         if(v === this.emptyText || v === undefined){
21504             v = '';
21505         }
21506         return v;
21507     },
21508
21509     /**
21510      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21511      * @param {Mixed} value The value to set
21512      */
21513     setRawValue : function(v){
21514         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21515     },
21516
21517     /**
21518      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21519      * @param {Mixed} value The value to set
21520      */
21521     setValue : function(v){
21522         this.value = v;
21523         if(this.rendered){
21524             this.el.dom.value = (v === null || v === undefined ? '' : v);
21525              this.validate();
21526         }
21527     },
21528
21529     adjustSize : function(w, h){
21530         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21531         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21532         return s;
21533     },
21534
21535     adjustWidth : function(tag, w){
21536         tag = tag.toLowerCase();
21537         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21538             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21539                 if(tag == 'input'){
21540                     return w + 2;
21541                 }
21542                 if(tag = 'textarea'){
21543                     return w-2;
21544                 }
21545             }else if(Roo.isOpera){
21546                 if(tag == 'input'){
21547                     return w + 2;
21548                 }
21549                 if(tag = 'textarea'){
21550                     return w-2;
21551                 }
21552             }
21553         }
21554         return w;
21555     }
21556 });
21557
21558
21559 // anything other than normal should be considered experimental
21560 Roo.form.Field.msgFx = {
21561     normal : {
21562         show: function(msgEl, f){
21563             msgEl.setDisplayed('block');
21564         },
21565
21566         hide : function(msgEl, f){
21567             msgEl.setDisplayed(false).update('');
21568         }
21569     },
21570
21571     slide : {
21572         show: function(msgEl, f){
21573             msgEl.slideIn('t', {stopFx:true});
21574         },
21575
21576         hide : function(msgEl, f){
21577             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21578         }
21579     },
21580
21581     slideRight : {
21582         show: function(msgEl, f){
21583             msgEl.fixDisplay();
21584             msgEl.alignTo(f.el, 'tl-tr');
21585             msgEl.slideIn('l', {stopFx:true});
21586         },
21587
21588         hide : function(msgEl, f){
21589             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21590         }
21591     }
21592 };/*
21593  * Based on:
21594  * Ext JS Library 1.1.1
21595  * Copyright(c) 2006-2007, Ext JS, LLC.
21596  *
21597  * Originally Released Under LGPL - original licence link has changed is not relivant.
21598  *
21599  * Fork - LGPL
21600  * <script type="text/javascript">
21601  */
21602  
21603
21604 /**
21605  * @class Roo.form.TextField
21606  * @extends Roo.form.Field
21607  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21608  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21609  * @constructor
21610  * Creates a new TextField
21611  * @param {Object} config Configuration options
21612  */
21613 Roo.form.TextField = function(config){
21614     Roo.form.TextField.superclass.constructor.call(this, config);
21615     this.addEvents({
21616         /**
21617          * @event autosize
21618          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21619          * according to the default logic, but this event provides a hook for the developer to apply additional
21620          * logic at runtime to resize the field if needed.
21621              * @param {Roo.form.Field} this This text field
21622              * @param {Number} width The new field width
21623              */
21624         autosize : true
21625     });
21626 };
21627
21628 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21629     /**
21630      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21631      */
21632     grow : false,
21633     /**
21634      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21635      */
21636     growMin : 30,
21637     /**
21638      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21639      */
21640     growMax : 800,
21641     /**
21642      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21643      */
21644     vtype : null,
21645     /**
21646      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21647      */
21648     maskRe : null,
21649     /**
21650      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21651      */
21652     disableKeyFilter : false,
21653     /**
21654      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21655      */
21656     allowBlank : true,
21657     /**
21658      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21659      */
21660     minLength : 0,
21661     /**
21662      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21663      */
21664     maxLength : Number.MAX_VALUE,
21665     /**
21666      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21667      */
21668     minLengthText : "The minimum length for this field is {0}",
21669     /**
21670      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21671      */
21672     maxLengthText : "The maximum length for this field is {0}",
21673     /**
21674      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21675      */
21676     selectOnFocus : false,
21677     /**
21678      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21679      */
21680     blankText : "This field is required",
21681     /**
21682      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21683      * If available, this function will be called only after the basic validators all return true, and will be passed the
21684      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21685      */
21686     validator : null,
21687     /**
21688      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21689      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21690      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21691      */
21692     regex : null,
21693     /**
21694      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21695      */
21696     regexText : "",
21697     /**
21698      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
21699      */
21700     emptyText : null,
21701     /**
21702      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
21703      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
21704      */
21705     emptyClass : 'x-form-empty-field',
21706
21707     // private
21708     initEvents : function(){
21709         Roo.form.TextField.superclass.initEvents.call(this);
21710         if(this.validationEvent == 'keyup'){
21711             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21712             this.el.on('keyup', this.filterValidation, this);
21713         }
21714         else if(this.validationEvent !== false){
21715             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21716         }
21717         if(this.selectOnFocus || this.emptyText){
21718             this.on("focus", this.preFocus, this);
21719             if(this.emptyText){
21720                 this.on('blur', this.postBlur, this);
21721                 this.applyEmptyText();
21722             }
21723         }
21724         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21725             this.el.on("keypress", this.filterKeys, this);
21726         }
21727         if(this.grow){
21728             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21729             this.el.on("click", this.autoSize,  this);
21730         }
21731     },
21732
21733     processValue : function(value){
21734         if(this.stripCharsRe){
21735             var newValue = value.replace(this.stripCharsRe, '');
21736             if(newValue !== value){
21737                 this.setRawValue(newValue);
21738                 return newValue;
21739             }
21740         }
21741         return value;
21742     },
21743
21744     filterValidation : function(e){
21745         if(!e.isNavKeyPress()){
21746             this.validationTask.delay(this.validationDelay);
21747         }
21748     },
21749
21750     // private
21751     onKeyUp : function(e){
21752         if(!e.isNavKeyPress()){
21753             this.autoSize();
21754         }
21755     },
21756
21757     /**
21758      * Resets the current field value to the originally-loaded value and clears any validation messages.
21759      * Also adds emptyText and emptyClass if the original value was blank.
21760      */
21761     reset : function(){
21762         Roo.form.TextField.superclass.reset.call(this);
21763         this.applyEmptyText();
21764     },
21765
21766     applyEmptyText : function(){
21767         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
21768             this.setRawValue(this.emptyText);
21769             this.el.addClass(this.emptyClass);
21770         }
21771     },
21772
21773     // private
21774     preFocus : function(){
21775         if(this.emptyText){
21776             if(this.el.dom.value == this.emptyText){
21777                 this.setRawValue('');
21778             }
21779             this.el.removeClass(this.emptyClass);
21780         }
21781         if(this.selectOnFocus){
21782             this.el.dom.select();
21783         }
21784     },
21785
21786     // private
21787     postBlur : function(){
21788         this.applyEmptyText();
21789     },
21790
21791     // private
21792     filterKeys : function(e){
21793         var k = e.getKey();
21794         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21795             return;
21796         }
21797         var c = e.getCharCode(), cc = String.fromCharCode(c);
21798         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21799             return;
21800         }
21801         if(!this.maskRe.test(cc)){
21802             e.stopEvent();
21803         }
21804     },
21805
21806     setValue : function(v){
21807         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
21808             this.el.removeClass(this.emptyClass);
21809         }
21810         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21811         this.applyEmptyText();
21812         this.autoSize();
21813     },
21814
21815     /**
21816      * Validates a value according to the field's validation rules and marks the field as invalid
21817      * if the validation fails
21818      * @param {Mixed} value The value to validate
21819      * @return {Boolean} True if the value is valid, else false
21820      */
21821     validateValue : function(value){
21822         if(value.length < 1 || value === this.emptyText){ // if it's blank
21823              if(this.allowBlank){
21824                 this.clearInvalid();
21825                 return true;
21826              }else{
21827                 this.markInvalid(this.blankText);
21828                 return false;
21829              }
21830         }
21831         if(value.length < this.minLength){
21832             this.markInvalid(String.format(this.minLengthText, this.minLength));
21833             return false;
21834         }
21835         if(value.length > this.maxLength){
21836             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21837             return false;
21838         }
21839         if(this.vtype){
21840             var vt = Roo.form.VTypes;
21841             if(!vt[this.vtype](value, this)){
21842                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21843                 return false;
21844             }
21845         }
21846         if(typeof this.validator == "function"){
21847             var msg = this.validator(value);
21848             if(msg !== true){
21849                 this.markInvalid(msg);
21850                 return false;
21851             }
21852         }
21853         if(this.regex && !this.regex.test(value)){
21854             this.markInvalid(this.regexText);
21855             return false;
21856         }
21857         return true;
21858     },
21859
21860     /**
21861      * Selects text in this field
21862      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21863      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21864      */
21865     selectText : function(start, end){
21866         var v = this.getRawValue();
21867         if(v.length > 0){
21868             start = start === undefined ? 0 : start;
21869             end = end === undefined ? v.length : end;
21870             var d = this.el.dom;
21871             if(d.setSelectionRange){
21872                 d.setSelectionRange(start, end);
21873             }else if(d.createTextRange){
21874                 var range = d.createTextRange();
21875                 range.moveStart("character", start);
21876                 range.moveEnd("character", v.length-end);
21877                 range.select();
21878             }
21879         }
21880     },
21881
21882     /**
21883      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21884      * This only takes effect if grow = true, and fires the autosize event.
21885      */
21886     autoSize : function(){
21887         if(!this.grow || !this.rendered){
21888             return;
21889         }
21890         if(!this.metrics){
21891             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21892         }
21893         var el = this.el;
21894         var v = el.dom.value;
21895         var d = document.createElement('div');
21896         d.appendChild(document.createTextNode(v));
21897         v = d.innerHTML;
21898         d = null;
21899         v += "&#160;";
21900         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21901         this.el.setWidth(w);
21902         this.fireEvent("autosize", this, w);
21903     }
21904 });/*
21905  * Based on:
21906  * Ext JS Library 1.1.1
21907  * Copyright(c) 2006-2007, Ext JS, LLC.
21908  *
21909  * Originally Released Under LGPL - original licence link has changed is not relivant.
21910  *
21911  * Fork - LGPL
21912  * <script type="text/javascript">
21913  */
21914  
21915 /**
21916  * @class Roo.form.Hidden
21917  * @extends Roo.form.TextField
21918  * Simple Hidden element used on forms 
21919  * 
21920  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21921  * 
21922  * @constructor
21923  * Creates a new Hidden form element.
21924  * @param {Object} config Configuration options
21925  */
21926
21927
21928
21929 // easy hidden field...
21930 Roo.form.Hidden = function(config){
21931     Roo.form.Hidden.superclass.constructor.call(this, config);
21932 };
21933   
21934 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21935     fieldLabel:      '',
21936     inputType:      'hidden',
21937     width:          50,
21938     allowBlank:     true,
21939     labelSeparator: '',
21940     hidden:         true,
21941     itemCls :       'x-form-item-display-none'
21942
21943
21944 });
21945
21946
21947 /*
21948  * Based on:
21949  * Ext JS Library 1.1.1
21950  * Copyright(c) 2006-2007, Ext JS, LLC.
21951  *
21952  * Originally Released Under LGPL - original licence link has changed is not relivant.
21953  *
21954  * Fork - LGPL
21955  * <script type="text/javascript">
21956  */
21957  
21958 /**
21959  * @class Roo.form.TriggerField
21960  * @extends Roo.form.TextField
21961  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21962  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21963  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21964  * for which you can provide a custom implementation.  For example:
21965  * <pre><code>
21966 var trigger = new Roo.form.TriggerField();
21967 trigger.onTriggerClick = myTriggerFn;
21968 trigger.applyTo('my-field');
21969 </code></pre>
21970  *
21971  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21972  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21973  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21974  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21975  * @constructor
21976  * Create a new TriggerField.
21977  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21978  * to the base TextField)
21979  */
21980 Roo.form.TriggerField = function(config){
21981     this.mimicing = false;
21982     Roo.form.TriggerField.superclass.constructor.call(this, config);
21983 };
21984
21985 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21986     /**
21987      * @cfg {String} triggerClass A CSS class to apply to the trigger
21988      */
21989     /**
21990      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21991      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21992      */
21993     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
21994     /**
21995      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21996      */
21997     hideTrigger:false,
21998
21999     /** @cfg {Boolean} grow @hide */
22000     /** @cfg {Number} growMin @hide */
22001     /** @cfg {Number} growMax @hide */
22002
22003     /**
22004      * @hide 
22005      * @method
22006      */
22007     autoSize: Roo.emptyFn,
22008     // private
22009     monitorTab : true,
22010     // private
22011     deferHeight : true,
22012
22013     
22014     actionMode : 'wrap',
22015     // private
22016     onResize : function(w, h){
22017         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
22018         if(typeof w == 'number'){
22019             var x = w - this.trigger.getWidth();
22020             this.el.setWidth(this.adjustWidth('input', x));
22021             this.trigger.setStyle('left', x+'px');
22022         }
22023     },
22024
22025     // private
22026     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22027
22028     // private
22029     getResizeEl : function(){
22030         return this.wrap;
22031     },
22032
22033     // private
22034     getPositionEl : function(){
22035         return this.wrap;
22036     },
22037
22038     // private
22039     alignErrorIcon : function(){
22040         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
22041     },
22042
22043     // private
22044     onRender : function(ct, position){
22045         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
22046         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
22047         this.trigger = this.wrap.createChild(this.triggerConfig ||
22048                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
22049         if(this.hideTrigger){
22050             this.trigger.setDisplayed(false);
22051         }
22052         this.initTrigger();
22053         if(!this.width){
22054             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
22055         }
22056     },
22057
22058     // private
22059     initTrigger : function(){
22060         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
22061         this.trigger.addClassOnOver('x-form-trigger-over');
22062         this.trigger.addClassOnClick('x-form-trigger-click');
22063     },
22064
22065     // private
22066     onDestroy : function(){
22067         if(this.trigger){
22068             this.trigger.removeAllListeners();
22069             this.trigger.remove();
22070         }
22071         if(this.wrap){
22072             this.wrap.remove();
22073         }
22074         Roo.form.TriggerField.superclass.onDestroy.call(this);
22075     },
22076
22077     // private
22078     onFocus : function(){
22079         Roo.form.TriggerField.superclass.onFocus.call(this);
22080         if(!this.mimicing){
22081             this.wrap.addClass('x-trigger-wrap-focus');
22082             this.mimicing = true;
22083             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
22084             if(this.monitorTab){
22085                 this.el.on("keydown", this.checkTab, this);
22086             }
22087         }
22088     },
22089
22090     // private
22091     checkTab : function(e){
22092         if(e.getKey() == e.TAB){
22093             this.triggerBlur();
22094         }
22095     },
22096
22097     // private
22098     onBlur : function(){
22099         // do nothing
22100     },
22101
22102     // private
22103     mimicBlur : function(e, t){
22104         if(!this.wrap.contains(t) && this.validateBlur()){
22105             this.triggerBlur();
22106         }
22107     },
22108
22109     // private
22110     triggerBlur : function(){
22111         this.mimicing = false;
22112         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
22113         if(this.monitorTab){
22114             this.el.un("keydown", this.checkTab, this);
22115         }
22116         this.wrap.removeClass('x-trigger-wrap-focus');
22117         Roo.form.TriggerField.superclass.onBlur.call(this);
22118     },
22119
22120     // private
22121     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
22122     validateBlur : function(e, t){
22123         return true;
22124     },
22125
22126     // private
22127     onDisable : function(){
22128         Roo.form.TriggerField.superclass.onDisable.call(this);
22129         if(this.wrap){
22130             this.wrap.addClass('x-item-disabled');
22131         }
22132     },
22133
22134     // private
22135     onEnable : function(){
22136         Roo.form.TriggerField.superclass.onEnable.call(this);
22137         if(this.wrap){
22138             this.wrap.removeClass('x-item-disabled');
22139         }
22140     },
22141
22142     // private
22143     onShow : function(){
22144         var ae = this.getActionEl();
22145         
22146         if(ae){
22147             ae.dom.style.display = '';
22148             ae.dom.style.visibility = 'visible';
22149         }
22150     },
22151
22152     // private
22153     
22154     onHide : function(){
22155         var ae = this.getActionEl();
22156         ae.dom.style.display = 'none';
22157     },
22158
22159     /**
22160      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
22161      * by an implementing function.
22162      * @method
22163      * @param {EventObject} e
22164      */
22165     onTriggerClick : Roo.emptyFn
22166 });
22167
22168 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
22169 // to be extended by an implementing class.  For an example of implementing this class, see the custom
22170 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
22171 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
22172     initComponent : function(){
22173         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
22174
22175         this.triggerConfig = {
22176             tag:'span', cls:'x-form-twin-triggers', cn:[
22177             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
22178             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
22179         ]};
22180     },
22181
22182     getTrigger : function(index){
22183         return this.triggers[index];
22184     },
22185
22186     initTrigger : function(){
22187         var ts = this.trigger.select('.x-form-trigger', true);
22188         this.wrap.setStyle('overflow', 'hidden');
22189         var triggerField = this;
22190         ts.each(function(t, all, index){
22191             t.hide = function(){
22192                 var w = triggerField.wrap.getWidth();
22193                 this.dom.style.display = 'none';
22194                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22195             };
22196             t.show = function(){
22197                 var w = triggerField.wrap.getWidth();
22198                 this.dom.style.display = '';
22199                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22200             };
22201             var triggerIndex = 'Trigger'+(index+1);
22202
22203             if(this['hide'+triggerIndex]){
22204                 t.dom.style.display = 'none';
22205             }
22206             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
22207             t.addClassOnOver('x-form-trigger-over');
22208             t.addClassOnClick('x-form-trigger-click');
22209         }, this);
22210         this.triggers = ts.elements;
22211     },
22212
22213     onTrigger1Click : Roo.emptyFn,
22214     onTrigger2Click : Roo.emptyFn
22215 });/*
22216  * Based on:
22217  * Ext JS Library 1.1.1
22218  * Copyright(c) 2006-2007, Ext JS, LLC.
22219  *
22220  * Originally Released Under LGPL - original licence link has changed is not relivant.
22221  *
22222  * Fork - LGPL
22223  * <script type="text/javascript">
22224  */
22225  
22226 /**
22227  * @class Roo.form.TextArea
22228  * @extends Roo.form.TextField
22229  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
22230  * support for auto-sizing.
22231  * @constructor
22232  * Creates a new TextArea
22233  * @param {Object} config Configuration options
22234  */
22235 Roo.form.TextArea = function(config){
22236     Roo.form.TextArea.superclass.constructor.call(this, config);
22237     // these are provided exchanges for backwards compat
22238     // minHeight/maxHeight were replaced by growMin/growMax to be
22239     // compatible with TextField growing config values
22240     if(this.minHeight !== undefined){
22241         this.growMin = this.minHeight;
22242     }
22243     if(this.maxHeight !== undefined){
22244         this.growMax = this.maxHeight;
22245     }
22246 };
22247
22248 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
22249     /**
22250      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
22251      */
22252     growMin : 60,
22253     /**
22254      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
22255      */
22256     growMax: 1000,
22257     /**
22258      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
22259      * in the field (equivalent to setting overflow: hidden, defaults to false)
22260      */
22261     preventScrollbars: false,
22262     /**
22263      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22264      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
22265      */
22266
22267     // private
22268     onRender : function(ct, position){
22269         if(!this.el){
22270             this.defaultAutoCreate = {
22271                 tag: "textarea",
22272                 style:"width:300px;height:60px;",
22273                 autocomplete: "off"
22274             };
22275         }
22276         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22277         if(this.grow){
22278             this.textSizeEl = Roo.DomHelper.append(document.body, {
22279                 tag: "pre", cls: "x-form-grow-sizer"
22280             });
22281             if(this.preventScrollbars){
22282                 this.el.setStyle("overflow", "hidden");
22283             }
22284             this.el.setHeight(this.growMin);
22285         }
22286     },
22287
22288     onDestroy : function(){
22289         if(this.textSizeEl){
22290             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22291         }
22292         Roo.form.TextArea.superclass.onDestroy.call(this);
22293     },
22294
22295     // private
22296     onKeyUp : function(e){
22297         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22298             this.autoSize();
22299         }
22300     },
22301
22302     /**
22303      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22304      * This only takes effect if grow = true, and fires the autosize event if the height changes.
22305      */
22306     autoSize : function(){
22307         if(!this.grow || !this.textSizeEl){
22308             return;
22309         }
22310         var el = this.el;
22311         var v = el.dom.value;
22312         var ts = this.textSizeEl;
22313
22314         ts.innerHTML = '';
22315         ts.appendChild(document.createTextNode(v));
22316         v = ts.innerHTML;
22317
22318         Roo.fly(ts).setWidth(this.el.getWidth());
22319         if(v.length < 1){
22320             v = "&#160;&#160;";
22321         }else{
22322             if(Roo.isIE){
22323                 v = v.replace(/\n/g, '<p>&#160;</p>');
22324             }
22325             v += "&#160;\n&#160;";
22326         }
22327         ts.innerHTML = v;
22328         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22329         if(h != this.lastHeight){
22330             this.lastHeight = h;
22331             this.el.setHeight(h);
22332             this.fireEvent("autosize", this, h);
22333         }
22334     }
22335 });/*
22336  * Based on:
22337  * Ext JS Library 1.1.1
22338  * Copyright(c) 2006-2007, Ext JS, LLC.
22339  *
22340  * Originally Released Under LGPL - original licence link has changed is not relivant.
22341  *
22342  * Fork - LGPL
22343  * <script type="text/javascript">
22344  */
22345  
22346
22347 /**
22348  * @class Roo.form.NumberField
22349  * @extends Roo.form.TextField
22350  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22351  * @constructor
22352  * Creates a new NumberField
22353  * @param {Object} config Configuration options
22354  */
22355 Roo.form.NumberField = function(config){
22356     Roo.form.NumberField.superclass.constructor.call(this, config);
22357 };
22358
22359 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22360     /**
22361      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22362      */
22363     fieldClass: "x-form-field x-form-num-field",
22364     /**
22365      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22366      */
22367     allowDecimals : true,
22368     /**
22369      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22370      */
22371     decimalSeparator : ".",
22372     /**
22373      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22374      */
22375     decimalPrecision : 2,
22376     /**
22377      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22378      */
22379     allowNegative : true,
22380     /**
22381      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22382      */
22383     minValue : Number.NEGATIVE_INFINITY,
22384     /**
22385      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22386      */
22387     maxValue : Number.MAX_VALUE,
22388     /**
22389      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22390      */
22391     minText : "The minimum value for this field is {0}",
22392     /**
22393      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22394      */
22395     maxText : "The maximum value for this field is {0}",
22396     /**
22397      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22398      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22399      */
22400     nanText : "{0} is not a valid number",
22401
22402     // private
22403     initEvents : function(){
22404         Roo.form.NumberField.superclass.initEvents.call(this);
22405         var allowed = "0123456789";
22406         if(this.allowDecimals){
22407             allowed += this.decimalSeparator;
22408         }
22409         if(this.allowNegative){
22410             allowed += "-";
22411         }
22412         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22413         var keyPress = function(e){
22414             var k = e.getKey();
22415             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22416                 return;
22417             }
22418             var c = e.getCharCode();
22419             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22420                 e.stopEvent();
22421             }
22422         };
22423         this.el.on("keypress", keyPress, this);
22424     },
22425
22426     // private
22427     validateValue : function(value){
22428         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22429             return false;
22430         }
22431         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22432              return true;
22433         }
22434         var num = this.parseValue(value);
22435         if(isNaN(num)){
22436             this.markInvalid(String.format(this.nanText, value));
22437             return false;
22438         }
22439         if(num < this.minValue){
22440             this.markInvalid(String.format(this.minText, this.minValue));
22441             return false;
22442         }
22443         if(num > this.maxValue){
22444             this.markInvalid(String.format(this.maxText, this.maxValue));
22445             return false;
22446         }
22447         return true;
22448     },
22449
22450     getValue : function(){
22451         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22452     },
22453
22454     // private
22455     parseValue : function(value){
22456         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22457         return isNaN(value) ? '' : value;
22458     },
22459
22460     // private
22461     fixPrecision : function(value){
22462         var nan = isNaN(value);
22463         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22464             return nan ? '' : value;
22465         }
22466         return parseFloat(value).toFixed(this.decimalPrecision);
22467     },
22468
22469     setValue : function(v){
22470         v = this.fixPrecision(v);
22471         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22472     },
22473
22474     // private
22475     decimalPrecisionFcn : function(v){
22476         return Math.floor(v);
22477     },
22478
22479     beforeBlur : function(){
22480         var v = this.parseValue(this.getRawValue());
22481         if(v){
22482             this.setValue(v);
22483         }
22484     }
22485 });/*
22486  * Based on:
22487  * Ext JS Library 1.1.1
22488  * Copyright(c) 2006-2007, Ext JS, LLC.
22489  *
22490  * Originally Released Under LGPL - original licence link has changed is not relivant.
22491  *
22492  * Fork - LGPL
22493  * <script type="text/javascript">
22494  */
22495  
22496 /**
22497  * @class Roo.form.DateField
22498  * @extends Roo.form.TriggerField
22499  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22500 * @constructor
22501 * Create a new DateField
22502 * @param {Object} config
22503  */
22504 Roo.form.DateField = function(config){
22505     Roo.form.DateField.superclass.constructor.call(this, config);
22506     
22507       this.addEvents({
22508          
22509         /**
22510          * @event select
22511          * Fires when a date is selected
22512              * @param {Roo.form.DateField} combo This combo box
22513              * @param {Date} date The date selected
22514              */
22515         'select' : true
22516          
22517     });
22518     
22519     
22520     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22521     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22522     this.ddMatch = null;
22523     if(this.disabledDates){
22524         var dd = this.disabledDates;
22525         var re = "(?:";
22526         for(var i = 0; i < dd.length; i++){
22527             re += dd[i];
22528             if(i != dd.length-1) re += "|";
22529         }
22530         this.ddMatch = new RegExp(re + ")");
22531     }
22532 };
22533
22534 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22535     /**
22536      * @cfg {String} format
22537      * The default date format string which can be overriden for localization support.  The format must be
22538      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22539      */
22540     format : "m/d/y",
22541     /**
22542      * @cfg {String} altFormats
22543      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22544      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22545      */
22546     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22547     /**
22548      * @cfg {Array} disabledDays
22549      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22550      */
22551     disabledDays : null,
22552     /**
22553      * @cfg {String} disabledDaysText
22554      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22555      */
22556     disabledDaysText : "Disabled",
22557     /**
22558      * @cfg {Array} disabledDates
22559      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22560      * expression so they are very powerful. Some examples:
22561      * <ul>
22562      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22563      * <li>["03/08", "09/16"] would disable those days for every year</li>
22564      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22565      * <li>["03/../2006"] would disable every day in March 2006</li>
22566      * <li>["^03"] would disable every day in every March</li>
22567      * </ul>
22568      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22569      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22570      */
22571     disabledDates : null,
22572     /**
22573      * @cfg {String} disabledDatesText
22574      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22575      */
22576     disabledDatesText : "Disabled",
22577     /**
22578      * @cfg {Date/String} minValue
22579      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22580      * valid format (defaults to null).
22581      */
22582     minValue : null,
22583     /**
22584      * @cfg {Date/String} maxValue
22585      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22586      * valid format (defaults to null).
22587      */
22588     maxValue : null,
22589     /**
22590      * @cfg {String} minText
22591      * The error text to display when the date in the cell is before minValue (defaults to
22592      * 'The date in this field must be after {minValue}').
22593      */
22594     minText : "The date in this field must be equal to or after {0}",
22595     /**
22596      * @cfg {String} maxText
22597      * The error text to display when the date in the cell is after maxValue (defaults to
22598      * 'The date in this field must be before {maxValue}').
22599      */
22600     maxText : "The date in this field must be equal to or before {0}",
22601     /**
22602      * @cfg {String} invalidText
22603      * The error text to display when the date in the field is invalid (defaults to
22604      * '{value} is not a valid date - it must be in the format {format}').
22605      */
22606     invalidText : "{0} is not a valid date - it must be in the format {1}",
22607     /**
22608      * @cfg {String} triggerClass
22609      * An additional CSS class used to style the trigger button.  The trigger will always get the
22610      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22611      * which displays a calendar icon).
22612      */
22613     triggerClass : 'x-form-date-trigger',
22614     
22615
22616     /**
22617      * @cfg {bool} useIso
22618      * if enabled, then the date field will use a hidden field to store the 
22619      * real value as iso formated date. default (false)
22620      */ 
22621     useIso : false,
22622     /**
22623      * @cfg {String/Object} autoCreate
22624      * A DomHelper element spec, or true for a default element spec (defaults to
22625      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22626      */ 
22627     // private
22628     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22629     
22630     // private
22631     hiddenField: false,
22632     
22633     onRender : function(ct, position)
22634     {
22635         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22636         if (this.useIso) {
22637             this.el.dom.removeAttribute('name'); 
22638             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22639                     'before', true);
22640             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
22641             // prevent input submission
22642             this.hiddenName = this.name;
22643         }
22644             
22645             
22646     },
22647     
22648     // private
22649     validateValue : function(value)
22650     {
22651         value = this.formatDate(value);
22652         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22653             return false;
22654         }
22655         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22656              return true;
22657         }
22658         var svalue = value;
22659         value = this.parseDate(value);
22660         if(!value){
22661             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22662             return false;
22663         }
22664         var time = value.getTime();
22665         if(this.minValue && time < this.minValue.getTime()){
22666             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22667             return false;
22668         }
22669         if(this.maxValue && time > this.maxValue.getTime()){
22670             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22671             return false;
22672         }
22673         if(this.disabledDays){
22674             var day = value.getDay();
22675             for(var i = 0; i < this.disabledDays.length; i++) {
22676                 if(day === this.disabledDays[i]){
22677                     this.markInvalid(this.disabledDaysText);
22678                     return false;
22679                 }
22680             }
22681         }
22682         var fvalue = this.formatDate(value);
22683         if(this.ddMatch && this.ddMatch.test(fvalue)){
22684             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22685             return false;
22686         }
22687         return true;
22688     },
22689
22690     // private
22691     // Provides logic to override the default TriggerField.validateBlur which just returns true
22692     validateBlur : function(){
22693         return !this.menu || !this.menu.isVisible();
22694     },
22695
22696     /**
22697      * Returns the current date value of the date field.
22698      * @return {Date} The date value
22699      */
22700     getValue : function(){
22701         
22702         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22703     },
22704
22705     /**
22706      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22707      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22708      * (the default format used is "m/d/y").
22709      * <br />Usage:
22710      * <pre><code>
22711 //All of these calls set the same date value (May 4, 2006)
22712
22713 //Pass a date object:
22714 var dt = new Date('5/4/06');
22715 dateField.setValue(dt);
22716
22717 //Pass a date string (default format):
22718 dateField.setValue('5/4/06');
22719
22720 //Pass a date string (custom format):
22721 dateField.format = 'Y-m-d';
22722 dateField.setValue('2006-5-4');
22723 </code></pre>
22724      * @param {String/Date} date The date or valid date string
22725      */
22726     setValue : function(date){
22727         if (this.hiddenField) {
22728             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22729         }
22730         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22731     },
22732
22733     // private
22734     parseDate : function(value){
22735         if(!value || value instanceof Date){
22736             return value;
22737         }
22738         var v = Date.parseDate(value, this.format);
22739         if(!v && this.altFormats){
22740             if(!this.altFormatsArray){
22741                 this.altFormatsArray = this.altFormats.split("|");
22742             }
22743             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22744                 v = Date.parseDate(value, this.altFormatsArray[i]);
22745             }
22746         }
22747         return v;
22748     },
22749
22750     // private
22751     formatDate : function(date, fmt){
22752         return (!date || !(date instanceof Date)) ?
22753                date : date.dateFormat(fmt || this.format);
22754     },
22755
22756     // private
22757     menuListeners : {
22758         select: function(m, d){
22759             this.setValue(d);
22760             this.fireEvent('select', this, d);
22761         },
22762         show : function(){ // retain focus styling
22763             this.onFocus();
22764         },
22765         hide : function(){
22766             this.focus.defer(10, this);
22767             var ml = this.menuListeners;
22768             this.menu.un("select", ml.select,  this);
22769             this.menu.un("show", ml.show,  this);
22770             this.menu.un("hide", ml.hide,  this);
22771         }
22772     },
22773
22774     // private
22775     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22776     onTriggerClick : function(){
22777         if(this.disabled){
22778             return;
22779         }
22780         if(this.menu == null){
22781             this.menu = new Roo.menu.DateMenu();
22782         }
22783         Roo.apply(this.menu.picker,  {
22784             showClear: this.allowBlank,
22785             minDate : this.minValue,
22786             maxDate : this.maxValue,
22787             disabledDatesRE : this.ddMatch,
22788             disabledDatesText : this.disabledDatesText,
22789             disabledDays : this.disabledDays,
22790             disabledDaysText : this.disabledDaysText,
22791             format : this.format,
22792             minText : String.format(this.minText, this.formatDate(this.minValue)),
22793             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22794         });
22795         this.menu.on(Roo.apply({}, this.menuListeners, {
22796             scope:this
22797         }));
22798         this.menu.picker.setValue(this.getValue() || new Date());
22799         this.menu.show(this.el, "tl-bl?");
22800     },
22801
22802     beforeBlur : function(){
22803         var v = this.parseDate(this.getRawValue());
22804         if(v){
22805             this.setValue(v);
22806         }
22807     }
22808
22809     /** @cfg {Boolean} grow @hide */
22810     /** @cfg {Number} growMin @hide */
22811     /** @cfg {Number} growMax @hide */
22812     /**
22813      * @hide
22814      * @method autoSize
22815      */
22816 });/*
22817  * Based on:
22818  * Ext JS Library 1.1.1
22819  * Copyright(c) 2006-2007, Ext JS, LLC.
22820  *
22821  * Originally Released Under LGPL - original licence link has changed is not relivant.
22822  *
22823  * Fork - LGPL
22824  * <script type="text/javascript">
22825  */
22826  
22827
22828 /**
22829  * @class Roo.form.ComboBox
22830  * @extends Roo.form.TriggerField
22831  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22832  * @constructor
22833  * Create a new ComboBox.
22834  * @param {Object} config Configuration options
22835  */
22836 Roo.form.ComboBox = function(config){
22837     Roo.form.ComboBox.superclass.constructor.call(this, config);
22838     this.addEvents({
22839         /**
22840          * @event expand
22841          * Fires when the dropdown list is expanded
22842              * @param {Roo.form.ComboBox} combo This combo box
22843              */
22844         'expand' : true,
22845         /**
22846          * @event collapse
22847          * Fires when the dropdown list is collapsed
22848              * @param {Roo.form.ComboBox} combo This combo box
22849              */
22850         'collapse' : true,
22851         /**
22852          * @event beforeselect
22853          * Fires before a list item is selected. Return false to cancel the selection.
22854              * @param {Roo.form.ComboBox} combo This combo box
22855              * @param {Roo.data.Record} record The data record returned from the underlying store
22856              * @param {Number} index The index of the selected item in the dropdown list
22857              */
22858         'beforeselect' : true,
22859         /**
22860          * @event select
22861          * Fires when a list item is selected
22862              * @param {Roo.form.ComboBox} combo This combo box
22863              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22864              * @param {Number} index The index of the selected item in the dropdown list
22865              */
22866         'select' : true,
22867         /**
22868          * @event beforequery
22869          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22870          * The event object passed has these properties:
22871              * @param {Roo.form.ComboBox} combo This combo box
22872              * @param {String} query The query
22873              * @param {Boolean} forceAll true to force "all" query
22874              * @param {Boolean} cancel true to cancel the query
22875              * @param {Object} e The query event object
22876              */
22877         'beforequery': true,
22878          /**
22879          * @event add
22880          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22881              * @param {Roo.form.ComboBox} combo This combo box
22882              */
22883         'add' : true,
22884         /**
22885          * @event edit
22886          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22887              * @param {Roo.form.ComboBox} combo This combo box
22888              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22889              */
22890         'edit' : true
22891         
22892         
22893     });
22894     if(this.transform){
22895         this.allowDomMove = false;
22896         var s = Roo.getDom(this.transform);
22897         if(!this.hiddenName){
22898             this.hiddenName = s.name;
22899         }
22900         if(!this.store){
22901             this.mode = 'local';
22902             var d = [], opts = s.options;
22903             for(var i = 0, len = opts.length;i < len; i++){
22904                 var o = opts[i];
22905                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22906                 if(o.selected) {
22907                     this.value = value;
22908                 }
22909                 d.push([value, o.text]);
22910             }
22911             this.store = new Roo.data.SimpleStore({
22912                 'id': 0,
22913                 fields: ['value', 'text'],
22914                 data : d
22915             });
22916             this.valueField = 'value';
22917             this.displayField = 'text';
22918         }
22919         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22920         if(!this.lazyRender){
22921             this.target = true;
22922             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22923             s.parentNode.removeChild(s); // remove it
22924             this.render(this.el.parentNode);
22925         }else{
22926             s.parentNode.removeChild(s); // remove it
22927         }
22928
22929     }
22930     if (this.store) {
22931         this.store = Roo.factory(this.store, Roo.data);
22932     }
22933     
22934     this.selectedIndex = -1;
22935     if(this.mode == 'local'){
22936         if(config.queryDelay === undefined){
22937             this.queryDelay = 10;
22938         }
22939         if(config.minChars === undefined){
22940             this.minChars = 0;
22941         }
22942     }
22943 };
22944
22945 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22946     /**
22947      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22948      */
22949     /**
22950      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22951      * rendering into an Roo.Editor, defaults to false)
22952      */
22953     /**
22954      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
22955      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
22956      */
22957     /**
22958      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
22959      */
22960     /**
22961      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
22962      * the dropdown list (defaults to undefined, with no header element)
22963      */
22964
22965      /**
22966      * @cfg {String/Roo.Template} tpl The template to use to render the output
22967      */
22968      
22969     // private
22970     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
22971     /**
22972      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
22973      */
22974     listWidth: undefined,
22975     /**
22976      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
22977      * mode = 'remote' or 'text' if mode = 'local')
22978      */
22979     displayField: undefined,
22980     /**
22981      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
22982      * mode = 'remote' or 'value' if mode = 'local'). 
22983      * Note: use of a valueField requires the user make a selection
22984      * in order for a value to be mapped.
22985      */
22986     valueField: undefined,
22987     
22988     
22989     /**
22990      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
22991      * field's data value (defaults to the underlying DOM element's name)
22992      */
22993     hiddenName: undefined,
22994     /**
22995      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
22996      */
22997     listClass: '',
22998     /**
22999      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
23000      */
23001     selectedClass: 'x-combo-selected',
23002     /**
23003      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
23004      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
23005      * which displays a downward arrow icon).
23006      */
23007     triggerClass : 'x-form-arrow-trigger',
23008     /**
23009      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
23010      */
23011     shadow:'sides',
23012     /**
23013      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
23014      * anchor positions (defaults to 'tl-bl')
23015      */
23016     listAlign: 'tl-bl?',
23017     /**
23018      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
23019      */
23020     maxHeight: 300,
23021     /**
23022      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
23023      * query specified by the allQuery config option (defaults to 'query')
23024      */
23025     triggerAction: 'query',
23026     /**
23027      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
23028      * (defaults to 4, does not apply if editable = false)
23029      */
23030     minChars : 4,
23031     /**
23032      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
23033      * delay (typeAheadDelay) if it matches a known value (defaults to false)
23034      */
23035     typeAhead: false,
23036     /**
23037      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
23038      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
23039      */
23040     queryDelay: 500,
23041     /**
23042      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
23043      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
23044      */
23045     pageSize: 0,
23046     /**
23047      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
23048      * when editable = true (defaults to false)
23049      */
23050     selectOnFocus:false,
23051     /**
23052      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23053      */
23054     queryParam: 'query',
23055     /**
23056      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
23057      * when mode = 'remote' (defaults to 'Loading...')
23058      */
23059     loadingText: 'Loading...',
23060     /**
23061      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23062      */
23063     resizable: false,
23064     /**
23065      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23066      */
23067     handleHeight : 8,
23068     /**
23069      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23070      * traditional select (defaults to true)
23071      */
23072     editable: true,
23073     /**
23074      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23075      */
23076     allQuery: '',
23077     /**
23078      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23079      */
23080     mode: 'remote',
23081     /**
23082      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23083      * listWidth has a higher value)
23084      */
23085     minListWidth : 70,
23086     /**
23087      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23088      * allow the user to set arbitrary text into the field (defaults to false)
23089      */
23090     forceSelection:false,
23091     /**
23092      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23093      * if typeAhead = true (defaults to 250)
23094      */
23095     typeAheadDelay : 250,
23096     /**
23097      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23098      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23099      */
23100     valueNotFoundText : undefined,
23101     /**
23102      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23103      */
23104     blockFocus : false,
23105     
23106     /**
23107      * @cfg {Boolean} disableClear Disable showing of clear button.
23108      */
23109     disableClear : false,
23110     /**
23111      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
23112      */
23113     alwaysQuery : false,
23114     
23115     //private
23116     addicon : false,
23117     editicon: false,
23118     
23119     // element that contains real text value.. (when hidden is used..)
23120      
23121     // private
23122     onRender : function(ct, position){
23123         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23124         if(this.hiddenName){
23125             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
23126                     'before', true);
23127             this.hiddenField.value =
23128                 this.hiddenValue !== undefined ? this.hiddenValue :
23129                 this.value !== undefined ? this.value : '';
23130
23131             // prevent input submission
23132             this.el.dom.removeAttribute('name');
23133              
23134              
23135         }
23136         if(Roo.isGecko){
23137             this.el.dom.setAttribute('autocomplete', 'off');
23138         }
23139
23140         var cls = 'x-combo-list';
23141
23142         this.list = new Roo.Layer({
23143             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23144         });
23145
23146         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23147         this.list.setWidth(lw);
23148         this.list.swallowEvent('mousewheel');
23149         this.assetHeight = 0;
23150
23151         if(this.title){
23152             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23153             this.assetHeight += this.header.getHeight();
23154         }
23155
23156         this.innerList = this.list.createChild({cls:cls+'-inner'});
23157         this.innerList.on('mouseover', this.onViewOver, this);
23158         this.innerList.on('mousemove', this.onViewMove, this);
23159         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23160         
23161         if(this.allowBlank && !this.pageSize && !this.disableClear){
23162             this.footer = this.list.createChild({cls:cls+'-ft'});
23163             this.pageTb = new Roo.Toolbar(this.footer);
23164            
23165         }
23166         if(this.pageSize){
23167             this.footer = this.list.createChild({cls:cls+'-ft'});
23168             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23169                     {pageSize: this.pageSize});
23170             
23171         }
23172         
23173         if (this.pageTb && this.allowBlank && !this.disableClear) {
23174             var _this = this;
23175             this.pageTb.add(new Roo.Toolbar.Fill(), {
23176                 cls: 'x-btn-icon x-btn-clear',
23177                 text: '&#160;',
23178                 handler: function()
23179                 {
23180                     _this.collapse();
23181                     _this.clearValue();
23182                     _this.onSelect(false, -1);
23183                 }
23184             });
23185         }
23186         if (this.footer) {
23187             this.assetHeight += this.footer.getHeight();
23188         }
23189         
23190
23191         if(!this.tpl){
23192             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23193         }
23194
23195         this.view = new Roo.View(this.innerList, this.tpl, {
23196             singleSelect:true, store: this.store, selectedClass: this.selectedClass
23197         });
23198
23199         this.view.on('click', this.onViewClick, this);
23200
23201         this.store.on('beforeload', this.onBeforeLoad, this);
23202         this.store.on('load', this.onLoad, this);
23203         this.store.on('loadexception', this.onLoadException, this);
23204
23205         if(this.resizable){
23206             this.resizer = new Roo.Resizable(this.list,  {
23207                pinned:true, handles:'se'
23208             });
23209             this.resizer.on('resize', function(r, w, h){
23210                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23211                 this.listWidth = w;
23212                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23213                 this.restrictHeight();
23214             }, this);
23215             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23216         }
23217         if(!this.editable){
23218             this.editable = true;
23219             this.setEditable(false);
23220         }  
23221         
23222         
23223         if (typeof(this.events.add.listeners) != 'undefined') {
23224             
23225             this.addicon = this.wrap.createChild(
23226                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23227        
23228             this.addicon.on('click', function(e) {
23229                 this.fireEvent('add', this);
23230             }, this);
23231         }
23232         if (typeof(this.events.edit.listeners) != 'undefined') {
23233             
23234             this.editicon = this.wrap.createChild(
23235                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23236             if (this.addicon) {
23237                 this.editicon.setStyle('margin-left', '40px');
23238             }
23239             this.editicon.on('click', function(e) {
23240                 
23241                 // we fire even  if inothing is selected..
23242                 this.fireEvent('edit', this, this.lastData );
23243                 
23244             }, this);
23245         }
23246         
23247         
23248         
23249     },
23250
23251     // private
23252     initEvents : function(){
23253         Roo.form.ComboBox.superclass.initEvents.call(this);
23254
23255         this.keyNav = new Roo.KeyNav(this.el, {
23256             "up" : function(e){
23257                 this.inKeyMode = true;
23258                 this.selectPrev();
23259             },
23260
23261             "down" : function(e){
23262                 if(!this.isExpanded()){
23263                     this.onTriggerClick();
23264                 }else{
23265                     this.inKeyMode = true;
23266                     this.selectNext();
23267                 }
23268             },
23269
23270             "enter" : function(e){
23271                 this.onViewClick();
23272                 //return true;
23273             },
23274
23275             "esc" : function(e){
23276                 this.collapse();
23277             },
23278
23279             "tab" : function(e){
23280                 this.onViewClick(false);
23281                 this.fireEvent("specialkey", this, e);
23282                 return true;
23283             },
23284
23285             scope : this,
23286
23287             doRelay : function(foo, bar, hname){
23288                 if(hname == 'down' || this.scope.isExpanded()){
23289                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23290                 }
23291                 return true;
23292             },
23293
23294             forceKeyDown: true
23295         });
23296         this.queryDelay = Math.max(this.queryDelay || 10,
23297                 this.mode == 'local' ? 10 : 250);
23298         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23299         if(this.typeAhead){
23300             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23301         }
23302         if(this.editable !== false){
23303             this.el.on("keyup", this.onKeyUp, this);
23304         }
23305         if(this.forceSelection){
23306             this.on('blur', this.doForce, this);
23307         }
23308     },
23309
23310     onDestroy : function(){
23311         if(this.view){
23312             this.view.setStore(null);
23313             this.view.el.removeAllListeners();
23314             this.view.el.remove();
23315             this.view.purgeListeners();
23316         }
23317         if(this.list){
23318             this.list.destroy();
23319         }
23320         if(this.store){
23321             this.store.un('beforeload', this.onBeforeLoad, this);
23322             this.store.un('load', this.onLoad, this);
23323             this.store.un('loadexception', this.onLoadException, this);
23324         }
23325         Roo.form.ComboBox.superclass.onDestroy.call(this);
23326     },
23327
23328     // private
23329     fireKey : function(e){
23330         if(e.isNavKeyPress() && !this.list.isVisible()){
23331             this.fireEvent("specialkey", this, e);
23332         }
23333     },
23334
23335     // private
23336     onResize: function(w, h){
23337         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23338         
23339         if(typeof w != 'number'){
23340             // we do not handle it!?!?
23341             return;
23342         }
23343         var tw = this.trigger.getWidth();
23344         tw += this.addicon ? this.addicon.getWidth() : 0;
23345         tw += this.editicon ? this.editicon.getWidth() : 0;
23346         var x = w - tw;
23347         this.el.setWidth( this.adjustWidth('input', x));
23348             
23349         this.trigger.setStyle('left', x+'px');
23350         
23351         if(this.list && this.listWidth === undefined){
23352             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23353             this.list.setWidth(lw);
23354             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23355         }
23356         
23357     
23358         
23359     },
23360
23361     /**
23362      * Allow or prevent the user from directly editing the field text.  If false is passed,
23363      * the user will only be able to select from the items defined in the dropdown list.  This method
23364      * is the runtime equivalent of setting the 'editable' config option at config time.
23365      * @param {Boolean} value True to allow the user to directly edit the field text
23366      */
23367     setEditable : function(value){
23368         if(value == this.editable){
23369             return;
23370         }
23371         this.editable = value;
23372         if(!value){
23373             this.el.dom.setAttribute('readOnly', true);
23374             this.el.on('mousedown', this.onTriggerClick,  this);
23375             this.el.addClass('x-combo-noedit');
23376         }else{
23377             this.el.dom.setAttribute('readOnly', false);
23378             this.el.un('mousedown', this.onTriggerClick,  this);
23379             this.el.removeClass('x-combo-noedit');
23380         }
23381     },
23382
23383     // private
23384     onBeforeLoad : function(){
23385         if(!this.hasFocus){
23386             return;
23387         }
23388         this.innerList.update(this.loadingText ?
23389                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23390         this.restrictHeight();
23391         this.selectedIndex = -1;
23392     },
23393
23394     // private
23395     onLoad : function(){
23396         if(!this.hasFocus){
23397             return;
23398         }
23399         if(this.store.getCount() > 0){
23400             this.expand();
23401             this.restrictHeight();
23402             if(this.lastQuery == this.allQuery){
23403                 if(this.editable){
23404                     this.el.dom.select();
23405                 }
23406                 if(!this.selectByValue(this.value, true)){
23407                     this.select(0, true);
23408                 }
23409             }else{
23410                 this.selectNext();
23411                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23412                     this.taTask.delay(this.typeAheadDelay);
23413                 }
23414             }
23415         }else{
23416             this.onEmptyResults();
23417         }
23418         //this.el.focus();
23419     },
23420     // private
23421     onLoadException : function()
23422     {
23423         this.collapse();
23424         Roo.log(this.store.reader.jsonData);
23425         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
23426             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
23427         }
23428         
23429         
23430     },
23431     // private
23432     onTypeAhead : function(){
23433         if(this.store.getCount() > 0){
23434             var r = this.store.getAt(0);
23435             var newValue = r.data[this.displayField];
23436             var len = newValue.length;
23437             var selStart = this.getRawValue().length;
23438             if(selStart != len){
23439                 this.setRawValue(newValue);
23440                 this.selectText(selStart, newValue.length);
23441             }
23442         }
23443     },
23444
23445     // private
23446     onSelect : function(record, index){
23447         if(this.fireEvent('beforeselect', this, record, index) !== false){
23448             this.setFromData(index > -1 ? record.data : false);
23449             this.collapse();
23450             this.fireEvent('select', this, record, index);
23451         }
23452     },
23453
23454     /**
23455      * Returns the currently selected field value or empty string if no value is set.
23456      * @return {String} value The selected value
23457      */
23458     getValue : function(){
23459         if(this.valueField){
23460             return typeof this.value != 'undefined' ? this.value : '';
23461         }else{
23462             return Roo.form.ComboBox.superclass.getValue.call(this);
23463         }
23464     },
23465
23466     /**
23467      * Clears any text/value currently set in the field
23468      */
23469     clearValue : function(){
23470         if(this.hiddenField){
23471             this.hiddenField.value = '';
23472         }
23473         this.value = '';
23474         this.setRawValue('');
23475         this.lastSelectionText = '';
23476         this.applyEmptyText();
23477     },
23478
23479     /**
23480      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23481      * will be displayed in the field.  If the value does not match the data value of an existing item,
23482      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23483      * Otherwise the field will be blank (although the value will still be set).
23484      * @param {String} value The value to match
23485      */
23486     setValue : function(v){
23487         var text = v;
23488         if(this.valueField){
23489             var r = this.findRecord(this.valueField, v);
23490             if(r){
23491                 text = r.data[this.displayField];
23492             }else if(this.valueNotFoundText !== undefined){
23493                 text = this.valueNotFoundText;
23494             }
23495         }
23496         this.lastSelectionText = text;
23497         if(this.hiddenField){
23498             this.hiddenField.value = v;
23499         }
23500         Roo.form.ComboBox.superclass.setValue.call(this, text);
23501         this.value = v;
23502     },
23503     /**
23504      * @property {Object} the last set data for the element
23505      */
23506     
23507     lastData : false,
23508     /**
23509      * Sets the value of the field based on a object which is related to the record format for the store.
23510      * @param {Object} value the value to set as. or false on reset?
23511      */
23512     setFromData : function(o){
23513         var dv = ''; // display value
23514         var vv = ''; // value value..
23515         this.lastData = o;
23516         if (this.displayField) {
23517             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23518         } else {
23519             // this is an error condition!!!
23520             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23521         }
23522         
23523         if(this.valueField){
23524             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23525         }
23526         if(this.hiddenField){
23527             this.hiddenField.value = vv;
23528             
23529             this.lastSelectionText = dv;
23530             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23531             this.value = vv;
23532             return;
23533         }
23534         // no hidden field.. - we store the value in 'value', but still display
23535         // display field!!!!
23536         this.lastSelectionText = dv;
23537         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23538         this.value = vv;
23539         
23540         
23541     },
23542     // private
23543     reset : function(){
23544         // overridden so that last data is reset..
23545         this.setValue(this.originalValue);
23546         this.clearInvalid();
23547         this.lastData = false;
23548     },
23549     // private
23550     findRecord : function(prop, value){
23551         var record;
23552         if(this.store.getCount() > 0){
23553             this.store.each(function(r){
23554                 if(r.data[prop] == value){
23555                     record = r;
23556                     return false;
23557                 }
23558                 return true;
23559             });
23560         }
23561         return record;
23562     },
23563     
23564     getName: function()
23565     {
23566         // returns hidden if it's set..
23567         if (!this.rendered) {return ''};
23568         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
23569         
23570     },
23571     // private
23572     onViewMove : function(e, t){
23573         this.inKeyMode = false;
23574     },
23575
23576     // private
23577     onViewOver : function(e, t){
23578         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23579             return;
23580         }
23581         var item = this.view.findItemFromChild(t);
23582         if(item){
23583             var index = this.view.indexOf(item);
23584             this.select(index, false);
23585         }
23586     },
23587
23588     // private
23589     onViewClick : function(doFocus)
23590     {
23591         var index = this.view.getSelectedIndexes()[0];
23592         var r = this.store.getAt(index);
23593         if(r){
23594             this.onSelect(r, index);
23595         }
23596         if(doFocus !== false && !this.blockFocus){
23597             this.el.focus();
23598         }
23599     },
23600
23601     // private
23602     restrictHeight : function(){
23603         this.innerList.dom.style.height = '';
23604         var inner = this.innerList.dom;
23605         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23606         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23607         this.list.beginUpdate();
23608         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23609         this.list.alignTo(this.el, this.listAlign);
23610         this.list.endUpdate();
23611     },
23612
23613     // private
23614     onEmptyResults : function(){
23615         this.collapse();
23616     },
23617
23618     /**
23619      * Returns true if the dropdown list is expanded, else false.
23620      */
23621     isExpanded : function(){
23622         return this.list.isVisible();
23623     },
23624
23625     /**
23626      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23627      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23628      * @param {String} value The data value of the item to select
23629      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23630      * selected item if it is not currently in view (defaults to true)
23631      * @return {Boolean} True if the value matched an item in the list, else false
23632      */
23633     selectByValue : function(v, scrollIntoView){
23634         if(v !== undefined && v !== null){
23635             var r = this.findRecord(this.valueField || this.displayField, v);
23636             if(r){
23637                 this.select(this.store.indexOf(r), scrollIntoView);
23638                 return true;
23639             }
23640         }
23641         return false;
23642     },
23643
23644     /**
23645      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23646      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23647      * @param {Number} index The zero-based index of the list item to select
23648      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23649      * selected item if it is not currently in view (defaults to true)
23650      */
23651     select : function(index, scrollIntoView){
23652         this.selectedIndex = index;
23653         this.view.select(index);
23654         if(scrollIntoView !== false){
23655             var el = this.view.getNode(index);
23656             if(el){
23657                 this.innerList.scrollChildIntoView(el, false);
23658             }
23659         }
23660     },
23661
23662     // private
23663     selectNext : function(){
23664         var ct = this.store.getCount();
23665         if(ct > 0){
23666             if(this.selectedIndex == -1){
23667                 this.select(0);
23668             }else if(this.selectedIndex < ct-1){
23669                 this.select(this.selectedIndex+1);
23670             }
23671         }
23672     },
23673
23674     // private
23675     selectPrev : function(){
23676         var ct = this.store.getCount();
23677         if(ct > 0){
23678             if(this.selectedIndex == -1){
23679                 this.select(0);
23680             }else if(this.selectedIndex != 0){
23681                 this.select(this.selectedIndex-1);
23682             }
23683         }
23684     },
23685
23686     // private
23687     onKeyUp : function(e){
23688         if(this.editable !== false && !e.isSpecialKey()){
23689             this.lastKey = e.getKey();
23690             this.dqTask.delay(this.queryDelay);
23691         }
23692     },
23693
23694     // private
23695     validateBlur : function(){
23696         return !this.list || !this.list.isVisible();   
23697     },
23698
23699     // private
23700     initQuery : function(){
23701         this.doQuery(this.getRawValue());
23702     },
23703
23704     // private
23705     doForce : function(){
23706         if(this.el.dom.value.length > 0){
23707             this.el.dom.value =
23708                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23709             this.applyEmptyText();
23710         }
23711     },
23712
23713     /**
23714      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23715      * query allowing the query action to be canceled if needed.
23716      * @param {String} query The SQL query to execute
23717      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23718      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23719      * saved in the current store (defaults to false)
23720      */
23721     doQuery : function(q, forceAll){
23722         if(q === undefined || q === null){
23723             q = '';
23724         }
23725         var qe = {
23726             query: q,
23727             forceAll: forceAll,
23728             combo: this,
23729             cancel:false
23730         };
23731         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23732             return false;
23733         }
23734         q = qe.query;
23735         forceAll = qe.forceAll;
23736         if(forceAll === true || (q.length >= this.minChars)){
23737             if(this.lastQuery != q || this.alwaysQuery){
23738                 this.lastQuery = q;
23739                 if(this.mode == 'local'){
23740                     this.selectedIndex = -1;
23741                     if(forceAll){
23742                         this.store.clearFilter();
23743                     }else{
23744                         this.store.filter(this.displayField, q);
23745                     }
23746                     this.onLoad();
23747                 }else{
23748                     this.store.baseParams[this.queryParam] = q;
23749                     this.store.load({
23750                         params: this.getParams(q)
23751                     });
23752                     this.expand();
23753                 }
23754             }else{
23755                 this.selectedIndex = -1;
23756                 this.onLoad();   
23757             }
23758         }
23759     },
23760
23761     // private
23762     getParams : function(q){
23763         var p = {};
23764         //p[this.queryParam] = q;
23765         if(this.pageSize){
23766             p.start = 0;
23767             p.limit = this.pageSize;
23768         }
23769         return p;
23770     },
23771
23772     /**
23773      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23774      */
23775     collapse : function(){
23776         if(!this.isExpanded()){
23777             return;
23778         }
23779         this.list.hide();
23780         Roo.get(document).un('mousedown', this.collapseIf, this);
23781         Roo.get(document).un('mousewheel', this.collapseIf, this);
23782         if (!this.editable) {
23783             Roo.get(document).un('keydown', this.listKeyPress, this);
23784         }
23785         this.fireEvent('collapse', this);
23786     },
23787
23788     // private
23789     collapseIf : function(e){
23790         if(!e.within(this.wrap) && !e.within(this.list)){
23791             this.collapse();
23792         }
23793     },
23794
23795     /**
23796      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23797      */
23798     expand : function(){
23799         if(this.isExpanded() || !this.hasFocus){
23800             return;
23801         }
23802         this.list.alignTo(this.el, this.listAlign);
23803         this.list.show();
23804         Roo.get(document).on('mousedown', this.collapseIf, this);
23805         Roo.get(document).on('mousewheel', this.collapseIf, this);
23806         if (!this.editable) {
23807             Roo.get(document).on('keydown', this.listKeyPress, this);
23808         }
23809         
23810         this.fireEvent('expand', this);
23811     },
23812
23813     // private
23814     // Implements the default empty TriggerField.onTriggerClick function
23815     onTriggerClick : function(){
23816         if(this.disabled){
23817             return;
23818         }
23819         if(this.isExpanded()){
23820             this.collapse();
23821             if (!this.blockFocus) {
23822                 this.el.focus();
23823             }
23824             
23825         }else {
23826             this.hasFocus = true;
23827             if(this.triggerAction == 'all') {
23828                 this.doQuery(this.allQuery, true);
23829             } else {
23830                 this.doQuery(this.getRawValue());
23831             }
23832             if (!this.blockFocus) {
23833                 this.el.focus();
23834             }
23835         }
23836     },
23837     listKeyPress : function(e)
23838     {
23839         //Roo.log('listkeypress');
23840         // scroll to first matching element based on key pres..
23841         if (e.isSpecialKey()) {
23842             return false;
23843         }
23844         var k = String.fromCharCode(e.getKey()).toUpperCase();
23845         //Roo.log(k);
23846         var match  = false;
23847         var csel = this.view.getSelectedNodes();
23848         var cselitem = false;
23849         if (csel.length) {
23850             var ix = this.view.indexOf(csel[0]);
23851             cselitem  = this.store.getAt(ix);
23852             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23853                 cselitem = false;
23854             }
23855             
23856         }
23857         
23858         this.store.each(function(v) { 
23859             if (cselitem) {
23860                 // start at existing selection.
23861                 if (cselitem.id == v.id) {
23862                     cselitem = false;
23863                 }
23864                 return;
23865             }
23866                 
23867             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23868                 match = this.store.indexOf(v);
23869                 return false;
23870             }
23871         }, this);
23872         
23873         if (match === false) {
23874             return true; // no more action?
23875         }
23876         // scroll to?
23877         this.view.select(match);
23878         var sn = Roo.get(this.view.getSelectedNodes()[0])
23879         sn.scrollIntoView(sn.dom.parentNode, false);
23880     }
23881
23882     /** 
23883     * @cfg {Boolean} grow 
23884     * @hide 
23885     */
23886     /** 
23887     * @cfg {Number} growMin 
23888     * @hide 
23889     */
23890     /** 
23891     * @cfg {Number} growMax 
23892     * @hide 
23893     */
23894     /**
23895      * @hide
23896      * @method autoSize
23897      */
23898 });/*
23899  * Based on:
23900  * Ext JS Library 1.1.1
23901  * Copyright(c) 2006-2007, Ext JS, LLC.
23902  *
23903  * Originally Released Under LGPL - original licence link has changed is not relivant.
23904  *
23905  * Fork - LGPL
23906  * <script type="text/javascript">
23907  */
23908 /**
23909  * @class Roo.form.Checkbox
23910  * @extends Roo.form.Field
23911  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
23912  * @constructor
23913  * Creates a new Checkbox
23914  * @param {Object} config Configuration options
23915  */
23916 Roo.form.Checkbox = function(config){
23917     Roo.form.Checkbox.superclass.constructor.call(this, config);
23918     this.addEvents({
23919         /**
23920          * @event check
23921          * Fires when the checkbox is checked or unchecked.
23922              * @param {Roo.form.Checkbox} this This checkbox
23923              * @param {Boolean} checked The new checked value
23924              */
23925         check : true
23926     });
23927 };
23928
23929 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
23930     /**
23931      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
23932      */
23933     focusClass : undefined,
23934     /**
23935      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
23936      */
23937     fieldClass: "x-form-field",
23938     /**
23939      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
23940      */
23941     checked: false,
23942     /**
23943      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
23944      * {tag: "input", type: "checkbox", autocomplete: "off"})
23945      */
23946     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
23947     /**
23948      * @cfg {String} boxLabel The text that appears beside the checkbox
23949      */
23950     boxLabel : "",
23951     /**
23952      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
23953      */  
23954     inputValue : '1',
23955     /**
23956      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23957      */
23958      valueOff: '0', // value when not checked..
23959
23960     actionMode : 'viewEl', 
23961     //
23962     // private
23963     itemCls : 'x-menu-check-item x-form-item',
23964     groupClass : 'x-menu-group-item',
23965     inputType : 'hidden',
23966     
23967     
23968     inSetChecked: false, // check that we are not calling self...
23969     
23970     inputElement: false, // real input element?
23971     basedOn: false, // ????
23972     
23973     isFormField: true, // not sure where this is needed!!!!
23974
23975     onResize : function(){
23976         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
23977         if(!this.boxLabel){
23978             this.el.alignTo(this.wrap, 'c-c');
23979         }
23980     },
23981
23982     initEvents : function(){
23983         Roo.form.Checkbox.superclass.initEvents.call(this);
23984         this.el.on("click", this.onClick,  this);
23985         this.el.on("change", this.onClick,  this);
23986     },
23987
23988
23989     getResizeEl : function(){
23990         return this.wrap;
23991     },
23992
23993     getPositionEl : function(){
23994         return this.wrap;
23995     },
23996
23997     // private
23998     onRender : function(ct, position){
23999         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24000         /*
24001         if(this.inputValue !== undefined){
24002             this.el.dom.value = this.inputValue;
24003         }
24004         */
24005         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24006         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24007         var viewEl = this.wrap.createChild({ 
24008             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24009         this.viewEl = viewEl;   
24010         this.wrap.on('click', this.onClick,  this); 
24011         
24012         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
24013         this.el.on('propertychange', this.setFromHidden,  this);  //ie
24014         
24015         
24016         
24017         if(this.boxLabel){
24018             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24019         //    viewEl.on('click', this.onClick,  this); 
24020         }
24021         //if(this.checked){
24022             this.setChecked(this.checked);
24023         //}else{
24024             //this.checked = this.el.dom;
24025         //}
24026
24027     },
24028
24029     // private
24030     initValue : Roo.emptyFn,
24031
24032     /**
24033      * Returns the checked state of the checkbox.
24034      * @return {Boolean} True if checked, else false
24035      */
24036     getValue : function(){
24037         if(this.el){
24038             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
24039         }
24040         return this.valueOff;
24041         
24042     },
24043
24044         // private
24045     onClick : function(){ 
24046         this.setChecked(!this.checked);
24047
24048         //if(this.el.dom.checked != this.checked){
24049         //    this.setValue(this.el.dom.checked);
24050        // }
24051     },
24052
24053     /**
24054      * Sets the checked state of the checkbox.
24055      * On is always based on a string comparison between inputValue and the param.
24056      * @param {Boolean/String} value - the value to set 
24057      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
24058      */
24059     setValue : function(v,suppressEvent){
24060         
24061         
24062         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
24063         //if(this.el && this.el.dom){
24064         //    this.el.dom.checked = this.checked;
24065         //    this.el.dom.defaultChecked = this.checked;
24066         //}
24067         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
24068         //this.fireEvent("check", this, this.checked);
24069     },
24070     // private..
24071     setChecked : function(state,suppressEvent)
24072     {
24073         if (this.inSetChecked) {
24074             this.checked = state;
24075             return;
24076         }
24077         
24078     
24079         if(this.wrap){
24080             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
24081         }
24082         this.checked = state;
24083         if(suppressEvent !== true){
24084             this.fireEvent('check', this, state);
24085         }
24086         this.inSetChecked = true;
24087         this.el.dom.value = state ? this.inputValue : this.valueOff;
24088         this.inSetChecked = false;
24089         
24090     },
24091     // handle setting of hidden value by some other method!!?!?
24092     setFromHidden: function()
24093     {
24094         if(!this.el){
24095             return;
24096         }
24097         //console.log("SET FROM HIDDEN");
24098         //alert('setFrom hidden');
24099         this.setValue(this.el.dom.value);
24100     },
24101     
24102     onDestroy : function()
24103     {
24104         if(this.viewEl){
24105             Roo.get(this.viewEl).remove();
24106         }
24107          
24108         Roo.form.Checkbox.superclass.onDestroy.call(this);
24109     }
24110
24111 });/*
24112  * Based on:
24113  * Ext JS Library 1.1.1
24114  * Copyright(c) 2006-2007, Ext JS, LLC.
24115  *
24116  * Originally Released Under LGPL - original licence link has changed is not relivant.
24117  *
24118  * Fork - LGPL
24119  * <script type="text/javascript">
24120  */
24121  
24122 /**
24123  * @class Roo.form.Radio
24124  * @extends Roo.form.Checkbox
24125  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
24126  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
24127  * @constructor
24128  * Creates a new Radio
24129  * @param {Object} config Configuration options
24130  */
24131 Roo.form.Radio = function(){
24132     Roo.form.Radio.superclass.constructor.apply(this, arguments);
24133 };
24134 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
24135     inputType: 'radio',
24136
24137     /**
24138      * If this radio is part of a group, it will return the selected value
24139      * @return {String}
24140      */
24141     getGroupValue : function(){
24142         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
24143     }
24144 });//<script type="text/javascript">
24145
24146 /*
24147  * Ext JS Library 1.1.1
24148  * Copyright(c) 2006-2007, Ext JS, LLC.
24149  * licensing@extjs.com
24150  * 
24151  * http://www.extjs.com/license
24152  */
24153  
24154  /*
24155   * 
24156   * Known bugs:
24157   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
24158   * - IE ? - no idea how much works there.
24159   * 
24160   * 
24161   * 
24162   */
24163  
24164
24165 /**
24166  * @class Ext.form.HtmlEditor
24167  * @extends Ext.form.Field
24168  * Provides a lightweight HTML Editor component.
24169  *
24170  * This has been tested on Fireforx / Chrome.. IE may not be so great..
24171  * 
24172  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
24173  * supported by this editor.</b><br/><br/>
24174  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
24175  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24176  */
24177 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
24178       /**
24179      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
24180      */
24181     toolbars : false,
24182     /**
24183      * @cfg {String} createLinkText The default text for the create link prompt
24184      */
24185     createLinkText : 'Please enter the URL for the link:',
24186     /**
24187      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
24188      */
24189     defaultLinkValue : 'http:/'+'/',
24190    
24191      /**
24192      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24193      *                        Roo.resizable.
24194      */
24195     resizable : false,
24196      /**
24197      * @cfg {Number} height (in pixels)
24198      */   
24199     height: 300,
24200    /**
24201      * @cfg {Number} width (in pixels)
24202      */   
24203     width: 500,
24204     
24205     /**
24206      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24207      * 
24208      */
24209     stylesheets: false,
24210     
24211     // id of frame..
24212     frameId: false,
24213     
24214     // private properties
24215     validationEvent : false,
24216     deferHeight: true,
24217     initialized : false,
24218     activated : false,
24219     sourceEditMode : false,
24220     onFocus : Roo.emptyFn,
24221     iframePad:3,
24222     hideMode:'offsets',
24223     
24224     defaultAutoCreate : { // modified by initCompnoent..
24225         tag: "textarea",
24226         style:"width:500px;height:300px;",
24227         autocomplete: "off"
24228     },
24229
24230     // private
24231     initComponent : function(){
24232         this.addEvents({
24233             /**
24234              * @event initialize
24235              * Fires when the editor is fully initialized (including the iframe)
24236              * @param {HtmlEditor} this
24237              */
24238             initialize: true,
24239             /**
24240              * @event activate
24241              * Fires when the editor is first receives the focus. Any insertion must wait
24242              * until after this event.
24243              * @param {HtmlEditor} this
24244              */
24245             activate: true,
24246              /**
24247              * @event beforesync
24248              * Fires before the textarea is updated with content from the editor iframe. Return false
24249              * to cancel the sync.
24250              * @param {HtmlEditor} this
24251              * @param {String} html
24252              */
24253             beforesync: true,
24254              /**
24255              * @event beforepush
24256              * Fires before the iframe editor is updated with content from the textarea. Return false
24257              * to cancel the push.
24258              * @param {HtmlEditor} this
24259              * @param {String} html
24260              */
24261             beforepush: true,
24262              /**
24263              * @event sync
24264              * Fires when the textarea is updated with content from the editor iframe.
24265              * @param {HtmlEditor} this
24266              * @param {String} html
24267              */
24268             sync: true,
24269              /**
24270              * @event push
24271              * Fires when the iframe editor is updated with content from the textarea.
24272              * @param {HtmlEditor} this
24273              * @param {String} html
24274              */
24275             push: true,
24276              /**
24277              * @event editmodechange
24278              * Fires when the editor switches edit modes
24279              * @param {HtmlEditor} this
24280              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
24281              */
24282             editmodechange: true,
24283             /**
24284              * @event editorevent
24285              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24286              * @param {HtmlEditor} this
24287              */
24288             editorevent: true
24289         });
24290         this.defaultAutoCreate =  {
24291             tag: "textarea",
24292             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
24293             autocomplete: "off"
24294         };
24295     },
24296
24297     /**
24298      * Protected method that will not generally be called directly. It
24299      * is called when the editor creates its toolbar. Override this method if you need to
24300      * add custom toolbar buttons.
24301      * @param {HtmlEditor} editor
24302      */
24303     createToolbar : function(editor){
24304         if (!editor.toolbars || !editor.toolbars.length) {
24305             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
24306         }
24307         
24308         for (var i =0 ; i < editor.toolbars.length;i++) {
24309             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
24310             editor.toolbars[i].init(editor);
24311         }
24312          
24313         
24314     },
24315
24316     /**
24317      * Protected method that will not generally be called directly. It
24318      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24319      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24320      */
24321     getDocMarkup : function(){
24322         // body styles..
24323         var st = '';
24324         if (this.stylesheets === false) {
24325             
24326             Roo.get(document.head).select('style').each(function(node) {
24327                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24328             });
24329             
24330             Roo.get(document.head).select('link').each(function(node) { 
24331                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24332             });
24333             
24334         } else if (!this.stylesheets.length) {
24335                 // simple..
24336                 st = '<style type="text/css">' +
24337                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24338                    '</style>';
24339         } else {
24340             Roo.each(this.stylesheets, function(s) {
24341                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
24342             });
24343             
24344         }
24345         
24346         st +=  '<style type="text/css">' +
24347             'IMG { cursor: pointer } ' +
24348         '</style>';
24349
24350         
24351         return '<html><head>' + st  +
24352             //<style type="text/css">' +
24353             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24354             //'</style>' +
24355             ' </head><body></body></html>';
24356     },
24357
24358     // private
24359     onRender : function(ct, position)
24360     {
24361         var _t = this;
24362         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
24363         this.el.dom.style.border = '0 none';
24364         this.el.dom.setAttribute('tabIndex', -1);
24365         this.el.addClass('x-hidden');
24366         if(Roo.isIE){ // fix IE 1px bogus margin
24367             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24368         }
24369         this.wrap = this.el.wrap({
24370             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24371         });
24372         
24373         if (this.resizable) {
24374             this.resizeEl = new Roo.Resizable(this.wrap, {
24375                 pinned : true,
24376                 wrap: true,
24377                 dynamic : true,
24378                 minHeight : this.height,
24379                 height: this.height,
24380                 handles : this.resizable,
24381                 width: this.width,
24382                 listeners : {
24383                     resize : function(r, w, h) {
24384                         _t.onResize(w,h); // -something
24385                     }
24386                 }
24387             });
24388             
24389         }
24390
24391         this.frameId = Roo.id();
24392         
24393         this.createToolbar(this);
24394         
24395       
24396         
24397         var iframe = this.wrap.createChild({
24398             tag: 'iframe',
24399             id: this.frameId,
24400             name: this.frameId,
24401             frameBorder : 'no',
24402             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24403         }, this.el
24404         );
24405         
24406        // console.log(iframe);
24407         //this.wrap.dom.appendChild(iframe);
24408
24409         this.iframe = iframe.dom;
24410
24411          this.assignDocWin();
24412         
24413         this.doc.designMode = 'on';
24414        
24415         this.doc.open();
24416         this.doc.write(this.getDocMarkup());
24417         this.doc.close();
24418
24419         
24420         var task = { // must defer to wait for browser to be ready
24421             run : function(){
24422                 //console.log("run task?" + this.doc.readyState);
24423                 this.assignDocWin();
24424                 if(this.doc.body || this.doc.readyState == 'complete'){
24425                     try {
24426                         this.doc.designMode="on";
24427                     } catch (e) {
24428                         return;
24429                     }
24430                     Roo.TaskMgr.stop(task);
24431                     this.initEditor.defer(10, this);
24432                 }
24433             },
24434             interval : 10,
24435             duration:10000,
24436             scope: this
24437         };
24438         Roo.TaskMgr.start(task);
24439
24440         if(!this.width){
24441             this.setSize(this.wrap.getSize());
24442         }
24443         if (this.resizeEl) {
24444             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
24445             // should trigger onReize..
24446         }
24447     },
24448
24449     // private
24450     onResize : function(w, h)
24451     {
24452         //Roo.log('resize: ' +w + ',' + h );
24453         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
24454         if(this.el && this.iframe){
24455             if(typeof w == 'number'){
24456                 var aw = w - this.wrap.getFrameWidth('lr');
24457                 this.el.setWidth(this.adjustWidth('textarea', aw));
24458                 this.iframe.style.width = aw + 'px';
24459             }
24460             if(typeof h == 'number'){
24461                 var tbh = 0;
24462                 for (var i =0; i < this.toolbars.length;i++) {
24463                     // fixme - ask toolbars for heights?
24464                     tbh += this.toolbars[i].tb.el.getHeight();
24465                     if (this.toolbars[i].footer) {
24466                         tbh += this.toolbars[i].footer.el.getHeight();
24467                     }
24468                 }
24469                 
24470                 
24471                 
24472                 
24473                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24474                 ah -= 5; // knock a few pixes off for look..
24475                 this.el.setHeight(this.adjustWidth('textarea', ah));
24476                 this.iframe.style.height = ah + 'px';
24477                 if(this.doc){
24478                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
24479                 }
24480             }
24481         }
24482     },
24483
24484     /**
24485      * Toggles the editor between standard and source edit mode.
24486      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24487      */
24488     toggleSourceEdit : function(sourceEditMode){
24489         
24490         this.sourceEditMode = sourceEditMode === true;
24491         
24492         if(this.sourceEditMode){
24493           
24494             this.syncValue();
24495             this.iframe.className = 'x-hidden';
24496             this.el.removeClass('x-hidden');
24497             this.el.dom.removeAttribute('tabIndex');
24498             this.el.focus();
24499         }else{
24500              
24501             this.pushValue();
24502             this.iframe.className = '';
24503             this.el.addClass('x-hidden');
24504             this.el.dom.setAttribute('tabIndex', -1);
24505             this.deferFocus();
24506         }
24507         this.setSize(this.wrap.getSize());
24508         this.fireEvent('editmodechange', this, this.sourceEditMode);
24509     },
24510
24511     // private used internally
24512     createLink : function(){
24513         var url = prompt(this.createLinkText, this.defaultLinkValue);
24514         if(url && url != 'http:/'+'/'){
24515             this.relayCmd('createlink', url);
24516         }
24517     },
24518
24519     // private (for BoxComponent)
24520     adjustSize : Roo.BoxComponent.prototype.adjustSize,
24521
24522     // private (for BoxComponent)
24523     getResizeEl : function(){
24524         return this.wrap;
24525     },
24526
24527     // private (for BoxComponent)
24528     getPositionEl : function(){
24529         return this.wrap;
24530     },
24531
24532     // private
24533     initEvents : function(){
24534         this.originalValue = this.getValue();
24535     },
24536
24537     /**
24538      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24539      * @method
24540      */
24541     markInvalid : Roo.emptyFn,
24542     /**
24543      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24544      * @method
24545      */
24546     clearInvalid : Roo.emptyFn,
24547
24548     setValue : function(v){
24549         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
24550         this.pushValue();
24551     },
24552
24553     /**
24554      * Protected method that will not generally be called directly. If you need/want
24555      * custom HTML cleanup, this is the method you should override.
24556      * @param {String} html The HTML to be cleaned
24557      * return {String} The cleaned HTML
24558      */
24559     cleanHtml : function(html){
24560         html = String(html);
24561         if(html.length > 5){
24562             if(Roo.isSafari){ // strip safari nonsense
24563                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24564             }
24565         }
24566         if(html == '&nbsp;'){
24567             html = '';
24568         }
24569         return html;
24570     },
24571
24572     /**
24573      * Protected method that will not generally be called directly. Syncs the contents
24574      * of the editor iframe with the textarea.
24575      */
24576     syncValue : function(){
24577         if(this.initialized){
24578             var bd = (this.doc.body || this.doc.documentElement);
24579             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24580             var html = bd.innerHTML;
24581             if(Roo.isSafari){
24582                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24583                 var m = bs.match(/text-align:(.*?);/i);
24584                 if(m && m[1]){
24585                     html = '<div style="'+m[0]+'">' + html + '</div>';
24586                 }
24587             }
24588             html = this.cleanHtml(html);
24589             // fix up the special chars..
24590             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
24591                 return "&#"+b.charCodeAt()+";" 
24592             });
24593             if(this.fireEvent('beforesync', this, html) !== false){
24594                 this.el.dom.value = html;
24595                 this.fireEvent('sync', this, html);
24596             }
24597         }
24598     },
24599
24600     /**
24601      * Protected method that will not generally be called directly. Pushes the value of the textarea
24602      * into the iframe editor.
24603      */
24604     pushValue : function(){
24605         if(this.initialized){
24606             var v = this.el.dom.value;
24607             if(v.length < 1){
24608                 v = '&#160;';
24609             }
24610             
24611             if(this.fireEvent('beforepush', this, v) !== false){
24612                 var d = (this.doc.body || this.doc.documentElement);
24613                 d.innerHTML = v;
24614                 this.cleanUpPaste();
24615                 this.el.dom.value = d.innerHTML;
24616                 this.fireEvent('push', this, v);
24617             }
24618         }
24619     },
24620
24621     // private
24622     deferFocus : function(){
24623         this.focus.defer(10, this);
24624     },
24625
24626     // doc'ed in Field
24627     focus : function(){
24628         if(this.win && !this.sourceEditMode){
24629             this.win.focus();
24630         }else{
24631             this.el.focus();
24632         }
24633     },
24634     
24635     assignDocWin: function()
24636     {
24637         var iframe = this.iframe;
24638         
24639          if(Roo.isIE){
24640             this.doc = iframe.contentWindow.document;
24641             this.win = iframe.contentWindow;
24642         } else {
24643             if (!Roo.get(this.frameId)) {
24644                 return;
24645             }
24646             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24647             this.win = Roo.get(this.frameId).dom.contentWindow;
24648         }
24649     },
24650     
24651     // private
24652     initEditor : function(){
24653         //console.log("INIT EDITOR");
24654         this.assignDocWin();
24655         
24656         
24657         
24658         this.doc.designMode="on";
24659         this.doc.open();
24660         this.doc.write(this.getDocMarkup());
24661         this.doc.close();
24662         
24663         var dbody = (this.doc.body || this.doc.documentElement);
24664         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24665         // this copies styles from the containing element into thsi one..
24666         // not sure why we need all of this..
24667         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24668         ss['background-attachment'] = 'fixed'; // w3c
24669         dbody.bgProperties = 'fixed'; // ie
24670         Roo.DomHelper.applyStyles(dbody, ss);
24671         Roo.EventManager.on(this.doc, {
24672             //'mousedown': this.onEditorEvent,
24673             'mouseup': this.onEditorEvent,
24674             'dblclick': this.onEditorEvent,
24675             'click': this.onEditorEvent,
24676             'keyup': this.onEditorEvent,
24677             buffer:100,
24678             scope: this
24679         });
24680         if(Roo.isGecko){
24681             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24682         }
24683         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24684             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24685         }
24686         this.initialized = true;
24687
24688         this.fireEvent('initialize', this);
24689         this.pushValue();
24690     },
24691
24692     // private
24693     onDestroy : function(){
24694         
24695         
24696         
24697         if(this.rendered){
24698             
24699             for (var i =0; i < this.toolbars.length;i++) {
24700                 // fixme - ask toolbars for heights?
24701                 this.toolbars[i].onDestroy();
24702             }
24703             
24704             this.wrap.dom.innerHTML = '';
24705             this.wrap.remove();
24706         }
24707     },
24708
24709     // private
24710     onFirstFocus : function(){
24711         
24712         this.assignDocWin();
24713         
24714         
24715         this.activated = true;
24716         for (var i =0; i < this.toolbars.length;i++) {
24717             this.toolbars[i].onFirstFocus();
24718         }
24719        
24720         if(Roo.isGecko){ // prevent silly gecko errors
24721             this.win.focus();
24722             var s = this.win.getSelection();
24723             if(!s.focusNode || s.focusNode.nodeType != 3){
24724                 var r = s.getRangeAt(0);
24725                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24726                 r.collapse(true);
24727                 this.deferFocus();
24728             }
24729             try{
24730                 this.execCmd('useCSS', true);
24731                 this.execCmd('styleWithCSS', false);
24732             }catch(e){}
24733         }
24734         this.fireEvent('activate', this);
24735     },
24736
24737     // private
24738     adjustFont: function(btn){
24739         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24740         //if(Roo.isSafari){ // safari
24741         //    adjust *= 2;
24742        // }
24743         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24744         if(Roo.isSafari){ // safari
24745             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24746             v =  (v < 10) ? 10 : v;
24747             v =  (v > 48) ? 48 : v;
24748             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24749             
24750         }
24751         
24752         
24753         v = Math.max(1, v+adjust);
24754         
24755         this.execCmd('FontSize', v  );
24756     },
24757
24758     onEditorEvent : function(e){
24759         this.fireEvent('editorevent', this, e);
24760       //  this.updateToolbar();
24761         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24762     },
24763
24764     insertTag : function(tg)
24765     {
24766         // could be a bit smarter... -> wrap the current selected tRoo..
24767         
24768         this.execCmd("formatblock",   tg);
24769         
24770     },
24771     
24772     insertText : function(txt)
24773     {
24774         
24775         
24776         range = this.createRange();
24777         range.deleteContents();
24778                //alert(Sender.getAttribute('label'));
24779                
24780         range.insertNode(this.doc.createTextNode(txt));
24781     } ,
24782     
24783     // private
24784     relayBtnCmd : function(btn){
24785         this.relayCmd(btn.cmd);
24786     },
24787
24788     /**
24789      * Executes a Midas editor command on the editor document and performs necessary focus and
24790      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24791      * @param {String} cmd The Midas command
24792      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24793      */
24794     relayCmd : function(cmd, value){
24795         this.win.focus();
24796         this.execCmd(cmd, value);
24797         this.fireEvent('editorevent', this);
24798         //this.updateToolbar();
24799         this.deferFocus();
24800     },
24801
24802     /**
24803      * Executes a Midas editor command directly on the editor document.
24804      * For visual commands, you should use {@link #relayCmd} instead.
24805      * <b>This should only be called after the editor is initialized.</b>
24806      * @param {String} cmd The Midas command
24807      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24808      */
24809     execCmd : function(cmd, value){
24810         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24811         this.syncValue();
24812     },
24813  
24814  
24815    
24816     /**
24817      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24818      * to insert tRoo.
24819      * @param {String} text | dom node.. 
24820      */
24821     insertAtCursor : function(text)
24822     {
24823         
24824         
24825         
24826         if(!this.activated){
24827             return;
24828         }
24829         /*
24830         if(Roo.isIE){
24831             this.win.focus();
24832             var r = this.doc.selection.createRange();
24833             if(r){
24834                 r.collapse(true);
24835                 r.pasteHTML(text);
24836                 this.syncValue();
24837                 this.deferFocus();
24838             
24839             }
24840             return;
24841         }
24842         */
24843         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24844             this.win.focus();
24845             
24846             
24847             // from jquery ui (MIT licenced)
24848             var range, node;
24849             var win = this.win;
24850             
24851             if (win.getSelection && win.getSelection().getRangeAt) {
24852                 range = win.getSelection().getRangeAt(0);
24853                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24854                 range.insertNode(node);
24855             } else if (win.document.selection && win.document.selection.createRange) {
24856                 // no firefox support
24857                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24858                 win.document.selection.createRange().pasteHTML(txt);
24859             } else {
24860                 // no firefox support
24861                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24862                 this.execCmd('InsertHTML', txt);
24863             } 
24864             
24865             this.syncValue();
24866             
24867             this.deferFocus();
24868         }
24869     },
24870  // private
24871     mozKeyPress : function(e){
24872         if(e.ctrlKey){
24873             var c = e.getCharCode(), cmd;
24874           
24875             if(c > 0){
24876                 c = String.fromCharCode(c).toLowerCase();
24877                 switch(c){
24878                     case 'b':
24879                         cmd = 'bold';
24880                         break;
24881                     case 'i':
24882                         cmd = 'italic';
24883                         break;
24884                     
24885                     case 'u':
24886                         cmd = 'underline';
24887                         break;
24888                     
24889                     case 'v':
24890                         this.cleanUpPaste.defer(100, this);
24891                         return;
24892                         
24893                 }
24894                 if(cmd){
24895                     this.win.focus();
24896                     this.execCmd(cmd);
24897                     this.deferFocus();
24898                     e.preventDefault();
24899                 }
24900                 
24901             }
24902         }
24903     },
24904
24905     // private
24906     fixKeys : function(){ // load time branching for fastest keydown performance
24907         if(Roo.isIE){
24908             return function(e){
24909                 var k = e.getKey(), r;
24910                 if(k == e.TAB){
24911                     e.stopEvent();
24912                     r = this.doc.selection.createRange();
24913                     if(r){
24914                         r.collapse(true);
24915                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24916                         this.deferFocus();
24917                     }
24918                     return;
24919                 }
24920                 
24921                 if(k == e.ENTER){
24922                     r = this.doc.selection.createRange();
24923                     if(r){
24924                         var target = r.parentElement();
24925                         if(!target || target.tagName.toLowerCase() != 'li'){
24926                             e.stopEvent();
24927                             r.pasteHTML('<br />');
24928                             r.collapse(false);
24929                             r.select();
24930                         }
24931                     }
24932                 }
24933                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24934                     this.cleanUpPaste.defer(100, this);
24935                     return;
24936                 }
24937                 
24938                 
24939             };
24940         }else if(Roo.isOpera){
24941             return function(e){
24942                 var k = e.getKey();
24943                 if(k == e.TAB){
24944                     e.stopEvent();
24945                     this.win.focus();
24946                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24947                     this.deferFocus();
24948                 }
24949                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24950                     this.cleanUpPaste.defer(100, this);
24951                     return;
24952                 }
24953                 
24954             };
24955         }else if(Roo.isSafari){
24956             return function(e){
24957                 var k = e.getKey();
24958                 
24959                 if(k == e.TAB){
24960                     e.stopEvent();
24961                     this.execCmd('InsertText','\t');
24962                     this.deferFocus();
24963                     return;
24964                 }
24965                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24966                     this.cleanUpPaste.defer(100, this);
24967                     return;
24968                 }
24969                 
24970              };
24971         }
24972     }(),
24973     
24974     getAllAncestors: function()
24975     {
24976         var p = this.getSelectedNode();
24977         var a = [];
24978         if (!p) {
24979             a.push(p); // push blank onto stack..
24980             p = this.getParentElement();
24981         }
24982         
24983         
24984         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24985             a.push(p);
24986             p = p.parentNode;
24987         }
24988         a.push(this.doc.body);
24989         return a;
24990     },
24991     lastSel : false,
24992     lastSelNode : false,
24993     
24994     
24995     getSelection : function() 
24996     {
24997         this.assignDocWin();
24998         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24999     },
25000     
25001     getSelectedNode: function() 
25002     {
25003         // this may only work on Gecko!!!
25004         
25005         // should we cache this!!!!
25006         
25007         
25008         
25009          
25010         var range = this.createRange(this.getSelection()).cloneRange();
25011         
25012         if (Roo.isIE) {
25013             var parent = range.parentElement();
25014             while (true) {
25015                 var testRange = range.duplicate();
25016                 testRange.moveToElementText(parent);
25017                 if (testRange.inRange(range)) {
25018                     break;
25019                 }
25020                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25021                     break;
25022                 }
25023                 parent = parent.parentElement;
25024             }
25025             return parent;
25026         }
25027         
25028         // is ancestor a text element.
25029         var ac =  range.commonAncestorContainer;
25030         if (ac.nodeType == 3) {
25031             ac = ac.parentNode;
25032         }
25033         
25034         var ar = ac.childNodes;
25035          
25036         var nodes = [];
25037         var other_nodes = [];
25038         var has_other_nodes = false;
25039         for (var i=0;i<ar.length;i++) {
25040             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25041                 continue;
25042             }
25043             // fullly contained node.
25044             
25045             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25046                 nodes.push(ar[i]);
25047                 continue;
25048             }
25049             
25050             // probably selected..
25051             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25052                 other_nodes.push(ar[i]);
25053                 continue;
25054             }
25055             // outer..
25056             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25057                 continue;
25058             }
25059             
25060             
25061             has_other_nodes = true;
25062         }
25063         if (!nodes.length && other_nodes.length) {
25064             nodes= other_nodes;
25065         }
25066         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25067             return false;
25068         }
25069         
25070         return nodes[0];
25071     },
25072     createRange: function(sel)
25073     {
25074         // this has strange effects when using with 
25075         // top toolbar - not sure if it's a great idea.
25076         //this.editor.contentWindow.focus();
25077         if (typeof sel != "undefined") {
25078             try {
25079                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25080             } catch(e) {
25081                 return this.doc.createRange();
25082             }
25083         } else {
25084             return this.doc.createRange();
25085         }
25086     },
25087     getParentElement: function()
25088     {
25089         
25090         this.assignDocWin();
25091         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25092         
25093         var range = this.createRange(sel);
25094          
25095         try {
25096             var p = range.commonAncestorContainer;
25097             while (p.nodeType == 3) { // text node
25098                 p = p.parentNode;
25099             }
25100             return p;
25101         } catch (e) {
25102             return null;
25103         }
25104     
25105     },
25106     /***
25107      *
25108      * Range intersection.. the hard stuff...
25109      *  '-1' = before
25110      *  '0' = hits..
25111      *  '1' = after.
25112      *         [ -- selected range --- ]
25113      *   [fail]                        [fail]
25114      *
25115      *    basically..
25116      *      if end is before start or  hits it. fail.
25117      *      if start is after end or hits it fail.
25118      *
25119      *   if either hits (but other is outside. - then it's not 
25120      *   
25121      *    
25122      **/
25123     
25124     
25125     // @see http://www.thismuchiknow.co.uk/?p=64.
25126     rangeIntersectsNode : function(range, node)
25127     {
25128         var nodeRange = node.ownerDocument.createRange();
25129         try {
25130             nodeRange.selectNode(node);
25131         } catch (e) {
25132             nodeRange.selectNodeContents(node);
25133         }
25134     
25135         var rangeStartRange = range.cloneRange();
25136         rangeStartRange.collapse(true);
25137     
25138         var rangeEndRange = range.cloneRange();
25139         rangeEndRange.collapse(false);
25140     
25141         var nodeStartRange = nodeRange.cloneRange();
25142         nodeStartRange.collapse(true);
25143     
25144         var nodeEndRange = nodeRange.cloneRange();
25145         nodeEndRange.collapse(false);
25146     
25147         return rangeStartRange.compareBoundaryPoints(
25148                  Range.START_TO_START, nodeEndRange) == -1 &&
25149                rangeEndRange.compareBoundaryPoints(
25150                  Range.START_TO_START, nodeStartRange) == 1;
25151         
25152          
25153     },
25154     rangeCompareNode : function(range, node)
25155     {
25156         var nodeRange = node.ownerDocument.createRange();
25157         try {
25158             nodeRange.selectNode(node);
25159         } catch (e) {
25160             nodeRange.selectNodeContents(node);
25161         }
25162         
25163         
25164         range.collapse(true);
25165     
25166         nodeRange.collapse(true);
25167      
25168         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25169         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25170          
25171         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25172         
25173         var nodeIsBefore   =  ss == 1;
25174         var nodeIsAfter    = ee == -1;
25175         
25176         if (nodeIsBefore && nodeIsAfter)
25177             return 0; // outer
25178         if (!nodeIsBefore && nodeIsAfter)
25179             return 1; //right trailed.
25180         
25181         if (nodeIsBefore && !nodeIsAfter)
25182             return 2;  // left trailed.
25183         // fully contined.
25184         return 3;
25185     },
25186
25187     // private? - in a new class?
25188     cleanUpPaste :  function()
25189     {
25190         // cleans up the whole document..
25191          Roo.log('cleanuppaste');
25192         this.cleanUpChildren(this.doc.body);
25193         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25194         if (clean != this.doc.body.innerHTML) {
25195             this.doc.body.innerHTML = clean;
25196         }
25197         
25198     },
25199     
25200     cleanWordChars : function(input) {
25201         var he = Roo.form.HtmlEditor;
25202     
25203         var output = input;
25204         Roo.each(he.swapCodes, function(sw) { 
25205         
25206             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25207             output = output.replace(swapper, sw[1]);
25208         });
25209         return output;
25210     },
25211     
25212     
25213     cleanUpChildren : function (n)
25214     {
25215         if (!n.childNodes.length) {
25216             return;
25217         }
25218         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25219            this.cleanUpChild(n.childNodes[i]);
25220         }
25221     },
25222     
25223     
25224         
25225     
25226     cleanUpChild : function (node)
25227     {
25228         //console.log(node);
25229         if (node.nodeName == "#text") {
25230             // clean up silly Windows -- stuff?
25231             return; 
25232         }
25233         if (node.nodeName == "#comment") {
25234             node.parentNode.removeChild(node);
25235             // clean up silly Windows -- stuff?
25236             return; 
25237         }
25238         
25239         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
25240             // remove node.
25241             node.parentNode.removeChild(node);
25242             return;
25243             
25244         }
25245         
25246         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
25247         
25248         // remove <a name=....> as rendering on yahoo mailer is bored with this.
25249         
25250         if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25251             remove_keep_children = true;
25252         }
25253         
25254         if (remove_keep_children) {
25255             this.cleanUpChildren(node);
25256             // inserts everything just before this node...
25257             while (node.childNodes.length) {
25258                 var cn = node.childNodes[0];
25259                 node.removeChild(cn);
25260                 node.parentNode.insertBefore(cn, node);
25261             }
25262             node.parentNode.removeChild(node);
25263             return;
25264         }
25265         
25266         if (!node.attributes || !node.attributes.length) {
25267             this.cleanUpChildren(node);
25268             return;
25269         }
25270         
25271         function cleanAttr(n,v)
25272         {
25273             
25274             if (v.match(/^\./) || v.match(/^\//)) {
25275                 return;
25276             }
25277             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
25278                 return;
25279             }
25280             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
25281             node.removeAttribute(n);
25282             
25283         }
25284         
25285         function cleanStyle(n,v)
25286         {
25287             if (v.match(/expression/)) { //XSS?? should we even bother..
25288                 node.removeAttribute(n);
25289                 return;
25290             }
25291             
25292             
25293             var parts = v.split(/;/);
25294             Roo.each(parts, function(p) {
25295                 p = p.replace(/\s+/g,'');
25296                 if (!p.length) {
25297                     return true;
25298                 }
25299                 var l = p.split(':').shift().replace(/\s+/g,'');
25300                 
25301                 // only allow 'c whitelisted system attributes'
25302                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
25303                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
25304                     node.removeAttribute(n);
25305                     return false;
25306                 }
25307                 return true;
25308             });
25309             
25310             
25311         }
25312         
25313         
25314         for (var i = node.attributes.length-1; i > -1 ; i--) {
25315             var a = node.attributes[i];
25316             //console.log(a);
25317             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
25318                 node.removeAttribute(a.name);
25319                 return;
25320             }
25321             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
25322                 cleanAttr(a.name,a.value); // fixme..
25323                 return;
25324             }
25325             if (a.name == 'style') {
25326                 cleanStyle(a.name,a.value);
25327             }
25328             /// clean up MS crap..
25329             // tecnically this should be a list of valid class'es..
25330             
25331             
25332             if (a.name == 'class') {
25333                 if (a.value.match(/^Mso/)) {
25334                     node.className = '';
25335                 }
25336                 
25337                 if (a.value.match(/body/)) {
25338                     node.className = '';
25339                 }
25340             }
25341             
25342             // style cleanup!?
25343             // class cleanup?
25344             
25345         }
25346         
25347         
25348         this.cleanUpChildren(node);
25349         
25350         
25351     }
25352     
25353     
25354     // hide stuff that is not compatible
25355     /**
25356      * @event blur
25357      * @hide
25358      */
25359     /**
25360      * @event change
25361      * @hide
25362      */
25363     /**
25364      * @event focus
25365      * @hide
25366      */
25367     /**
25368      * @event specialkey
25369      * @hide
25370      */
25371     /**
25372      * @cfg {String} fieldClass @hide
25373      */
25374     /**
25375      * @cfg {String} focusClass @hide
25376      */
25377     /**
25378      * @cfg {String} autoCreate @hide
25379      */
25380     /**
25381      * @cfg {String} inputType @hide
25382      */
25383     /**
25384      * @cfg {String} invalidClass @hide
25385      */
25386     /**
25387      * @cfg {String} invalidText @hide
25388      */
25389     /**
25390      * @cfg {String} msgFx @hide
25391      */
25392     /**
25393      * @cfg {String} validateOnBlur @hide
25394      */
25395 });
25396
25397 Roo.form.HtmlEditor.white = [
25398         'area', 'br', 'img', 'input', 'hr', 'wbr',
25399         
25400        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25401        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25402        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25403        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25404        'table',   'ul',         'xmp', 
25405        
25406        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25407       'thead',   'tr', 
25408      
25409       'dir', 'menu', 'ol', 'ul', 'dl',
25410        
25411       'embed',  'object'
25412 ];
25413
25414
25415 Roo.form.HtmlEditor.black = [
25416     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25417         'applet', // 
25418         'base',   'basefont', 'bgsound', 'blink',  'body', 
25419         'frame',  'frameset', 'head',    'html',   'ilayer', 
25420         'iframe', 'layer',  'link',     'meta',    'object',   
25421         'script', 'style' ,'title',  'xml' // clean later..
25422 ];
25423 Roo.form.HtmlEditor.clean = [
25424     'script', 'style', 'title', 'xml'
25425 ];
25426 Roo.form.HtmlEditor.remove = [
25427     'font'
25428 ];
25429 // attributes..
25430
25431 Roo.form.HtmlEditor.ablack = [
25432     'on'
25433 ];
25434     
25435 Roo.form.HtmlEditor.aclean = [ 
25436     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25437 ];
25438
25439 // protocols..
25440 Roo.form.HtmlEditor.pwhite= [
25441         'http',  'https',  'mailto'
25442 ];
25443
25444 // white listed style attributes.
25445 Roo.form.HtmlEditor.cwhite= [
25446         'text-align',
25447         'font-size'
25448 ];
25449
25450
25451 Roo.form.HtmlEditor.swapCodes   =[ 
25452     [    8211, "--" ], 
25453     [    8212, "--" ], 
25454     [    8216,  "'" ],  
25455     [    8217, "'" ],  
25456     [    8220, '"' ],  
25457     [    8221, '"' ],  
25458     [    8226, "*" ],  
25459     [    8230, "..." ]
25460 ]; 
25461
25462     // <script type="text/javascript">
25463 /*
25464  * Based on
25465  * Ext JS Library 1.1.1
25466  * Copyright(c) 2006-2007, Ext JS, LLC.
25467  *  
25468  
25469  */
25470
25471 /**
25472  * @class Roo.form.HtmlEditorToolbar1
25473  * Basic Toolbar
25474  * 
25475  * Usage:
25476  *
25477  new Roo.form.HtmlEditor({
25478     ....
25479     toolbars : [
25480         new Roo.form.HtmlEditorToolbar1({
25481             disable : { fonts: 1 , format: 1, ..., ... , ...],
25482             btns : [ .... ]
25483         })
25484     }
25485      
25486  * 
25487  * @cfg {Object} disable List of elements to disable..
25488  * @cfg {Array} btns List of additional buttons.
25489  * 
25490  * 
25491  * NEEDS Extra CSS? 
25492  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25493  */
25494  
25495 Roo.form.HtmlEditor.ToolbarStandard = function(config)
25496 {
25497     
25498     Roo.apply(this, config);
25499     
25500     // default disabled, based on 'good practice'..
25501     this.disable = this.disable || {};
25502     Roo.applyIf(this.disable, {
25503         fontSize : true,
25504         colors : true,
25505         specialElements : true
25506     });
25507     
25508     
25509     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25510     // dont call parent... till later.
25511 }
25512
25513 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
25514     
25515     tb: false,
25516     
25517     rendered: false,
25518     
25519     editor : false,
25520     /**
25521      * @cfg {Object} disable  List of toolbar elements to disable
25522          
25523      */
25524     disable : false,
25525       /**
25526      * @cfg {Array} fontFamilies An array of available font families
25527      */
25528     fontFamilies : [
25529         'Arial',
25530         'Courier New',
25531         'Tahoma',
25532         'Times New Roman',
25533         'Verdana'
25534     ],
25535     
25536     specialChars : [
25537            "&#169;",
25538           "&#174;",     
25539           "&#8482;",    
25540           "&#163;" ,    
25541          // "&#8212;",    
25542           "&#8230;",    
25543           "&#247;" ,    
25544         //  "&#225;" ,     ?? a acute?
25545            "&#8364;"    , //Euro
25546        //   "&#8220;"    ,
25547         //  "&#8221;"    ,
25548         //  "&#8226;"    ,
25549           "&#176;"  //   , // degrees
25550
25551          // "&#233;"     , // e ecute
25552          // "&#250;"     , // u ecute?
25553     ],
25554     
25555     specialElements : [
25556         {
25557             text: "Insert Table",
25558             xtype: 'MenuItem',
25559             xns : Roo.Menu,
25560             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
25561                 
25562         },
25563         {    
25564             text: "Insert Image",
25565             xtype: 'MenuItem',
25566             xns : Roo.Menu,
25567             ihtml : '<img src="about:blank"/>'
25568             
25569         }
25570         
25571          
25572     ],
25573     
25574     
25575     inputElements : [ 
25576             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
25577             "input:submit", "input:button", "select", "textarea", "label" ],
25578     formats : [
25579         ["p"] ,  
25580         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
25581         ["pre"],[ "code"], 
25582         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
25583     ],
25584      /**
25585      * @cfg {String} defaultFont default font to use.
25586      */
25587     defaultFont: 'tahoma',
25588    
25589     fontSelect : false,
25590     
25591     
25592     formatCombo : false,
25593     
25594     init : function(editor)
25595     {
25596         this.editor = editor;
25597         
25598         
25599         var fid = editor.frameId;
25600         var etb = this;
25601         function btn(id, toggle, handler){
25602             var xid = fid + '-'+ id ;
25603             return {
25604                 id : xid,
25605                 cmd : id,
25606                 cls : 'x-btn-icon x-edit-'+id,
25607                 enableToggle:toggle !== false,
25608                 scope: editor, // was editor...
25609                 handler:handler||editor.relayBtnCmd,
25610                 clickEvent:'mousedown',
25611                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25612                 tabIndex:-1
25613             };
25614         }
25615         
25616         
25617         
25618         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
25619         this.tb = tb;
25620          // stop form submits
25621         tb.el.on('click', function(e){
25622             e.preventDefault(); // what does this do?
25623         });
25624
25625         if(!this.disable.font && !Roo.isSafari){
25626             /* why no safari for fonts
25627             editor.fontSelect = tb.el.createChild({
25628                 tag:'select',
25629                 tabIndex: -1,
25630                 cls:'x-font-select',
25631                 html: editor.createFontOptions()
25632             });
25633             editor.fontSelect.on('change', function(){
25634                 var font = editor.fontSelect.dom.value;
25635                 editor.relayCmd('fontname', font);
25636                 editor.deferFocus();
25637             }, editor);
25638             tb.add(
25639                 editor.fontSelect.dom,
25640                 '-'
25641             );
25642             */
25643         };
25644         if(!this.disable.formats){
25645             this.formatCombo = new Roo.form.ComboBox({
25646                 store: new Roo.data.SimpleStore({
25647                     id : 'tag',
25648                     fields: ['tag'],
25649                     data : this.formats // from states.js
25650                 }),
25651                 blockFocus : true,
25652                 //autoCreate : {tag: "div",  size: "20"},
25653                 displayField:'tag',
25654                 typeAhead: false,
25655                 mode: 'local',
25656                 editable : false,
25657                 triggerAction: 'all',
25658                 emptyText:'Add tag',
25659                 selectOnFocus:true,
25660                 width:135,
25661                 listeners : {
25662                     'select': function(c, r, i) {
25663                         editor.insertTag(r.get('tag'));
25664                         editor.focus();
25665                     }
25666                 }
25667
25668             });
25669             tb.addField(this.formatCombo);
25670             
25671         }
25672         
25673         if(!this.disable.format){
25674             tb.add(
25675                 btn('bold'),
25676                 btn('italic'),
25677                 btn('underline')
25678             );
25679         };
25680         if(!this.disable.fontSize){
25681             tb.add(
25682                 '-',
25683                 
25684                 
25685                 btn('increasefontsize', false, editor.adjustFont),
25686                 btn('decreasefontsize', false, editor.adjustFont)
25687             );
25688         };
25689         
25690         
25691         if(!this.disable.colors){
25692             tb.add(
25693                 '-', {
25694                     id:editor.frameId +'-forecolor',
25695                     cls:'x-btn-icon x-edit-forecolor',
25696                     clickEvent:'mousedown',
25697                     tooltip: this.buttonTips['forecolor'] || undefined,
25698                     tabIndex:-1,
25699                     menu : new Roo.menu.ColorMenu({
25700                         allowReselect: true,
25701                         focus: Roo.emptyFn,
25702                         value:'000000',
25703                         plain:true,
25704                         selectHandler: function(cp, color){
25705                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
25706                             editor.deferFocus();
25707                         },
25708                         scope: editor,
25709                         clickEvent:'mousedown'
25710                     })
25711                 }, {
25712                     id:editor.frameId +'backcolor',
25713                     cls:'x-btn-icon x-edit-backcolor',
25714                     clickEvent:'mousedown',
25715                     tooltip: this.buttonTips['backcolor'] || undefined,
25716                     tabIndex:-1,
25717                     menu : new Roo.menu.ColorMenu({
25718                         focus: Roo.emptyFn,
25719                         value:'FFFFFF',
25720                         plain:true,
25721                         allowReselect: true,
25722                         selectHandler: function(cp, color){
25723                             if(Roo.isGecko){
25724                                 editor.execCmd('useCSS', false);
25725                                 editor.execCmd('hilitecolor', color);
25726                                 editor.execCmd('useCSS', true);
25727                                 editor.deferFocus();
25728                             }else{
25729                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
25730                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
25731                                 editor.deferFocus();
25732                             }
25733                         },
25734                         scope:editor,
25735                         clickEvent:'mousedown'
25736                     })
25737                 }
25738             );
25739         };
25740         // now add all the items...
25741         
25742
25743         if(!this.disable.alignments){
25744             tb.add(
25745                 '-',
25746                 btn('justifyleft'),
25747                 btn('justifycenter'),
25748                 btn('justifyright')
25749             );
25750         };
25751
25752         //if(!Roo.isSafari){
25753             if(!this.disable.links){
25754                 tb.add(
25755                     '-',
25756                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
25757                 );
25758             };
25759
25760             if(!this.disable.lists){
25761                 tb.add(
25762                     '-',
25763                     btn('insertorderedlist'),
25764                     btn('insertunorderedlist')
25765                 );
25766             }
25767             if(!this.disable.sourceEdit){
25768                 tb.add(
25769                     '-',
25770                     btn('sourceedit', true, function(btn){
25771                         this.toggleSourceEdit(btn.pressed);
25772                     })
25773                 );
25774             }
25775         //}
25776         
25777         var smenu = { };
25778         // special menu.. - needs to be tidied up..
25779         if (!this.disable.special) {
25780             smenu = {
25781                 text: "&#169;",
25782                 cls: 'x-edit-none',
25783                 
25784                 menu : {
25785                     items : []
25786                 }
25787             };
25788             for (var i =0; i < this.specialChars.length; i++) {
25789                 smenu.menu.items.push({
25790                     
25791                     html: this.specialChars[i],
25792                     handler: function(a,b) {
25793                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
25794                         //editor.insertAtCursor(a.html);
25795                         
25796                     },
25797                     tabIndex:-1
25798                 });
25799             }
25800             
25801             
25802             tb.add(smenu);
25803             
25804             
25805         }
25806          
25807         if (!this.disable.specialElements) {
25808             var semenu = {
25809                 text: "Other;",
25810                 cls: 'x-edit-none',
25811                 menu : {
25812                     items : []
25813                 }
25814             };
25815             for (var i =0; i < this.specialElements.length; i++) {
25816                 semenu.menu.items.push(
25817                     Roo.apply({ 
25818                         handler: function(a,b) {
25819                             editor.insertAtCursor(this.ihtml);
25820                         }
25821                     }, this.specialElements[i])
25822                 );
25823                     
25824             }
25825             
25826             tb.add(semenu);
25827             
25828             
25829         }
25830          
25831         
25832         if (this.btns) {
25833             for(var i =0; i< this.btns.length;i++) {
25834                 var b = this.btns[i];
25835                 b.cls =  'x-edit-none';
25836                 b.scope = editor;
25837                 tb.add(b);
25838             }
25839         
25840         }
25841         
25842         
25843         
25844         // disable everything...
25845         
25846         this.tb.items.each(function(item){
25847            if(item.id != editor.frameId+ '-sourceedit'){
25848                 item.disable();
25849             }
25850         });
25851         this.rendered = true;
25852         
25853         // the all the btns;
25854         editor.on('editorevent', this.updateToolbar, this);
25855         // other toolbars need to implement this..
25856         //editor.on('editmodechange', this.updateToolbar, this);
25857     },
25858     
25859     
25860     
25861     /**
25862      * Protected method that will not generally be called directly. It triggers
25863      * a toolbar update by reading the markup state of the current selection in the editor.
25864      */
25865     updateToolbar: function(){
25866
25867         if(!this.editor.activated){
25868             this.editor.onFirstFocus();
25869             return;
25870         }
25871
25872         var btns = this.tb.items.map, 
25873             doc = this.editor.doc,
25874             frameId = this.editor.frameId;
25875
25876         if(!this.disable.font && !Roo.isSafari){
25877             /*
25878             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
25879             if(name != this.fontSelect.dom.value){
25880                 this.fontSelect.dom.value = name;
25881             }
25882             */
25883         }
25884         if(!this.disable.format){
25885             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
25886             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
25887             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
25888         }
25889         if(!this.disable.alignments){
25890             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
25891             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
25892             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
25893         }
25894         if(!Roo.isSafari && !this.disable.lists){
25895             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
25896             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
25897         }
25898         
25899         var ans = this.editor.getAllAncestors();
25900         if (this.formatCombo) {
25901             
25902             
25903             var store = this.formatCombo.store;
25904             this.formatCombo.setValue("");
25905             for (var i =0; i < ans.length;i++) {
25906                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25907                     // select it..
25908                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
25909                     break;
25910                 }
25911             }
25912         }
25913         
25914         
25915         
25916         // hides menus... - so this cant be on a menu...
25917         Roo.menu.MenuMgr.hideAll();
25918
25919         //this.editorsyncValue();
25920     },
25921    
25922     
25923     createFontOptions : function(){
25924         var buf = [], fs = this.fontFamilies, ff, lc;
25925         for(var i = 0, len = fs.length; i< len; i++){
25926             ff = fs[i];
25927             lc = ff.toLowerCase();
25928             buf.push(
25929                 '<option value="',lc,'" style="font-family:',ff,';"',
25930                     (this.defaultFont == lc ? ' selected="true">' : '>'),
25931                     ff,
25932                 '</option>'
25933             );
25934         }
25935         return buf.join('');
25936     },
25937     
25938     toggleSourceEdit : function(sourceEditMode){
25939         if(sourceEditMode === undefined){
25940             sourceEditMode = !this.sourceEditMode;
25941         }
25942         this.sourceEditMode = sourceEditMode === true;
25943         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
25944         // just toggle the button?
25945         if(btn.pressed !== this.editor.sourceEditMode){
25946             btn.toggle(this.editor.sourceEditMode);
25947             return;
25948         }
25949         
25950         if(this.sourceEditMode){
25951             this.tb.items.each(function(item){
25952                 if(item.cmd != 'sourceedit'){
25953                     item.disable();
25954                 }
25955             });
25956           
25957         }else{
25958             if(this.initialized){
25959                 this.tb.items.each(function(item){
25960                     item.enable();
25961                 });
25962             }
25963             
25964         }
25965         // tell the editor that it's been pressed..
25966         this.editor.toggleSourceEdit(sourceEditMode);
25967        
25968     },
25969      /**
25970      * Object collection of toolbar tooltips for the buttons in the editor. The key
25971      * is the command id associated with that button and the value is a valid QuickTips object.
25972      * For example:
25973 <pre><code>
25974 {
25975     bold : {
25976         title: 'Bold (Ctrl+B)',
25977         text: 'Make the selected text bold.',
25978         cls: 'x-html-editor-tip'
25979     },
25980     italic : {
25981         title: 'Italic (Ctrl+I)',
25982         text: 'Make the selected text italic.',
25983         cls: 'x-html-editor-tip'
25984     },
25985     ...
25986 </code></pre>
25987     * @type Object
25988      */
25989     buttonTips : {
25990         bold : {
25991             title: 'Bold (Ctrl+B)',
25992             text: 'Make the selected text bold.',
25993             cls: 'x-html-editor-tip'
25994         },
25995         italic : {
25996             title: 'Italic (Ctrl+I)',
25997             text: 'Make the selected text italic.',
25998             cls: 'x-html-editor-tip'
25999         },
26000         underline : {
26001             title: 'Underline (Ctrl+U)',
26002             text: 'Underline the selected text.',
26003             cls: 'x-html-editor-tip'
26004         },
26005         increasefontsize : {
26006             title: 'Grow Text',
26007             text: 'Increase the font size.',
26008             cls: 'x-html-editor-tip'
26009         },
26010         decreasefontsize : {
26011             title: 'Shrink Text',
26012             text: 'Decrease the font size.',
26013             cls: 'x-html-editor-tip'
26014         },
26015         backcolor : {
26016             title: 'Text Highlight Color',
26017             text: 'Change the background color of the selected text.',
26018             cls: 'x-html-editor-tip'
26019         },
26020         forecolor : {
26021             title: 'Font Color',
26022             text: 'Change the color of the selected text.',
26023             cls: 'x-html-editor-tip'
26024         },
26025         justifyleft : {
26026             title: 'Align Text Left',
26027             text: 'Align text to the left.',
26028             cls: 'x-html-editor-tip'
26029         },
26030         justifycenter : {
26031             title: 'Center Text',
26032             text: 'Center text in the editor.',
26033             cls: 'x-html-editor-tip'
26034         },
26035         justifyright : {
26036             title: 'Align Text Right',
26037             text: 'Align text to the right.',
26038             cls: 'x-html-editor-tip'
26039         },
26040         insertunorderedlist : {
26041             title: 'Bullet List',
26042             text: 'Start a bulleted list.',
26043             cls: 'x-html-editor-tip'
26044         },
26045         insertorderedlist : {
26046             title: 'Numbered List',
26047             text: 'Start a numbered list.',
26048             cls: 'x-html-editor-tip'
26049         },
26050         createlink : {
26051             title: 'Hyperlink',
26052             text: 'Make the selected text a hyperlink.',
26053             cls: 'x-html-editor-tip'
26054         },
26055         sourceedit : {
26056             title: 'Source Edit',
26057             text: 'Switch to source editing mode.',
26058             cls: 'x-html-editor-tip'
26059         }
26060     },
26061     // private
26062     onDestroy : function(){
26063         if(this.rendered){
26064             
26065             this.tb.items.each(function(item){
26066                 if(item.menu){
26067                     item.menu.removeAll();
26068                     if(item.menu.el){
26069                         item.menu.el.destroy();
26070                     }
26071                 }
26072                 item.destroy();
26073             });
26074              
26075         }
26076     },
26077     onFirstFocus: function() {
26078         this.tb.items.each(function(item){
26079            item.enable();
26080         });
26081     }
26082 });
26083
26084
26085
26086
26087 // <script type="text/javascript">
26088 /*
26089  * Based on
26090  * Ext JS Library 1.1.1
26091  * Copyright(c) 2006-2007, Ext JS, LLC.
26092  *  
26093  
26094  */
26095
26096  
26097 /**
26098  * @class Roo.form.HtmlEditor.ToolbarContext
26099  * Context Toolbar
26100  * 
26101  * Usage:
26102  *
26103  new Roo.form.HtmlEditor({
26104     ....
26105     toolbars : [
26106         { xtype: 'ToolbarStandard', styles : {} }
26107         { xtype: 'ToolbarContext', disable : {} }
26108     ]
26109 })
26110
26111      
26112  * 
26113  * @config : {Object} disable List of elements to disable.. (not done yet.)
26114  * @config : {Object} styles  Map of styles available.
26115  * 
26116  */
26117
26118 Roo.form.HtmlEditor.ToolbarContext = function(config)
26119 {
26120     
26121     Roo.apply(this, config);
26122     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26123     // dont call parent... till later.
26124     this.styles = this.styles || {};
26125 }
26126 Roo.form.HtmlEditor.ToolbarContext.types = {
26127     'IMG' : {
26128         width : {
26129             title: "Width",
26130             width: 40
26131         },
26132         height:  {
26133             title: "Height",
26134             width: 40
26135         },
26136         align: {
26137             title: "Align",
26138             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
26139             width : 80
26140             
26141         },
26142         border: {
26143             title: "Border",
26144             width: 40
26145         },
26146         alt: {
26147             title: "Alt",
26148             width: 120
26149         },
26150         src : {
26151             title: "Src",
26152             width: 220
26153         }
26154         
26155     },
26156     'A' : {
26157         name : {
26158             title: "Name",
26159             width: 50
26160         },
26161         href:  {
26162             title: "Href",
26163             width: 220
26164         } // border?
26165         
26166     },
26167     'TABLE' : {
26168         rows : {
26169             title: "Rows",
26170             width: 20
26171         },
26172         cols : {
26173             title: "Cols",
26174             width: 20
26175         },
26176         width : {
26177             title: "Width",
26178             width: 40
26179         },
26180         height : {
26181             title: "Height",
26182             width: 40
26183         },
26184         border : {
26185             title: "Border",
26186             width: 20
26187         }
26188     },
26189     'TD' : {
26190         width : {
26191             title: "Width",
26192             width: 40
26193         },
26194         height : {
26195             title: "Height",
26196             width: 40
26197         },   
26198         align: {
26199             title: "Align",
26200             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
26201             width: 80
26202         },
26203         valign: {
26204             title: "Valign",
26205             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
26206             width: 80
26207         },
26208         colspan: {
26209             title: "Colspan",
26210             width: 20
26211             
26212         }
26213     },
26214     'INPUT' : {
26215         name : {
26216             title: "name",
26217             width: 120
26218         },
26219         value : {
26220             title: "Value",
26221             width: 120
26222         },
26223         width : {
26224             title: "Width",
26225             width: 40
26226         }
26227     },
26228     'LABEL' : {
26229         'for' : {
26230             title: "For",
26231             width: 120
26232         }
26233     },
26234     'TEXTAREA' : {
26235           name : {
26236             title: "name",
26237             width: 120
26238         },
26239         rows : {
26240             title: "Rows",
26241             width: 20
26242         },
26243         cols : {
26244             title: "Cols",
26245             width: 20
26246         }
26247     },
26248     'SELECT' : {
26249         name : {
26250             title: "name",
26251             width: 120
26252         },
26253         selectoptions : {
26254             title: "Options",
26255             width: 200
26256         }
26257     },
26258     
26259     // should we really allow this??
26260     // should this just be 
26261     'BODY' : {
26262         title : {
26263             title: "title",
26264             width: 200,
26265             disabled : true
26266         }
26267     },
26268     '*' : {
26269         // empty..
26270     }
26271 };
26272
26273
26274
26275 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
26276     
26277     tb: false,
26278     
26279     rendered: false,
26280     
26281     editor : false,
26282     /**
26283      * @cfg {Object} disable  List of toolbar elements to disable
26284          
26285      */
26286     disable : false,
26287     /**
26288      * @cfg {Object} styles List of styles 
26289      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
26290      *
26291      * These must be defined in the page, so they get rendered correctly..
26292      * .headline { }
26293      * TD.underline { }
26294      * 
26295      */
26296     styles : false,
26297     
26298     
26299     
26300     toolbars : false,
26301     
26302     init : function(editor)
26303     {
26304         this.editor = editor;
26305         
26306         
26307         var fid = editor.frameId;
26308         var etb = this;
26309         function btn(id, toggle, handler){
26310             var xid = fid + '-'+ id ;
26311             return {
26312                 id : xid,
26313                 cmd : id,
26314                 cls : 'x-btn-icon x-edit-'+id,
26315                 enableToggle:toggle !== false,
26316                 scope: editor, // was editor...
26317                 handler:handler||editor.relayBtnCmd,
26318                 clickEvent:'mousedown',
26319                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26320                 tabIndex:-1
26321             };
26322         }
26323         // create a new element.
26324         var wdiv = editor.wrap.createChild({
26325                 tag: 'div'
26326             }, editor.wrap.dom.firstChild.nextSibling, true);
26327         
26328         // can we do this more than once??
26329         
26330          // stop form submits
26331       
26332  
26333         // disable everything...
26334         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
26335         this.toolbars = {};
26336            
26337         for (var i in  ty) {
26338           
26339             this.toolbars[i] = this.buildToolbar(ty[i],i);
26340         }
26341         this.tb = this.toolbars.BODY;
26342         this.tb.el.show();
26343         this.buildFooter();
26344         this.footer.show();
26345          
26346         this.rendered = true;
26347         
26348         // the all the btns;
26349         editor.on('editorevent', this.updateToolbar, this);
26350         // other toolbars need to implement this..
26351         //editor.on('editmodechange', this.updateToolbar, this);
26352     },
26353     
26354     
26355     
26356     /**
26357      * Protected method that will not generally be called directly. It triggers
26358      * a toolbar update by reading the markup state of the current selection in the editor.
26359      */
26360     updateToolbar: function(editor,ev,sel){
26361
26362         //Roo.log(ev);
26363         // capture mouse up - this is handy for selecting images..
26364         // perhaps should go somewhere else...
26365         if(!this.editor.activated){
26366              this.editor.onFirstFocus();
26367             return;
26368         }
26369         
26370         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
26371         // selectNode - might want to handle IE?
26372         if (ev &&
26373             (ev.type == 'mouseup' || ev.type == 'click' ) &&
26374             ev.target && ev.target.tagName == 'IMG') {
26375             // they have click on an image...
26376             // let's see if we can change the selection...
26377             sel = ev.target;
26378          
26379               var nodeRange = sel.ownerDocument.createRange();
26380             try {
26381                 nodeRange.selectNode(sel);
26382             } catch (e) {
26383                 nodeRange.selectNodeContents(sel);
26384             }
26385             //nodeRange.collapse(true);
26386             var s = editor.win.getSelection();
26387             s.removeAllRanges();
26388             s.addRange(nodeRange);
26389         }  
26390         
26391       
26392         var updateFooter = sel ? false : true;
26393         
26394         
26395         var ans = this.editor.getAllAncestors();
26396         
26397         // pick
26398         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
26399         
26400         if (!sel) { 
26401             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
26402             sel = sel ? sel : this.editor.doc.body;
26403             sel = sel.tagName.length ? sel : this.editor.doc.body;
26404             
26405         }
26406         // pick a menu that exists..
26407         var tn = sel.tagName.toUpperCase();
26408         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
26409         
26410         tn = sel.tagName.toUpperCase();
26411         
26412         var lastSel = this.tb.selectedNode
26413         
26414         this.tb.selectedNode = sel;
26415         
26416         // if current menu does not match..
26417         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
26418                 
26419             this.tb.el.hide();
26420             ///console.log("show: " + tn);
26421             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
26422             this.tb.el.show();
26423             // update name
26424             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
26425             
26426             
26427             // update attributes
26428             if (this.tb.fields) {
26429                 this.tb.fields.each(function(e) {
26430                    e.setValue(sel.getAttribute(e.name));
26431                 });
26432             }
26433             
26434             // update styles
26435             var st = this.tb.fields.item(0);
26436             st.store.removeAll();
26437             var cn = sel.className.split(/\s+/);
26438             
26439             var avs = [];
26440             if (this.styles['*']) {
26441                 
26442                 Roo.each(this.styles['*'], function(v) {
26443                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
26444                 });
26445             }
26446             if (this.styles[tn]) { 
26447                 Roo.each(this.styles[tn], function(v) {
26448                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
26449                 });
26450             }
26451             
26452             st.store.loadData(avs);
26453             st.collapse();
26454             st.setValue(cn);
26455             
26456             // flag our selected Node.
26457             this.tb.selectedNode = sel;
26458            
26459            
26460             Roo.menu.MenuMgr.hideAll();
26461
26462         }
26463         
26464         if (!updateFooter) {
26465             return;
26466         }
26467         // update the footer
26468         //
26469         var html = '';
26470         
26471         this.footerEls = ans.reverse();
26472         Roo.each(this.footerEls, function(a,i) {
26473             if (!a) { return; }
26474             html += html.length ? ' &gt; '  :  '';
26475             
26476             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
26477             
26478         });
26479        
26480         // 
26481         var sz = this.footDisp.up('td').getSize();
26482         this.footDisp.dom.style.width = (sz.width -10) + 'px';
26483         this.footDisp.dom.style.marginLeft = '5px';
26484         
26485         this.footDisp.dom.style.overflow = 'hidden';
26486         
26487         this.footDisp.dom.innerHTML = html;
26488             
26489         //this.editorsyncValue();
26490     },
26491    
26492        
26493     // private
26494     onDestroy : function(){
26495         if(this.rendered){
26496             
26497             this.tb.items.each(function(item){
26498                 if(item.menu){
26499                     item.menu.removeAll();
26500                     if(item.menu.el){
26501                         item.menu.el.destroy();
26502                     }
26503                 }
26504                 item.destroy();
26505             });
26506              
26507         }
26508     },
26509     onFirstFocus: function() {
26510         // need to do this for all the toolbars..
26511         this.tb.items.each(function(item){
26512            item.enable();
26513         });
26514     },
26515     buildToolbar: function(tlist, nm)
26516     {
26517         var editor = this.editor;
26518          // create a new element.
26519         var wdiv = editor.wrap.createChild({
26520                 tag: 'div'
26521             }, editor.wrap.dom.firstChild.nextSibling, true);
26522         
26523        
26524         var tb = new Roo.Toolbar(wdiv);
26525         // add the name..
26526         
26527         tb.add(nm+ ":&nbsp;");
26528         
26529         var styles = [];
26530         for(var i in this.styles) {
26531             styles.push(i);
26532         }
26533         
26534         // styles...
26535         if (styles && styles.length) {
26536             
26537             // this needs a multi-select checkbox...
26538             tb.addField( new Roo.form.ComboBox({
26539                 store: new Roo.data.SimpleStore({
26540                     id : 'val',
26541                     fields: ['val', 'selected'],
26542                     data : [] 
26543                 }),
26544                 name : 'className',
26545                 displayField:'val',
26546                 typeAhead: false,
26547                 mode: 'local',
26548                 editable : false,
26549                 triggerAction: 'all',
26550                 emptyText:'Select Style',
26551                 selectOnFocus:true,
26552                 width: 130,
26553                 listeners : {
26554                     'select': function(c, r, i) {
26555                         // initial support only for on class per el..
26556                         tb.selectedNode.className =  r ? r.get('val') : '';
26557                         editor.syncValue();
26558                     }
26559                 }
26560     
26561             }));
26562         }
26563             
26564         
26565         
26566         for (var i in tlist) {
26567             
26568             var item = tlist[i];
26569             tb.add(item.title + ":&nbsp;");
26570             
26571             
26572             
26573             
26574             if (item.opts) {
26575                 // opts == pulldown..
26576                 tb.addField( new Roo.form.ComboBox({
26577                     store: new Roo.data.SimpleStore({
26578                         id : 'val',
26579                         fields: ['val'],
26580                         data : item.opts  
26581                     }),
26582                     name : i,
26583                     displayField:'val',
26584                     typeAhead: false,
26585                     mode: 'local',
26586                     editable : false,
26587                     triggerAction: 'all',
26588                     emptyText:'Select',
26589                     selectOnFocus:true,
26590                     width: item.width ? item.width  : 130,
26591                     listeners : {
26592                         'select': function(c, r, i) {
26593                             tb.selectedNode.setAttribute(c.name, r.get('val'));
26594                         }
26595                     }
26596
26597                 }));
26598                 continue;
26599                     
26600                  
26601                 
26602                 tb.addField( new Roo.form.TextField({
26603                     name: i,
26604                     width: 100,
26605                     //allowBlank:false,
26606                     value: ''
26607                 }));
26608                 continue;
26609             }
26610             tb.addField( new Roo.form.TextField({
26611                 name: i,
26612                 width: item.width,
26613                 //allowBlank:true,
26614                 value: '',
26615                 listeners: {
26616                     'change' : function(f, nv, ov) {
26617                         tb.selectedNode.setAttribute(f.name, nv);
26618                     }
26619                 }
26620             }));
26621              
26622         }
26623         tb.el.on('click', function(e){
26624             e.preventDefault(); // what does this do?
26625         });
26626         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
26627         tb.el.hide();
26628         tb.name = nm;
26629         // dont need to disable them... as they will get hidden
26630         return tb;
26631          
26632         
26633     },
26634     buildFooter : function()
26635     {
26636         
26637         var fel = this.editor.wrap.createChild();
26638         this.footer = new Roo.Toolbar(fel);
26639         // toolbar has scrolly on left / right?
26640         var footDisp= new Roo.Toolbar.Fill();
26641         var _t = this;
26642         this.footer.add(
26643             {
26644                 text : '&lt;',
26645                 xtype: 'Button',
26646                 handler : function() {
26647                     _t.footDisp.scrollTo('left',0,true)
26648                 }
26649             }
26650         );
26651         this.footer.add( footDisp );
26652         this.footer.add( 
26653             {
26654                 text : '&gt;',
26655                 xtype: 'Button',
26656                 handler : function() {
26657                     // no animation..
26658                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
26659                 }
26660             }
26661         );
26662         var fel = Roo.get(footDisp.el);
26663         fel.addClass('x-editor-context');
26664         this.footDispWrap = fel; 
26665         this.footDispWrap.overflow  = 'hidden';
26666         
26667         this.footDisp = fel.createChild();
26668         this.footDispWrap.on('click', this.onContextClick, this)
26669         
26670         
26671     },
26672     onContextClick : function (ev,dom)
26673     {
26674         ev.preventDefault();
26675         var  cn = dom.className;
26676         Roo.log(cn);
26677         if (!cn.match(/x-ed-loc-/)) {
26678             return;
26679         }
26680         var n = cn.split('-').pop();
26681         var ans = this.footerEls;
26682         var sel = ans[n];
26683         
26684          // pick
26685         var range = this.editor.createRange();
26686         
26687         range.selectNodeContents(sel);
26688         //range.selectNode(sel);
26689         
26690         
26691         var selection = this.editor.getSelection();
26692         selection.removeAllRanges();
26693         selection.addRange(range);
26694         
26695         
26696         
26697         this.updateToolbar(null, null, sel);
26698         
26699         
26700     }
26701     
26702     
26703     
26704     
26705     
26706 });
26707
26708
26709
26710
26711
26712 /*
26713  * Based on:
26714  * Ext JS Library 1.1.1
26715  * Copyright(c) 2006-2007, Ext JS, LLC.
26716  *
26717  * Originally Released Under LGPL - original licence link has changed is not relivant.
26718  *
26719  * Fork - LGPL
26720  * <script type="text/javascript">
26721  */
26722  
26723 /**
26724  * @class Roo.form.BasicForm
26725  * @extends Roo.util.Observable
26726  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
26727  * @constructor
26728  * @param {String/HTMLElement/Roo.Element} el The form element or its id
26729  * @param {Object} config Configuration options
26730  */
26731 Roo.form.BasicForm = function(el, config){
26732     this.allItems = [];
26733     this.childForms = [];
26734     Roo.apply(this, config);
26735     /*
26736      * The Roo.form.Field items in this form.
26737      * @type MixedCollection
26738      */
26739      
26740      
26741     this.items = new Roo.util.MixedCollection(false, function(o){
26742         return o.id || (o.id = Roo.id());
26743     });
26744     this.addEvents({
26745         /**
26746          * @event beforeaction
26747          * Fires before any action is performed. Return false to cancel the action.
26748          * @param {Form} this
26749          * @param {Action} action The action to be performed
26750          */
26751         beforeaction: true,
26752         /**
26753          * @event actionfailed
26754          * Fires when an action fails.
26755          * @param {Form} this
26756          * @param {Action} action The action that failed
26757          */
26758         actionfailed : true,
26759         /**
26760          * @event actioncomplete
26761          * Fires when an action is completed.
26762          * @param {Form} this
26763          * @param {Action} action The action that completed
26764          */
26765         actioncomplete : true
26766     });
26767     if(el){
26768         this.initEl(el);
26769     }
26770     Roo.form.BasicForm.superclass.constructor.call(this);
26771 };
26772
26773 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
26774     /**
26775      * @cfg {String} method
26776      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
26777      */
26778     /**
26779      * @cfg {DataReader} reader
26780      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
26781      * This is optional as there is built-in support for processing JSON.
26782      */
26783     /**
26784      * @cfg {DataReader} errorReader
26785      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
26786      * This is completely optional as there is built-in support for processing JSON.
26787      */
26788     /**
26789      * @cfg {String} url
26790      * The URL to use for form actions if one isn't supplied in the action options.
26791      */
26792     /**
26793      * @cfg {Boolean} fileUpload
26794      * Set to true if this form is a file upload.
26795      */
26796      
26797     /**
26798      * @cfg {Object} baseParams
26799      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
26800      */
26801      /**
26802      
26803     /**
26804      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
26805      */
26806     timeout: 30,
26807
26808     // private
26809     activeAction : null,
26810
26811     /**
26812      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
26813      * or setValues() data instead of when the form was first created.
26814      */
26815     trackResetOnLoad : false,
26816     
26817     
26818     /**
26819      * childForms - used for multi-tab forms
26820      * @type {Array}
26821      */
26822     childForms : false,
26823     
26824     /**
26825      * allItems - full list of fields.
26826      * @type {Array}
26827      */
26828     allItems : false,
26829     
26830     /**
26831      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
26832      * element by passing it or its id or mask the form itself by passing in true.
26833      * @type Mixed
26834      */
26835     waitMsgTarget : false,
26836
26837     // private
26838     initEl : function(el){
26839         this.el = Roo.get(el);
26840         this.id = this.el.id || Roo.id();
26841         this.el.on('submit', this.onSubmit, this);
26842         this.el.addClass('x-form');
26843     },
26844
26845     // private
26846     onSubmit : function(e){
26847         e.stopEvent();
26848     },
26849
26850     /**
26851      * Returns true if client-side validation on the form is successful.
26852      * @return Boolean
26853      */
26854     isValid : function(){
26855         var valid = true;
26856         this.items.each(function(f){
26857            if(!f.validate()){
26858                valid = false;
26859            }
26860         });
26861         return valid;
26862     },
26863
26864     /**
26865      * Returns true if any fields in this form have changed since their original load.
26866      * @return Boolean
26867      */
26868     isDirty : function(){
26869         var dirty = false;
26870         this.items.each(function(f){
26871            if(f.isDirty()){
26872                dirty = true;
26873                return false;
26874            }
26875         });
26876         return dirty;
26877     },
26878
26879     /**
26880      * Performs a predefined action (submit or load) or custom actions you define on this form.
26881      * @param {String} actionName The name of the action type
26882      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
26883      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
26884      * accept other config options):
26885      * <pre>
26886 Property          Type             Description
26887 ----------------  ---------------  ----------------------------------------------------------------------------------
26888 url               String           The url for the action (defaults to the form's url)
26889 method            String           The form method to use (defaults to the form's method, or POST if not defined)
26890 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
26891 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
26892                                    validate the form on the client (defaults to false)
26893      * </pre>
26894      * @return {BasicForm} this
26895      */
26896     doAction : function(action, options){
26897         if(typeof action == 'string'){
26898             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
26899         }
26900         if(this.fireEvent('beforeaction', this, action) !== false){
26901             this.beforeAction(action);
26902             action.run.defer(100, action);
26903         }
26904         return this;
26905     },
26906
26907     /**
26908      * Shortcut to do a submit action.
26909      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26910      * @return {BasicForm} this
26911      */
26912     submit : function(options){
26913         this.doAction('submit', options);
26914         return this;
26915     },
26916
26917     /**
26918      * Shortcut to do a load action.
26919      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26920      * @return {BasicForm} this
26921      */
26922     load : function(options){
26923         this.doAction('load', options);
26924         return this;
26925     },
26926
26927     /**
26928      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
26929      * @param {Record} record The record to edit
26930      * @return {BasicForm} this
26931      */
26932     updateRecord : function(record){
26933         record.beginEdit();
26934         var fs = record.fields;
26935         fs.each(function(f){
26936             var field = this.findField(f.name);
26937             if(field){
26938                 record.set(f.name, field.getValue());
26939             }
26940         }, this);
26941         record.endEdit();
26942         return this;
26943     },
26944
26945     /**
26946      * Loads an Roo.data.Record into this form.
26947      * @param {Record} record The record to load
26948      * @return {BasicForm} this
26949      */
26950     loadRecord : function(record){
26951         this.setValues(record.data);
26952         return this;
26953     },
26954
26955     // private
26956     beforeAction : function(action){
26957         var o = action.options;
26958         
26959        
26960         if(this.waitMsgTarget === true){
26961             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
26962         }else if(this.waitMsgTarget){
26963             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
26964             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
26965         }else {
26966             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
26967         }
26968          
26969     },
26970
26971     // private
26972     afterAction : function(action, success){
26973         this.activeAction = null;
26974         var o = action.options;
26975         
26976         if(this.waitMsgTarget === true){
26977             this.el.unmask();
26978         }else if(this.waitMsgTarget){
26979             this.waitMsgTarget.unmask();
26980         }else{
26981             Roo.MessageBox.updateProgress(1);
26982             Roo.MessageBox.hide();
26983         }
26984          
26985         if(success){
26986             if(o.reset){
26987                 this.reset();
26988             }
26989             Roo.callback(o.success, o.scope, [this, action]);
26990             this.fireEvent('actioncomplete', this, action);
26991             
26992         }else{
26993             
26994             // failure condition..
26995             // we have a scenario where updates need confirming.
26996             // eg. if a locking scenario exists..
26997             // we look for { errors : { needs_confirm : true }} in the response.
26998             if (typeof(action.result.errors.needs_confirm) != 'undefined') {
26999                 var _t = this;
27000                 Roo.MessageBox.confirm(
27001                     "Change requires confirmation",
27002                     action.result.errorMsg,
27003                     function(r) {
27004                         if (r != 'yes') {
27005                             return;
27006                         }
27007                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
27008                     }
27009                     
27010                 );
27011                 
27012                 
27013                 
27014                 return;
27015             }
27016             
27017             Roo.callback(o.failure, o.scope, [this, action]);
27018             // show an error message if no failed handler is set..
27019             if (!this.hasListener('actionfailed')) {
27020                 Roo.MessageBox.alert("Error",
27021                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
27022                         action.result.errorMsg :
27023                         "Saving Failed, please check your entries"
27024                 );
27025             }
27026             
27027             this.fireEvent('actionfailed', this, action);
27028         }
27029         
27030     },
27031
27032     /**
27033      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
27034      * @param {String} id The value to search for
27035      * @return Field
27036      */
27037     findField : function(id){
27038         var field = this.items.get(id);
27039         if(!field){
27040             this.items.each(function(f){
27041                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
27042                     field = f;
27043                     return false;
27044                 }
27045             });
27046         }
27047         return field || null;
27048     },
27049
27050     /**
27051      * Add a secondary form to this one, 
27052      * Used to provide tabbed forms. One form is primary, with hidden values 
27053      * which mirror the elements from the other forms.
27054      * 
27055      * @param {Roo.form.Form} form to add.
27056      * 
27057      */
27058     addForm : function(form)
27059     {
27060        
27061         if (this.childForms.indexOf(form) > -1) {
27062             // already added..
27063             return;
27064         }
27065         this.childForms.push(form);
27066         var n = '';
27067         Roo.each(form.allItems, function (fe) {
27068             
27069             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
27070             if (this.findField(n)) { // already added..
27071                 return;
27072             }
27073             var add = new Roo.form.Hidden({
27074                 name : n
27075             });
27076             add.render(this.el);
27077             
27078             this.add( add );
27079         }, this);
27080         
27081     },
27082     /**
27083      * Mark fields in this form invalid in bulk.
27084      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
27085      * @return {BasicForm} this
27086      */
27087     markInvalid : function(errors){
27088         if(errors instanceof Array){
27089             for(var i = 0, len = errors.length; i < len; i++){
27090                 var fieldError = errors[i];
27091                 var f = this.findField(fieldError.id);
27092                 if(f){
27093                     f.markInvalid(fieldError.msg);
27094                 }
27095             }
27096         }else{
27097             var field, id;
27098             for(id in errors){
27099                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
27100                     field.markInvalid(errors[id]);
27101                 }
27102             }
27103         }
27104         Roo.each(this.childForms || [], function (f) {
27105             f.markInvalid(errors);
27106         });
27107         
27108         return this;
27109     },
27110
27111     /**
27112      * Set values for fields in this form in bulk.
27113      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
27114      * @return {BasicForm} this
27115      */
27116     setValues : function(values){
27117         if(values instanceof Array){ // array of objects
27118             for(var i = 0, len = values.length; i < len; i++){
27119                 var v = values[i];
27120                 var f = this.findField(v.id);
27121                 if(f){
27122                     f.setValue(v.value);
27123                     if(this.trackResetOnLoad){
27124                         f.originalValue = f.getValue();
27125                     }
27126                 }
27127             }
27128         }else{ // object hash
27129             var field, id;
27130             for(id in values){
27131                 if(typeof values[id] != 'function' && (field = this.findField(id))){
27132                     
27133                     if (field.setFromData && 
27134                         field.valueField && 
27135                         field.displayField &&
27136                         // combos' with local stores can 
27137                         // be queried via setValue()
27138                         // to set their value..
27139                         (field.store && !field.store.isLocal)
27140                         ) {
27141                         // it's a combo
27142                         var sd = { };
27143                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
27144                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
27145                         field.setFromData(sd);
27146                         
27147                     } else {
27148                         field.setValue(values[id]);
27149                     }
27150                     
27151                     
27152                     if(this.trackResetOnLoad){
27153                         field.originalValue = field.getValue();
27154                     }
27155                 }
27156             }
27157         }
27158          
27159         Roo.each(this.childForms || [], function (f) {
27160             f.setValues(values);
27161         });
27162                 
27163         return this;
27164     },
27165
27166     /**
27167      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
27168      * they are returned as an array.
27169      * @param {Boolean} asString
27170      * @return {Object}
27171      */
27172     getValues : function(asString){
27173         if (this.childForms) {
27174             // copy values from the child forms
27175             Roo.each(this.childForms, function (f) {
27176                 this.setValues(f.getValues());
27177             }, this);
27178         }
27179         
27180         
27181         
27182         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
27183         if(asString === true){
27184             return fs;
27185         }
27186         return Roo.urlDecode(fs);
27187     },
27188     
27189     /**
27190      * Returns the fields in this form as an object with key/value pairs. 
27191      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
27192      * @return {Object}
27193      */
27194     getFieldValues : function(with_hidden)
27195     {
27196         if (this.childForms) {
27197             // copy values from the child forms
27198             // should this call getFieldValues - probably not as we do not currently copy
27199             // hidden fields when we generate..
27200             Roo.each(this.childForms, function (f) {
27201                 this.setValues(f.getValues());
27202             }, this);
27203         }
27204         
27205         var ret = {};
27206         this.items.each(function(f){
27207             if (!f.getName()) {
27208                 return;
27209             }
27210             var v = f.getValue();
27211             // not sure if this supported any more..
27212             if ((typeof(v) == 'object') && f.getRawValue) {
27213                 v = f.getRawValue() ; // dates..
27214             }
27215             // combo boxes where name != hiddenName...
27216             if (f.name != f.getName()) {
27217                 ret[f.name] = f.getRawValue();
27218             }
27219             ret[f.getName()] = v;
27220         });
27221         
27222         return ret;
27223     },
27224
27225     /**
27226      * Clears all invalid messages in this form.
27227      * @return {BasicForm} this
27228      */
27229     clearInvalid : function(){
27230         this.items.each(function(f){
27231            f.clearInvalid();
27232         });
27233         
27234         Roo.each(this.childForms || [], function (f) {
27235             f.clearInvalid();
27236         });
27237         
27238         
27239         return this;
27240     },
27241
27242     /**
27243      * Resets this form.
27244      * @return {BasicForm} this
27245      */
27246     reset : function(){
27247         this.items.each(function(f){
27248             f.reset();
27249         });
27250         
27251         Roo.each(this.childForms || [], function (f) {
27252             f.reset();
27253         });
27254        
27255         
27256         return this;
27257     },
27258
27259     /**
27260      * Add Roo.form components to this form.
27261      * @param {Field} field1
27262      * @param {Field} field2 (optional)
27263      * @param {Field} etc (optional)
27264      * @return {BasicForm} this
27265      */
27266     add : function(){
27267         this.items.addAll(Array.prototype.slice.call(arguments, 0));
27268         return this;
27269     },
27270
27271
27272     /**
27273      * Removes a field from the items collection (does NOT remove its markup).
27274      * @param {Field} field
27275      * @return {BasicForm} this
27276      */
27277     remove : function(field){
27278         this.items.remove(field);
27279         return this;
27280     },
27281
27282     /**
27283      * Looks at the fields in this form, checks them for an id attribute,
27284      * and calls applyTo on the existing dom element with that id.
27285      * @return {BasicForm} this
27286      */
27287     render : function(){
27288         this.items.each(function(f){
27289             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
27290                 f.applyTo(f.id);
27291             }
27292         });
27293         return this;
27294     },
27295
27296     /**
27297      * Calls {@link Ext#apply} for all fields in this form with the passed object.
27298      * @param {Object} values
27299      * @return {BasicForm} this
27300      */
27301     applyToFields : function(o){
27302         this.items.each(function(f){
27303            Roo.apply(f, o);
27304         });
27305         return this;
27306     },
27307
27308     /**
27309      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
27310      * @param {Object} values
27311      * @return {BasicForm} this
27312      */
27313     applyIfToFields : function(o){
27314         this.items.each(function(f){
27315            Roo.applyIf(f, o);
27316         });
27317         return this;
27318     }
27319 });
27320
27321 // back compat
27322 Roo.BasicForm = Roo.form.BasicForm;/*
27323  * Based on:
27324  * Ext JS Library 1.1.1
27325  * Copyright(c) 2006-2007, Ext JS, LLC.
27326  *
27327  * Originally Released Under LGPL - original licence link has changed is not relivant.
27328  *
27329  * Fork - LGPL
27330  * <script type="text/javascript">
27331  */
27332
27333 /**
27334  * @class Roo.form.Form
27335  * @extends Roo.form.BasicForm
27336  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
27337  * @constructor
27338  * @param {Object} config Configuration options
27339  */
27340 Roo.form.Form = function(config){
27341     var xitems =  [];
27342     if (config.items) {
27343         xitems = config.items;
27344         delete config.items;
27345     }
27346    
27347     
27348     Roo.form.Form.superclass.constructor.call(this, null, config);
27349     this.url = this.url || this.action;
27350     if(!this.root){
27351         this.root = new Roo.form.Layout(Roo.applyIf({
27352             id: Roo.id()
27353         }, config));
27354     }
27355     this.active = this.root;
27356     /**
27357      * Array of all the buttons that have been added to this form via {@link addButton}
27358      * @type Array
27359      */
27360     this.buttons = [];
27361     this.allItems = [];
27362     this.addEvents({
27363         /**
27364          * @event clientvalidation
27365          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
27366          * @param {Form} this
27367          * @param {Boolean} valid true if the form has passed client-side validation
27368          */
27369         clientvalidation: true,
27370         /**
27371          * @event rendered
27372          * Fires when the form is rendered
27373          * @param {Roo.form.Form} form
27374          */
27375         rendered : true
27376     });
27377     
27378     if (this.progressUrl) {
27379             // push a hidden field onto the list of fields..
27380             this.addxtype( {
27381                     xns: Roo.form, 
27382                     xtype : 'Hidden', 
27383                     name : 'UPLOAD_IDENTIFIER' 
27384             });
27385         }
27386         
27387     
27388     Roo.each(xitems, this.addxtype, this);
27389     
27390     
27391     
27392 };
27393
27394 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
27395     /**
27396      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
27397      */
27398     /**
27399      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
27400      */
27401     /**
27402      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
27403      */
27404     buttonAlign:'center',
27405
27406     /**
27407      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
27408      */
27409     minButtonWidth:75,
27410
27411     /**
27412      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
27413      * This property cascades to child containers if not set.
27414      */
27415     labelAlign:'left',
27416
27417     /**
27418      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
27419      * fires a looping event with that state. This is required to bind buttons to the valid
27420      * state using the config value formBind:true on the button.
27421      */
27422     monitorValid : false,
27423
27424     /**
27425      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
27426      */
27427     monitorPoll : 200,
27428     
27429     /**
27430      * @cfg {String} progressUrl - Url to return progress data 
27431      */
27432     
27433     progressUrl : false,
27434   
27435     /**
27436      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
27437      * fields are added and the column is closed. If no fields are passed the column remains open
27438      * until end() is called.
27439      * @param {Object} config The config to pass to the column
27440      * @param {Field} field1 (optional)
27441      * @param {Field} field2 (optional)
27442      * @param {Field} etc (optional)
27443      * @return Column The column container object
27444      */
27445     column : function(c){
27446         var col = new Roo.form.Column(c);
27447         this.start(col);
27448         if(arguments.length > 1){ // duplicate code required because of Opera
27449             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
27450             this.end();
27451         }
27452         return col;
27453     },
27454
27455     /**
27456      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
27457      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
27458      * until end() is called.
27459      * @param {Object} config The config to pass to the fieldset
27460      * @param {Field} field1 (optional)
27461      * @param {Field} field2 (optional)
27462      * @param {Field} etc (optional)
27463      * @return FieldSet The fieldset container object
27464      */
27465     fieldset : function(c){
27466         var fs = new Roo.form.FieldSet(c);
27467         this.start(fs);
27468         if(arguments.length > 1){ // duplicate code required because of Opera
27469             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
27470             this.end();
27471         }
27472         return fs;
27473     },
27474
27475     /**
27476      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
27477      * fields are added and the container is closed. If no fields are passed the container remains open
27478      * until end() is called.
27479      * @param {Object} config The config to pass to the Layout
27480      * @param {Field} field1 (optional)
27481      * @param {Field} field2 (optional)
27482      * @param {Field} etc (optional)
27483      * @return Layout The container object
27484      */
27485     container : function(c){
27486         var l = new Roo.form.Layout(c);
27487         this.start(l);
27488         if(arguments.length > 1){ // duplicate code required because of Opera
27489             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
27490             this.end();
27491         }
27492         return l;
27493     },
27494
27495     /**
27496      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
27497      * @param {Object} container A Roo.form.Layout or subclass of Layout
27498      * @return {Form} this
27499      */
27500     start : function(c){
27501         // cascade label info
27502         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
27503         this.active.stack.push(c);
27504         c.ownerCt = this.active;
27505         this.active = c;
27506         return this;
27507     },
27508
27509     /**
27510      * Closes the current open container
27511      * @return {Form} this
27512      */
27513     end : function(){
27514         if(this.active == this.root){
27515             return this;
27516         }
27517         this.active = this.active.ownerCt;
27518         return this;
27519     },
27520
27521     /**
27522      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
27523      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
27524      * as the label of the field.
27525      * @param {Field} field1
27526      * @param {Field} field2 (optional)
27527      * @param {Field} etc. (optional)
27528      * @return {Form} this
27529      */
27530     add : function(){
27531         this.active.stack.push.apply(this.active.stack, arguments);
27532         this.allItems.push.apply(this.allItems,arguments);
27533         var r = [];
27534         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
27535             if(a[i].isFormField){
27536                 r.push(a[i]);
27537             }
27538         }
27539         if(r.length > 0){
27540             Roo.form.Form.superclass.add.apply(this, r);
27541         }
27542         return this;
27543     },
27544     
27545
27546     
27547     
27548     
27549      /**
27550      * Find any element that has been added to a form, using it's ID or name
27551      * This can include framesets, columns etc. along with regular fields..
27552      * @param {String} id - id or name to find.
27553      
27554      * @return {Element} e - or false if nothing found.
27555      */
27556     findbyId : function(id)
27557     {
27558         var ret = false;
27559         if (!id) {
27560             return ret;
27561         }
27562         Roo.each(this.allItems, function(f){
27563             if (f.id == id || f.name == id ){
27564                 ret = f;
27565                 return false;
27566             }
27567         });
27568         return ret;
27569     },
27570
27571     
27572     
27573     /**
27574      * Render this form into the passed container. This should only be called once!
27575      * @param {String/HTMLElement/Element} container The element this component should be rendered into
27576      * @return {Form} this
27577      */
27578     render : function(ct)
27579     {
27580         
27581         
27582         
27583         ct = Roo.get(ct);
27584         var o = this.autoCreate || {
27585             tag: 'form',
27586             method : this.method || 'POST',
27587             id : this.id || Roo.id()
27588         };
27589         this.initEl(ct.createChild(o));
27590
27591         this.root.render(this.el);
27592         
27593        
27594              
27595         this.items.each(function(f){
27596             f.render('x-form-el-'+f.id);
27597         });
27598
27599         if(this.buttons.length > 0){
27600             // tables are required to maintain order and for correct IE layout
27601             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
27602                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
27603                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
27604             }}, null, true);
27605             var tr = tb.getElementsByTagName('tr')[0];
27606             for(var i = 0, len = this.buttons.length; i < len; i++) {
27607                 var b = this.buttons[i];
27608                 var td = document.createElement('td');
27609                 td.className = 'x-form-btn-td';
27610                 b.render(tr.appendChild(td));
27611             }
27612         }
27613         if(this.monitorValid){ // initialize after render
27614             this.startMonitoring();
27615         }
27616         this.fireEvent('rendered', this);
27617         return this;
27618     },
27619
27620     /**
27621      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
27622      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
27623      * object or a valid Roo.DomHelper element config
27624      * @param {Function} handler The function called when the button is clicked
27625      * @param {Object} scope (optional) The scope of the handler function
27626      * @return {Roo.Button}
27627      */
27628     addButton : function(config, handler, scope){
27629         var bc = {
27630             handler: handler,
27631             scope: scope,
27632             minWidth: this.minButtonWidth,
27633             hideParent:true
27634         };
27635         if(typeof config == "string"){
27636             bc.text = config;
27637         }else{
27638             Roo.apply(bc, config);
27639         }
27640         var btn = new Roo.Button(null, bc);
27641         this.buttons.push(btn);
27642         return btn;
27643     },
27644
27645      /**
27646      * Adds a series of form elements (using the xtype property as the factory method.
27647      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
27648      * @param {Object} config 
27649      */
27650     
27651     addxtype : function()
27652     {
27653         var ar = Array.prototype.slice.call(arguments, 0);
27654         var ret = false;
27655         for(var i = 0; i < ar.length; i++) {
27656             if (!ar[i]) {
27657                 continue; // skip -- if this happends something invalid got sent, we 
27658                 // should ignore it, as basically that interface element will not show up
27659                 // and that should be pretty obvious!!
27660             }
27661             
27662             if (Roo.form[ar[i].xtype]) {
27663                 ar[i].form = this;
27664                 var fe = Roo.factory(ar[i], Roo.form);
27665                 if (!ret) {
27666                     ret = fe;
27667                 }
27668                 fe.form = this;
27669                 if (fe.store) {
27670                     fe.store.form = this;
27671                 }
27672                 if (fe.isLayout) {  
27673                          
27674                     this.start(fe);
27675                     this.allItems.push(fe);
27676                     if (fe.items && fe.addxtype) {
27677                         fe.addxtype.apply(fe, fe.items);
27678                         delete fe.items;
27679                     }
27680                      this.end();
27681                     continue;
27682                 }
27683                 
27684                 
27685                  
27686                 this.add(fe);
27687               //  console.log('adding ' + ar[i].xtype);
27688             }
27689             if (ar[i].xtype == 'Button') {  
27690                 //console.log('adding button');
27691                 //console.log(ar[i]);
27692                 this.addButton(ar[i]);
27693                 this.allItems.push(fe);
27694                 continue;
27695             }
27696             
27697             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
27698                 alert('end is not supported on xtype any more, use items');
27699             //    this.end();
27700             //    //console.log('adding end');
27701             }
27702             
27703         }
27704         return ret;
27705     },
27706     
27707     /**
27708      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
27709      * option "monitorValid"
27710      */
27711     startMonitoring : function(){
27712         if(!this.bound){
27713             this.bound = true;
27714             Roo.TaskMgr.start({
27715                 run : this.bindHandler,
27716                 interval : this.monitorPoll || 200,
27717                 scope: this
27718             });
27719         }
27720     },
27721
27722     /**
27723      * Stops monitoring of the valid state of this form
27724      */
27725     stopMonitoring : function(){
27726         this.bound = false;
27727     },
27728
27729     // private
27730     bindHandler : function(){
27731         if(!this.bound){
27732             return false; // stops binding
27733         }
27734         var valid = true;
27735         this.items.each(function(f){
27736             if(!f.isValid(true)){
27737                 valid = false;
27738                 return false;
27739             }
27740         });
27741         for(var i = 0, len = this.buttons.length; i < len; i++){
27742             var btn = this.buttons[i];
27743             if(btn.formBind === true && btn.disabled === valid){
27744                 btn.setDisabled(!valid);
27745             }
27746         }
27747         this.fireEvent('clientvalidation', this, valid);
27748     }
27749     
27750     
27751     
27752     
27753     
27754     
27755     
27756     
27757 });
27758
27759
27760 // back compat
27761 Roo.Form = Roo.form.Form;
27762 /*
27763  * Based on:
27764  * Ext JS Library 1.1.1
27765  * Copyright(c) 2006-2007, Ext JS, LLC.
27766  *
27767  * Originally Released Under LGPL - original licence link has changed is not relivant.
27768  *
27769  * Fork - LGPL
27770  * <script type="text/javascript">
27771  */
27772  
27773  /**
27774  * @class Roo.form.Action
27775  * Internal Class used to handle form actions
27776  * @constructor
27777  * @param {Roo.form.BasicForm} el The form element or its id
27778  * @param {Object} config Configuration options
27779  */
27780  
27781  
27782 // define the action interface
27783 Roo.form.Action = function(form, options){
27784     this.form = form;
27785     this.options = options || {};
27786 };
27787 /**
27788  * Client Validation Failed
27789  * @const 
27790  */
27791 Roo.form.Action.CLIENT_INVALID = 'client';
27792 /**
27793  * Server Validation Failed
27794  * @const 
27795  */
27796  Roo.form.Action.SERVER_INVALID = 'server';
27797  /**
27798  * Connect to Server Failed
27799  * @const 
27800  */
27801 Roo.form.Action.CONNECT_FAILURE = 'connect';
27802 /**
27803  * Reading Data from Server Failed
27804  * @const 
27805  */
27806 Roo.form.Action.LOAD_FAILURE = 'load';
27807
27808 Roo.form.Action.prototype = {
27809     type : 'default',
27810     failureType : undefined,
27811     response : undefined,
27812     result : undefined,
27813
27814     // interface method
27815     run : function(options){
27816
27817     },
27818
27819     // interface method
27820     success : function(response){
27821
27822     },
27823
27824     // interface method
27825     handleResponse : function(response){
27826
27827     },
27828
27829     // default connection failure
27830     failure : function(response){
27831         
27832         this.response = response;
27833         this.failureType = Roo.form.Action.CONNECT_FAILURE;
27834         this.form.afterAction(this, false);
27835     },
27836
27837     processResponse : function(response){
27838         this.response = response;
27839         if(!response.responseText){
27840             return true;
27841         }
27842         this.result = this.handleResponse(response);
27843         return this.result;
27844     },
27845
27846     // utility functions used internally
27847     getUrl : function(appendParams){
27848         var url = this.options.url || this.form.url || this.form.el.dom.action;
27849         if(appendParams){
27850             var p = this.getParams();
27851             if(p){
27852                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
27853             }
27854         }
27855         return url;
27856     },
27857
27858     getMethod : function(){
27859         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
27860     },
27861
27862     getParams : function(){
27863         var bp = this.form.baseParams;
27864         var p = this.options.params;
27865         if(p){
27866             if(typeof p == "object"){
27867                 p = Roo.urlEncode(Roo.applyIf(p, bp));
27868             }else if(typeof p == 'string' && bp){
27869                 p += '&' + Roo.urlEncode(bp);
27870             }
27871         }else if(bp){
27872             p = Roo.urlEncode(bp);
27873         }
27874         return p;
27875     },
27876
27877     createCallback : function(){
27878         return {
27879             success: this.success,
27880             failure: this.failure,
27881             scope: this,
27882             timeout: (this.form.timeout*1000),
27883             upload: this.form.fileUpload ? this.success : undefined
27884         };
27885     }
27886 };
27887
27888 Roo.form.Action.Submit = function(form, options){
27889     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
27890 };
27891
27892 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
27893     type : 'submit',
27894
27895     haveProgress : false,
27896     uploadComplete : false,
27897     
27898     // uploadProgress indicator.
27899     uploadProgress : function()
27900     {
27901         if (!this.form.progressUrl) {
27902             return;
27903         }
27904         
27905         if (!this.haveProgress) {
27906             Roo.MessageBox.progress("Uploading", "Uploading");
27907         }
27908         if (this.uploadComplete) {
27909            Roo.MessageBox.hide();
27910            return;
27911         }
27912         
27913         this.haveProgress = true;
27914    
27915         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
27916         
27917         var c = new Roo.data.Connection();
27918         c.request({
27919             url : this.form.progressUrl,
27920             params: {
27921                 id : uid
27922             },
27923             method: 'GET',
27924             success : function(req){
27925                //console.log(data);
27926                 var rdata = false;
27927                 var edata;
27928                 try  {
27929                    rdata = Roo.decode(req.responseText)
27930                 } catch (e) {
27931                     Roo.log("Invalid data from server..");
27932                     Roo.log(edata);
27933                     return;
27934                 }
27935                 if (!rdata || !rdata.success) {
27936                     Roo.log(rdata);
27937                     return;
27938                 }
27939                 var data = rdata.data;
27940                 
27941                 if (this.uploadComplete) {
27942                    Roo.MessageBox.hide();
27943                    return;
27944                 }
27945                    
27946                 if (data){
27947                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
27948                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
27949                     );
27950                 }
27951                 this.uploadProgress.defer(2000,this);
27952             },
27953        
27954             failure: function(data) {
27955                 Roo.log('progress url failed ');
27956                 Roo.log(data);
27957             },
27958             scope : this
27959         });
27960            
27961     },
27962     
27963     
27964     run : function()
27965     {
27966         // run get Values on the form, so it syncs any secondary forms.
27967         this.form.getValues();
27968         
27969         var o = this.options;
27970         var method = this.getMethod();
27971         var isPost = method == 'POST';
27972         if(o.clientValidation === false || this.form.isValid()){
27973             
27974             if (this.form.progressUrl) {
27975                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
27976                     (new Date() * 1) + '' + Math.random());
27977                     
27978             } 
27979             
27980             
27981             Roo.Ajax.request(Roo.apply(this.createCallback(), {
27982                 form:this.form.el.dom,
27983                 url:this.getUrl(!isPost),
27984                 method: method,
27985                 params:isPost ? this.getParams() : null,
27986                 isUpload: this.form.fileUpload
27987             }));
27988             
27989             this.uploadProgress();
27990
27991         }else if (o.clientValidation !== false){ // client validation failed
27992             this.failureType = Roo.form.Action.CLIENT_INVALID;
27993             this.form.afterAction(this, false);
27994         }
27995     },
27996
27997     success : function(response)
27998     {
27999         this.uploadComplete= true;
28000         if (this.haveProgress) {
28001             Roo.MessageBox.hide();
28002         }
28003         
28004         
28005         var result = this.processResponse(response);
28006         if(result === true || result.success){
28007             this.form.afterAction(this, true);
28008             return;
28009         }
28010         if(result.errors){
28011             this.form.markInvalid(result.errors);
28012             this.failureType = Roo.form.Action.SERVER_INVALID;
28013         }
28014         this.form.afterAction(this, false);
28015     },
28016     failure : function(response)
28017     {
28018         this.uploadComplete= true;
28019         if (this.haveProgress) {
28020             Roo.MessageBox.hide();
28021         }
28022         
28023         this.response = response;
28024         this.failureType = Roo.form.Action.CONNECT_FAILURE;
28025         this.form.afterAction(this, false);
28026     },
28027     
28028     handleResponse : function(response){
28029         if(this.form.errorReader){
28030             var rs = this.form.errorReader.read(response);
28031             var errors = [];
28032             if(rs.records){
28033                 for(var i = 0, len = rs.records.length; i < len; i++) {
28034                     var r = rs.records[i];
28035                     errors[i] = r.data;
28036                 }
28037             }
28038             if(errors.length < 1){
28039                 errors = null;
28040             }
28041             return {
28042                 success : rs.success,
28043                 errors : errors
28044             };
28045         }
28046         var ret = false;
28047         try {
28048             ret = Roo.decode(response.responseText);
28049         } catch (e) {
28050             ret = {
28051                 success: false,
28052                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
28053                 errors : []
28054             };
28055         }
28056         return ret;
28057         
28058     }
28059 });
28060
28061
28062 Roo.form.Action.Load = function(form, options){
28063     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
28064     this.reader = this.form.reader;
28065 };
28066
28067 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
28068     type : 'load',
28069
28070     run : function(){
28071         
28072         Roo.Ajax.request(Roo.apply(
28073                 this.createCallback(), {
28074                     method:this.getMethod(),
28075                     url:this.getUrl(false),
28076                     params:this.getParams()
28077         }));
28078     },
28079
28080     success : function(response){
28081         
28082         var result = this.processResponse(response);
28083         if(result === true || !result.success || !result.data){
28084             this.failureType = Roo.form.Action.LOAD_FAILURE;
28085             this.form.afterAction(this, false);
28086             return;
28087         }
28088         this.form.clearInvalid();
28089         this.form.setValues(result.data);
28090         this.form.afterAction(this, true);
28091     },
28092
28093     handleResponse : function(response){
28094         if(this.form.reader){
28095             var rs = this.form.reader.read(response);
28096             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
28097             return {
28098                 success : rs.success,
28099                 data : data
28100             };
28101         }
28102         return Roo.decode(response.responseText);
28103     }
28104 });
28105
28106 Roo.form.Action.ACTION_TYPES = {
28107     'load' : Roo.form.Action.Load,
28108     'submit' : Roo.form.Action.Submit
28109 };/*
28110  * Based on:
28111  * Ext JS Library 1.1.1
28112  * Copyright(c) 2006-2007, Ext JS, LLC.
28113  *
28114  * Originally Released Under LGPL - original licence link has changed is not relivant.
28115  *
28116  * Fork - LGPL
28117  * <script type="text/javascript">
28118  */
28119  
28120 /**
28121  * @class Roo.form.Layout
28122  * @extends Roo.Component
28123  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
28124  * @constructor
28125  * @param {Object} config Configuration options
28126  */
28127 Roo.form.Layout = function(config){
28128     var xitems = [];
28129     if (config.items) {
28130         xitems = config.items;
28131         delete config.items;
28132     }
28133     Roo.form.Layout.superclass.constructor.call(this, config);
28134     this.stack = [];
28135     Roo.each(xitems, this.addxtype, this);
28136      
28137 };
28138
28139 Roo.extend(Roo.form.Layout, Roo.Component, {
28140     /**
28141      * @cfg {String/Object} autoCreate
28142      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
28143      */
28144     /**
28145      * @cfg {String/Object/Function} style
28146      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
28147      * a function which returns such a specification.
28148      */
28149     /**
28150      * @cfg {String} labelAlign
28151      * Valid values are "left," "top" and "right" (defaults to "left")
28152      */
28153     /**
28154      * @cfg {Number} labelWidth
28155      * Fixed width in pixels of all field labels (defaults to undefined)
28156      */
28157     /**
28158      * @cfg {Boolean} clear
28159      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
28160      */
28161     clear : true,
28162     /**
28163      * @cfg {String} labelSeparator
28164      * The separator to use after field labels (defaults to ':')
28165      */
28166     labelSeparator : ':',
28167     /**
28168      * @cfg {Boolean} hideLabels
28169      * True to suppress the display of field labels in this layout (defaults to false)
28170      */
28171     hideLabels : false,
28172
28173     // private
28174     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
28175     
28176     isLayout : true,
28177     
28178     // private
28179     onRender : function(ct, position){
28180         if(this.el){ // from markup
28181             this.el = Roo.get(this.el);
28182         }else {  // generate
28183             var cfg = this.getAutoCreate();
28184             this.el = ct.createChild(cfg, position);
28185         }
28186         if(this.style){
28187             this.el.applyStyles(this.style);
28188         }
28189         if(this.labelAlign){
28190             this.el.addClass('x-form-label-'+this.labelAlign);
28191         }
28192         if(this.hideLabels){
28193             this.labelStyle = "display:none";
28194             this.elementStyle = "padding-left:0;";
28195         }else{
28196             if(typeof this.labelWidth == 'number'){
28197                 this.labelStyle = "width:"+this.labelWidth+"px;";
28198                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
28199             }
28200             if(this.labelAlign == 'top'){
28201                 this.labelStyle = "width:auto;";
28202                 this.elementStyle = "padding-left:0;";
28203             }
28204         }
28205         var stack = this.stack;
28206         var slen = stack.length;
28207         if(slen > 0){
28208             if(!this.fieldTpl){
28209                 var t = new Roo.Template(
28210                     '<div class="x-form-item {5}">',
28211                         '<label for="{0}" style="{2}">{1}{4}</label>',
28212                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
28213                         '</div>',
28214                     '</div><div class="x-form-clear-left"></div>'
28215                 );
28216                 t.disableFormats = true;
28217                 t.compile();
28218                 Roo.form.Layout.prototype.fieldTpl = t;
28219             }
28220             for(var i = 0; i < slen; i++) {
28221                 if(stack[i].isFormField){
28222                     this.renderField(stack[i]);
28223                 }else{
28224                     this.renderComponent(stack[i]);
28225                 }
28226             }
28227         }
28228         if(this.clear){
28229             this.el.createChild({cls:'x-form-clear'});
28230         }
28231     },
28232
28233     // private
28234     renderField : function(f){
28235         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
28236                f.id, //0
28237                f.fieldLabel, //1
28238                f.labelStyle||this.labelStyle||'', //2
28239                this.elementStyle||'', //3
28240                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
28241                f.itemCls||this.itemCls||''  //5
28242        ], true).getPrevSibling());
28243     },
28244
28245     // private
28246     renderComponent : function(c){
28247         c.render(c.isLayout ? this.el : this.el.createChild());    
28248     },
28249     /**
28250      * Adds a object form elements (using the xtype property as the factory method.)
28251      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
28252      * @param {Object} config 
28253      */
28254     addxtype : function(o)
28255     {
28256         // create the lement.
28257         o.form = this.form;
28258         var fe = Roo.factory(o, Roo.form);
28259         this.form.allItems.push(fe);
28260         this.stack.push(fe);
28261         
28262         if (fe.isFormField) {
28263             this.form.items.add(fe);
28264         }
28265          
28266         return fe;
28267     }
28268 });
28269
28270 /**
28271  * @class Roo.form.Column
28272  * @extends Roo.form.Layout
28273  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
28274  * @constructor
28275  * @param {Object} config Configuration options
28276  */
28277 Roo.form.Column = function(config){
28278     Roo.form.Column.superclass.constructor.call(this, config);
28279 };
28280
28281 Roo.extend(Roo.form.Column, Roo.form.Layout, {
28282     /**
28283      * @cfg {Number/String} width
28284      * The fixed width of the column in pixels or CSS value (defaults to "auto")
28285      */
28286     /**
28287      * @cfg {String/Object} autoCreate
28288      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
28289      */
28290
28291     // private
28292     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
28293
28294     // private
28295     onRender : function(ct, position){
28296         Roo.form.Column.superclass.onRender.call(this, ct, position);
28297         if(this.width){
28298             this.el.setWidth(this.width);
28299         }
28300     }
28301 });
28302
28303
28304 /**
28305  * @class Roo.form.Row
28306  * @extends Roo.form.Layout
28307  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
28308  * @constructor
28309  * @param {Object} config Configuration options
28310  */
28311
28312  
28313 Roo.form.Row = function(config){
28314     Roo.form.Row.superclass.constructor.call(this, config);
28315 };
28316  
28317 Roo.extend(Roo.form.Row, Roo.form.Layout, {
28318       /**
28319      * @cfg {Number/String} width
28320      * The fixed width of the column in pixels or CSS value (defaults to "auto")
28321      */
28322     /**
28323      * @cfg {Number/String} height
28324      * The fixed height of the column in pixels or CSS value (defaults to "auto")
28325      */
28326     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
28327     
28328     padWidth : 20,
28329     // private
28330     onRender : function(ct, position){
28331         //console.log('row render');
28332         if(!this.rowTpl){
28333             var t = new Roo.Template(
28334                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
28335                     '<label for="{0}" style="{2}">{1}{4}</label>',
28336                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
28337                     '</div>',
28338                 '</div>'
28339             );
28340             t.disableFormats = true;
28341             t.compile();
28342             Roo.form.Layout.prototype.rowTpl = t;
28343         }
28344         this.fieldTpl = this.rowTpl;
28345         
28346         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
28347         var labelWidth = 100;
28348         
28349         if ((this.labelAlign != 'top')) {
28350             if (typeof this.labelWidth == 'number') {
28351                 labelWidth = this.labelWidth
28352             }
28353             this.padWidth =  20 + labelWidth;
28354             
28355         }
28356         
28357         Roo.form.Column.superclass.onRender.call(this, ct, position);
28358         if(this.width){
28359             this.el.setWidth(this.width);
28360         }
28361         if(this.height){
28362             this.el.setHeight(this.height);
28363         }
28364     },
28365     
28366     // private
28367     renderField : function(f){
28368         f.fieldEl = this.fieldTpl.append(this.el, [
28369                f.id, f.fieldLabel,
28370                f.labelStyle||this.labelStyle||'',
28371                this.elementStyle||'',
28372                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
28373                f.itemCls||this.itemCls||'',
28374                f.width ? f.width + this.padWidth : 160 + this.padWidth
28375        ],true);
28376     }
28377 });
28378  
28379
28380 /**
28381  * @class Roo.form.FieldSet
28382  * @extends Roo.form.Layout
28383  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
28384  * @constructor
28385  * @param {Object} config Configuration options
28386  */
28387 Roo.form.FieldSet = function(config){
28388     Roo.form.FieldSet.superclass.constructor.call(this, config);
28389 };
28390
28391 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
28392     /**
28393      * @cfg {String} legend
28394      * The text to display as the legend for the FieldSet (defaults to '')
28395      */
28396     /**
28397      * @cfg {String/Object} autoCreate
28398      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
28399      */
28400
28401     // private
28402     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
28403
28404     // private
28405     onRender : function(ct, position){
28406         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
28407         if(this.legend){
28408             this.setLegend(this.legend);
28409         }
28410     },
28411
28412     // private
28413     setLegend : function(text){
28414         if(this.rendered){
28415             this.el.child('legend').update(text);
28416         }
28417     }
28418 });/*
28419  * Based on:
28420  * Ext JS Library 1.1.1
28421  * Copyright(c) 2006-2007, Ext JS, LLC.
28422  *
28423  * Originally Released Under LGPL - original licence link has changed is not relivant.
28424  *
28425  * Fork - LGPL
28426  * <script type="text/javascript">
28427  */
28428 /**
28429  * @class Roo.form.VTypes
28430  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
28431  * @singleton
28432  */
28433 Roo.form.VTypes = function(){
28434     // closure these in so they are only created once.
28435     var alpha = /^[a-zA-Z_]+$/;
28436     var alphanum = /^[a-zA-Z0-9_]+$/;
28437     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
28438     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
28439
28440     // All these messages and functions are configurable
28441     return {
28442         /**
28443          * The function used to validate email addresses
28444          * @param {String} value The email address
28445          */
28446         'email' : function(v){
28447             return email.test(v);
28448         },
28449         /**
28450          * The error text to display when the email validation function returns false
28451          * @type String
28452          */
28453         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
28454         /**
28455          * The keystroke filter mask to be applied on email input
28456          * @type RegExp
28457          */
28458         'emailMask' : /[a-z0-9_\.\-@]/i,
28459
28460         /**
28461          * The function used to validate URLs
28462          * @param {String} value The URL
28463          */
28464         'url' : function(v){
28465             return url.test(v);
28466         },
28467         /**
28468          * The error text to display when the url validation function returns false
28469          * @type String
28470          */
28471         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
28472         
28473         /**
28474          * The function used to validate alpha values
28475          * @param {String} value The value
28476          */
28477         'alpha' : function(v){
28478             return alpha.test(v);
28479         },
28480         /**
28481          * The error text to display when the alpha validation function returns false
28482          * @type String
28483          */
28484         'alphaText' : 'This field should only contain letters and _',
28485         /**
28486          * The keystroke filter mask to be applied on alpha input
28487          * @type RegExp
28488          */
28489         'alphaMask' : /[a-z_]/i,
28490
28491         /**
28492          * The function used to validate alphanumeric values
28493          * @param {String} value The value
28494          */
28495         'alphanum' : function(v){
28496             return alphanum.test(v);
28497         },
28498         /**
28499          * The error text to display when the alphanumeric validation function returns false
28500          * @type String
28501          */
28502         'alphanumText' : 'This field should only contain letters, numbers and _',
28503         /**
28504          * The keystroke filter mask to be applied on alphanumeric input
28505          * @type RegExp
28506          */
28507         'alphanumMask' : /[a-z0-9_]/i
28508     };
28509 }();//<script type="text/javascript">
28510
28511 /**
28512  * @class Roo.form.FCKeditor
28513  * @extends Roo.form.TextArea
28514  * Wrapper around the FCKEditor http://www.fckeditor.net
28515  * @constructor
28516  * Creates a new FCKeditor
28517  * @param {Object} config Configuration options
28518  */
28519 Roo.form.FCKeditor = function(config){
28520     Roo.form.FCKeditor.superclass.constructor.call(this, config);
28521     this.addEvents({
28522          /**
28523          * @event editorinit
28524          * Fired when the editor is initialized - you can add extra handlers here..
28525          * @param {FCKeditor} this
28526          * @param {Object} the FCK object.
28527          */
28528         editorinit : true
28529     });
28530     
28531     
28532 };
28533 Roo.form.FCKeditor.editors = { };
28534 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
28535 {
28536     //defaultAutoCreate : {
28537     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
28538     //},
28539     // private
28540     /**
28541      * @cfg {Object} fck options - see fck manual for details.
28542      */
28543     fckconfig : false,
28544     
28545     /**
28546      * @cfg {Object} fck toolbar set (Basic or Default)
28547      */
28548     toolbarSet : 'Basic',
28549     /**
28550      * @cfg {Object} fck BasePath
28551      */ 
28552     basePath : '/fckeditor/',
28553     
28554     
28555     frame : false,
28556     
28557     value : '',
28558     
28559    
28560     onRender : function(ct, position)
28561     {
28562         if(!this.el){
28563             this.defaultAutoCreate = {
28564                 tag: "textarea",
28565                 style:"width:300px;height:60px;",
28566                 autocomplete: "off"
28567             };
28568         }
28569         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
28570         /*
28571         if(this.grow){
28572             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
28573             if(this.preventScrollbars){
28574                 this.el.setStyle("overflow", "hidden");
28575             }
28576             this.el.setHeight(this.growMin);
28577         }
28578         */
28579         //console.log('onrender' + this.getId() );
28580         Roo.form.FCKeditor.editors[this.getId()] = this;
28581          
28582
28583         this.replaceTextarea() ;
28584         
28585     },
28586     
28587     getEditor : function() {
28588         return this.fckEditor;
28589     },
28590     /**
28591      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
28592      * @param {Mixed} value The value to set
28593      */
28594     
28595     
28596     setValue : function(value)
28597     {
28598         //console.log('setValue: ' + value);
28599         
28600         if(typeof(value) == 'undefined') { // not sure why this is happending...
28601             return;
28602         }
28603         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
28604         
28605         //if(!this.el || !this.getEditor()) {
28606         //    this.value = value;
28607             //this.setValue.defer(100,this,[value]);    
28608         //    return;
28609         //} 
28610         
28611         if(!this.getEditor()) {
28612             return;
28613         }
28614         
28615         this.getEditor().SetData(value);
28616         
28617         //
28618
28619     },
28620
28621     /**
28622      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
28623      * @return {Mixed} value The field value
28624      */
28625     getValue : function()
28626     {
28627         
28628         if (this.frame && this.frame.dom.style.display == 'none') {
28629             return Roo.form.FCKeditor.superclass.getValue.call(this);
28630         }
28631         
28632         if(!this.el || !this.getEditor()) {
28633            
28634            // this.getValue.defer(100,this); 
28635             return this.value;
28636         }
28637        
28638         
28639         var value=this.getEditor().GetData();
28640         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
28641         return Roo.form.FCKeditor.superclass.getValue.call(this);
28642         
28643
28644     },
28645
28646     /**
28647      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
28648      * @return {Mixed} value The field value
28649      */
28650     getRawValue : function()
28651     {
28652         if (this.frame && this.frame.dom.style.display == 'none') {
28653             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
28654         }
28655         
28656         if(!this.el || !this.getEditor()) {
28657             //this.getRawValue.defer(100,this); 
28658             return this.value;
28659             return;
28660         }
28661         
28662         
28663         
28664         var value=this.getEditor().GetData();
28665         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
28666         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
28667          
28668     },
28669     
28670     setSize : function(w,h) {
28671         
28672         
28673         
28674         //if (this.frame && this.frame.dom.style.display == 'none') {
28675         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
28676         //    return;
28677         //}
28678         //if(!this.el || !this.getEditor()) {
28679         //    this.setSize.defer(100,this, [w,h]); 
28680         //    return;
28681         //}
28682         
28683         
28684         
28685         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
28686         
28687         this.frame.dom.setAttribute('width', w);
28688         this.frame.dom.setAttribute('height', h);
28689         this.frame.setSize(w,h);
28690         
28691     },
28692     
28693     toggleSourceEdit : function(value) {
28694         
28695       
28696          
28697         this.el.dom.style.display = value ? '' : 'none';
28698         this.frame.dom.style.display = value ?  'none' : '';
28699         
28700     },
28701     
28702     
28703     focus: function(tag)
28704     {
28705         if (this.frame.dom.style.display == 'none') {
28706             return Roo.form.FCKeditor.superclass.focus.call(this);
28707         }
28708         if(!this.el || !this.getEditor()) {
28709             this.focus.defer(100,this, [tag]); 
28710             return;
28711         }
28712         
28713         
28714         
28715         
28716         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
28717         this.getEditor().Focus();
28718         if (tgs.length) {
28719             if (!this.getEditor().Selection.GetSelection()) {
28720                 this.focus.defer(100,this, [tag]); 
28721                 return;
28722             }
28723             
28724             
28725             var r = this.getEditor().EditorDocument.createRange();
28726             r.setStart(tgs[0],0);
28727             r.setEnd(tgs[0],0);
28728             this.getEditor().Selection.GetSelection().removeAllRanges();
28729             this.getEditor().Selection.GetSelection().addRange(r);
28730             this.getEditor().Focus();
28731         }
28732         
28733     },
28734     
28735     
28736     
28737     replaceTextarea : function()
28738     {
28739         if ( document.getElementById( this.getId() + '___Frame' ) )
28740             return ;
28741         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
28742         //{
28743             // We must check the elements firstly using the Id and then the name.
28744         var oTextarea = document.getElementById( this.getId() );
28745         
28746         var colElementsByName = document.getElementsByName( this.getId() ) ;
28747          
28748         oTextarea.style.display = 'none' ;
28749
28750         if ( oTextarea.tabIndex ) {            
28751             this.TabIndex = oTextarea.tabIndex ;
28752         }
28753         
28754         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
28755         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
28756         this.frame = Roo.get(this.getId() + '___Frame')
28757     },
28758     
28759     _getConfigHtml : function()
28760     {
28761         var sConfig = '' ;
28762
28763         for ( var o in this.fckconfig ) {
28764             sConfig += sConfig.length > 0  ? '&amp;' : '';
28765             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
28766         }
28767
28768         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
28769     },
28770     
28771     
28772     _getIFrameHtml : function()
28773     {
28774         var sFile = 'fckeditor.html' ;
28775         /* no idea what this is about..
28776         try
28777         {
28778             if ( (/fcksource=true/i).test( window.top.location.search ) )
28779                 sFile = 'fckeditor.original.html' ;
28780         }
28781         catch (e) { 
28782         */
28783
28784         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
28785         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
28786         
28787         
28788         var html = '<iframe id="' + this.getId() +
28789             '___Frame" src="' + sLink +
28790             '" width="' + this.width +
28791             '" height="' + this.height + '"' +
28792             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
28793             ' frameborder="0" scrolling="no"></iframe>' ;
28794
28795         return html ;
28796     },
28797     
28798     _insertHtmlBefore : function( html, element )
28799     {
28800         if ( element.insertAdjacentHTML )       {
28801             // IE
28802             element.insertAdjacentHTML( 'beforeBegin', html ) ;
28803         } else { // Gecko
28804             var oRange = document.createRange() ;
28805             oRange.setStartBefore( element ) ;
28806             var oFragment = oRange.createContextualFragment( html );
28807             element.parentNode.insertBefore( oFragment, element ) ;
28808         }
28809     }
28810     
28811     
28812   
28813     
28814     
28815     
28816     
28817
28818 });
28819
28820 //Roo.reg('fckeditor', Roo.form.FCKeditor);
28821
28822 function FCKeditor_OnComplete(editorInstance){
28823     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
28824     f.fckEditor = editorInstance;
28825     //console.log("loaded");
28826     f.fireEvent('editorinit', f, editorInstance);
28827
28828   
28829
28830  
28831
28832
28833
28834
28835
28836
28837
28838
28839
28840
28841
28842
28843
28844
28845
28846 //<script type="text/javascript">
28847 /**
28848  * @class Roo.form.GridField
28849  * @extends Roo.form.Field
28850  * Embed a grid (or editable grid into a form)
28851  * STATUS ALPHA
28852  * 
28853  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
28854  * it needs 
28855  * xgrid.store = Roo.data.Store
28856  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
28857  * xgrid.store.reader = Roo.data.JsonReader 
28858  * 
28859  * 
28860  * @constructor
28861  * Creates a new GridField
28862  * @param {Object} config Configuration options
28863  */
28864 Roo.form.GridField = function(config){
28865     Roo.form.GridField.superclass.constructor.call(this, config);
28866      
28867 };
28868
28869 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
28870     /**
28871      * @cfg {Number} width  - used to restrict width of grid..
28872      */
28873     width : 100,
28874     /**
28875      * @cfg {Number} height - used to restrict height of grid..
28876      */
28877     height : 50,
28878      /**
28879      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
28880          * 
28881          *}
28882      */
28883     xgrid : false, 
28884     /**
28885      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28886      * {tag: "input", type: "checkbox", autocomplete: "off"})
28887      */
28888    // defaultAutoCreate : { tag: 'div' },
28889     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28890     /**
28891      * @cfg {String} addTitle Text to include for adding a title.
28892      */
28893     addTitle : false,
28894     //
28895     onResize : function(){
28896         Roo.form.Field.superclass.onResize.apply(this, arguments);
28897     },
28898
28899     initEvents : function(){
28900         // Roo.form.Checkbox.superclass.initEvents.call(this);
28901         // has no events...
28902        
28903     },
28904
28905
28906     getResizeEl : function(){
28907         return this.wrap;
28908     },
28909
28910     getPositionEl : function(){
28911         return this.wrap;
28912     },
28913
28914     // private
28915     onRender : function(ct, position){
28916         
28917         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
28918         var style = this.style;
28919         delete this.style;
28920         
28921         Roo.form.GridField.superclass.onRender.call(this, ct, position);
28922         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
28923         this.viewEl = this.wrap.createChild({ tag: 'div' });
28924         if (style) {
28925             this.viewEl.applyStyles(style);
28926         }
28927         if (this.width) {
28928             this.viewEl.setWidth(this.width);
28929         }
28930         if (this.height) {
28931             this.viewEl.setHeight(this.height);
28932         }
28933         //if(this.inputValue !== undefined){
28934         //this.setValue(this.value);
28935         
28936         
28937         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
28938         
28939         
28940         this.grid.render();
28941         this.grid.getDataSource().on('remove', this.refreshValue, this);
28942         this.grid.getDataSource().on('update', this.refreshValue, this);
28943         this.grid.on('afteredit', this.refreshValue, this);
28944  
28945     },
28946      
28947     
28948     /**
28949      * Sets the value of the item. 
28950      * @param {String} either an object  or a string..
28951      */
28952     setValue : function(v){
28953         //this.value = v;
28954         v = v || []; // empty set..
28955         // this does not seem smart - it really only affects memoryproxy grids..
28956         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
28957             var ds = this.grid.getDataSource();
28958             // assumes a json reader..
28959             var data = {}
28960             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
28961             ds.loadData( data);
28962         }
28963         // clear selection so it does not get stale.
28964         if (this.grid.sm) { 
28965             this.grid.sm.clearSelections();
28966         }
28967         
28968         Roo.form.GridField.superclass.setValue.call(this, v);
28969         this.refreshValue();
28970         // should load data in the grid really....
28971     },
28972     
28973     // private
28974     refreshValue: function() {
28975          var val = [];
28976         this.grid.getDataSource().each(function(r) {
28977             val.push(r.data);
28978         });
28979         this.el.dom.value = Roo.encode(val);
28980     }
28981     
28982      
28983     
28984     
28985 });/*
28986  * Based on:
28987  * Ext JS Library 1.1.1
28988  * Copyright(c) 2006-2007, Ext JS, LLC.
28989  *
28990  * Originally Released Under LGPL - original licence link has changed is not relivant.
28991  *
28992  * Fork - LGPL
28993  * <script type="text/javascript">
28994  */
28995 /**
28996  * @class Roo.form.DisplayField
28997  * @extends Roo.form.Field
28998  * A generic Field to display non-editable data.
28999  * @constructor
29000  * Creates a new Display Field item.
29001  * @param {Object} config Configuration options
29002  */
29003 Roo.form.DisplayField = function(config){
29004     Roo.form.DisplayField.superclass.constructor.call(this, config);
29005     
29006 };
29007
29008 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
29009     inputType:      'hidden',
29010     allowBlank:     true,
29011     readOnly:         true,
29012     
29013  
29014     /**
29015      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
29016      */
29017     focusClass : undefined,
29018     /**
29019      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
29020      */
29021     fieldClass: 'x-form-field',
29022     
29023      /**
29024      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
29025      */
29026     valueRenderer: undefined,
29027     
29028     width: 100,
29029     /**
29030      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
29031      * {tag: "input", type: "checkbox", autocomplete: "off"})
29032      */
29033      
29034  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
29035
29036     onResize : function(){
29037         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
29038         
29039     },
29040
29041     initEvents : function(){
29042         // Roo.form.Checkbox.superclass.initEvents.call(this);
29043         // has no events...
29044        
29045     },
29046
29047
29048     getResizeEl : function(){
29049         return this.wrap;
29050     },
29051
29052     getPositionEl : function(){
29053         return this.wrap;
29054     },
29055
29056     // private
29057     onRender : function(ct, position){
29058         
29059         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
29060         //if(this.inputValue !== undefined){
29061         this.wrap = this.el.wrap();
29062         
29063         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
29064         
29065         if (this.bodyStyle) {
29066             this.viewEl.applyStyles(this.bodyStyle);
29067         }
29068         //this.viewEl.setStyle('padding', '2px');
29069         
29070         this.setValue(this.value);
29071         
29072     },
29073 /*
29074     // private
29075     initValue : Roo.emptyFn,
29076
29077   */
29078
29079         // private
29080     onClick : function(){
29081         
29082     },
29083
29084     /**
29085      * Sets the checked state of the checkbox.
29086      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
29087      */
29088     setValue : function(v){
29089         this.value = v;
29090         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
29091         // this might be called before we have a dom element..
29092         if (!this.viewEl) {
29093             return;
29094         }
29095         this.viewEl.dom.innerHTML = html;
29096         Roo.form.DisplayField.superclass.setValue.call(this, v);
29097
29098     }
29099 });/*
29100  * 
29101  * Licence- LGPL
29102  * 
29103  */
29104
29105 /**
29106  * @class Roo.form.DayPicker
29107  * @extends Roo.form.Field
29108  * A Day picker show [M] [T] [W] ....
29109  * @constructor
29110  * Creates a new Day Picker
29111  * @param {Object} config Configuration options
29112  */
29113 Roo.form.DayPicker= function(config){
29114     Roo.form.DayPicker.superclass.constructor.call(this, config);
29115      
29116 };
29117
29118 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
29119     /**
29120      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
29121      */
29122     focusClass : undefined,
29123     /**
29124      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
29125      */
29126     fieldClass: "x-form-field",
29127    
29128     /**
29129      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
29130      * {tag: "input", type: "checkbox", autocomplete: "off"})
29131      */
29132     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
29133     
29134    
29135     actionMode : 'viewEl', 
29136     //
29137     // private
29138  
29139     inputType : 'hidden',
29140     
29141      
29142     inputElement: false, // real input element?
29143     basedOn: false, // ????
29144     
29145     isFormField: true, // not sure where this is needed!!!!
29146
29147     onResize : function(){
29148         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
29149         if(!this.boxLabel){
29150             this.el.alignTo(this.wrap, 'c-c');
29151         }
29152     },
29153
29154     initEvents : function(){
29155         Roo.form.Checkbox.superclass.initEvents.call(this);
29156         this.el.on("click", this.onClick,  this);
29157         this.el.on("change", this.onClick,  this);
29158     },
29159
29160
29161     getResizeEl : function(){
29162         return this.wrap;
29163     },
29164
29165     getPositionEl : function(){
29166         return this.wrap;
29167     },
29168
29169     
29170     // private
29171     onRender : function(ct, position){
29172         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
29173        
29174         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
29175         
29176         var r1 = '<table><tr>';
29177         var r2 = '<tr class="x-form-daypick-icons">';
29178         for (var i=0; i < 7; i++) {
29179             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
29180             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
29181         }
29182         
29183         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
29184         viewEl.select('img').on('click', this.onClick, this);
29185         this.viewEl = viewEl;   
29186         
29187         
29188         // this will not work on Chrome!!!
29189         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
29190         this.el.on('propertychange', this.setFromHidden,  this);  //ie
29191         
29192         
29193           
29194
29195     },
29196
29197     // private
29198     initValue : Roo.emptyFn,
29199
29200     /**
29201      * Returns the checked state of the checkbox.
29202      * @return {Boolean} True if checked, else false
29203      */
29204     getValue : function(){
29205         return this.el.dom.value;
29206         
29207     },
29208
29209         // private
29210     onClick : function(e){ 
29211         //this.setChecked(!this.checked);
29212         Roo.get(e.target).toggleClass('x-menu-item-checked');
29213         this.refreshValue();
29214         //if(this.el.dom.checked != this.checked){
29215         //    this.setValue(this.el.dom.checked);
29216        // }
29217     },
29218     
29219     // private
29220     refreshValue : function()
29221     {
29222         var val = '';
29223         this.viewEl.select('img',true).each(function(e,i,n)  {
29224             val += e.is(".x-menu-item-checked") ? String(n) : '';
29225         });
29226         this.setValue(val, true);
29227     },
29228
29229     /**
29230      * Sets the checked state of the checkbox.
29231      * On is always based on a string comparison between inputValue and the param.
29232      * @param {Boolean/String} value - the value to set 
29233      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
29234      */
29235     setValue : function(v,suppressEvent){
29236         if (!this.el.dom) {
29237             return;
29238         }
29239         var old = this.el.dom.value ;
29240         this.el.dom.value = v;
29241         if (suppressEvent) {
29242             return ;
29243         }
29244          
29245         // update display..
29246         this.viewEl.select('img',true).each(function(e,i,n)  {
29247             
29248             var on = e.is(".x-menu-item-checked");
29249             var newv = v.indexOf(String(n)) > -1;
29250             if (on != newv) {
29251                 e.toggleClass('x-menu-item-checked');
29252             }
29253             
29254         });
29255         
29256         
29257         this.fireEvent('change', this, v, old);
29258         
29259         
29260     },
29261    
29262     // handle setting of hidden value by some other method!!?!?
29263     setFromHidden: function()
29264     {
29265         if(!this.el){
29266             return;
29267         }
29268         //console.log("SET FROM HIDDEN");
29269         //alert('setFrom hidden');
29270         this.setValue(this.el.dom.value);
29271     },
29272     
29273     onDestroy : function()
29274     {
29275         if(this.viewEl){
29276             Roo.get(this.viewEl).remove();
29277         }
29278          
29279         Roo.form.DayPicker.superclass.onDestroy.call(this);
29280     }
29281
29282 });/*
29283  * RooJS Library 1.1.1
29284  * Copyright(c) 2008-2011  Alan Knowles
29285  *
29286  * License - LGPL
29287  */
29288  
29289
29290 /**
29291  * @class Roo.form.ComboCheck
29292  * @extends Roo.form.ComboBox
29293  * A combobox for multiple select items.
29294  *
29295  * FIXME - could do with a reset button..
29296  * 
29297  * @constructor
29298  * Create a new ComboCheck
29299  * @param {Object} config Configuration options
29300  */
29301 Roo.form.ComboCheck = function(config){
29302     Roo.form.ComboCheck.superclass.constructor.call(this, config);
29303     // should verify some data...
29304     // like
29305     // hiddenName = required..
29306     // displayField = required
29307     // valudField == required
29308     var req= [ 'hiddenName', 'displayField', 'valueField' ];
29309     var _t = this;
29310     Roo.each(req, function(e) {
29311         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
29312             throw "Roo.form.ComboCheck : missing value for: " + e;
29313         }
29314     });
29315     
29316     
29317 };
29318
29319 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
29320      
29321      
29322     editable : false,
29323      
29324     selectedClass: 'x-menu-item-checked', 
29325     
29326     // private
29327     onRender : function(ct, position){
29328         var _t = this;
29329         
29330         
29331         
29332         if(!this.tpl){
29333             var cls = 'x-combo-list';
29334
29335             
29336             this.tpl =  new Roo.Template({
29337                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
29338                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
29339                    '<span>{' + this.displayField + '}</span>' +
29340                     '</div>' 
29341                 
29342             });
29343         }
29344  
29345         
29346         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
29347         this.view.singleSelect = false;
29348         this.view.multiSelect = true;
29349         this.view.toggleSelect = true;
29350         this.pageTb.add(new Roo.Toolbar.Fill(), {
29351             
29352             text: 'Done',
29353             handler: function()
29354             {
29355                 _t.collapse();
29356             }
29357         });
29358     },
29359     
29360     onViewOver : function(e, t){
29361         // do nothing...
29362         return;
29363         
29364     },
29365     
29366     onViewClick : function(doFocus,index){
29367         return;
29368         
29369     },
29370     select: function () {
29371         //Roo.log("SELECT CALLED");
29372     },
29373      
29374     selectByValue : function(xv, scrollIntoView){
29375         var ar = this.getValueArray();
29376         var sels = [];
29377         
29378         Roo.each(ar, function(v) {
29379             if(v === undefined || v === null){
29380                 return;
29381             }
29382             var r = this.findRecord(this.valueField, v);
29383             if(r){
29384                 sels.push(this.store.indexOf(r))
29385                 
29386             }
29387         },this);
29388         this.view.select(sels);
29389         return false;
29390     },
29391     
29392     
29393     
29394     onSelect : function(record, index){
29395        // Roo.log("onselect Called");
29396        // this is only called by the clear button now..
29397         this.view.clearSelections();
29398         this.setValue('[]');
29399         if (this.value != this.valueBefore) {
29400             this.fireEvent('change', this, this.value, this.valueBefore);
29401         }
29402     },
29403     getValueArray : function()
29404     {
29405         var ar = [] ;
29406         
29407         try {
29408             //Roo.log(this.value);
29409             if (typeof(this.value) == 'undefined') {
29410                 return [];
29411             }
29412             var ar = Roo.decode(this.value);
29413             return  ar instanceof Array ? ar : []; //?? valid?
29414             
29415         } catch(e) {
29416             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
29417             return [];
29418         }
29419          
29420     },
29421     expand : function ()
29422     {
29423         Roo.form.ComboCheck.superclass.expand.call(this);
29424         this.valueBefore = this.value;
29425         
29426
29427     },
29428     
29429     collapse : function(){
29430         Roo.form.ComboCheck.superclass.collapse.call(this);
29431         var sl = this.view.getSelectedIndexes();
29432         var st = this.store;
29433         var nv = [];
29434         var tv = [];
29435         var r;
29436         Roo.each(sl, function(i) {
29437             r = st.getAt(i);
29438             nv.push(r.get(this.valueField));
29439         },this);
29440         this.setValue(Roo.encode(nv));
29441         if (this.value != this.valueBefore) {
29442
29443             this.fireEvent('change', this, this.value, this.valueBefore);
29444         }
29445         
29446     },
29447     
29448     setValue : function(v){
29449         // Roo.log(v);
29450         this.value = v;
29451         
29452         var vals = this.getValueArray();
29453         var tv = [];
29454         Roo.each(vals, function(k) {
29455             var r = this.findRecord(this.valueField, k);
29456             if(r){
29457                 tv.push(r.data[this.displayField]);
29458             }else if(this.valueNotFoundText !== undefined){
29459                 tv.push( this.valueNotFoundText );
29460             }
29461         },this);
29462        // Roo.log(tv);
29463         
29464         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
29465         this.hiddenField.value = v;
29466         this.value = v;
29467     }
29468     
29469 });//<script type="text/javasscript">
29470  
29471
29472 /**
29473  * @class Roo.DDView
29474  * A DnD enabled version of Roo.View.
29475  * @param {Element/String} container The Element in which to create the View.
29476  * @param {String} tpl The template string used to create the markup for each element of the View
29477  * @param {Object} config The configuration properties. These include all the config options of
29478  * {@link Roo.View} plus some specific to this class.<br>
29479  * <p>
29480  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
29481  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
29482  * <p>
29483  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
29484 .x-view-drag-insert-above {
29485         border-top:1px dotted #3366cc;
29486 }
29487 .x-view-drag-insert-below {
29488         border-bottom:1px dotted #3366cc;
29489 }
29490 </code></pre>
29491  * 
29492  */
29493  
29494 Roo.DDView = function(container, tpl, config) {
29495     Roo.DDView.superclass.constructor.apply(this, arguments);
29496     this.getEl().setStyle("outline", "0px none");
29497     this.getEl().unselectable();
29498     if (this.dragGroup) {
29499                 this.setDraggable(this.dragGroup.split(","));
29500     }
29501     if (this.dropGroup) {
29502                 this.setDroppable(this.dropGroup.split(","));
29503     }
29504     if (this.deletable) {
29505         this.setDeletable();
29506     }
29507     this.isDirtyFlag = false;
29508         this.addEvents({
29509                 "drop" : true
29510         });
29511 };
29512
29513 Roo.extend(Roo.DDView, Roo.View, {
29514 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
29515 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
29516 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
29517 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
29518
29519         isFormField: true,
29520
29521         reset: Roo.emptyFn,
29522         
29523         clearInvalid: Roo.form.Field.prototype.clearInvalid,
29524
29525         validate: function() {
29526                 return true;
29527         },
29528         
29529         destroy: function() {
29530                 this.purgeListeners();
29531                 this.getEl.removeAllListeners();
29532                 this.getEl().remove();
29533                 if (this.dragZone) {
29534                         if (this.dragZone.destroy) {
29535                                 this.dragZone.destroy();
29536                         }
29537                 }
29538                 if (this.dropZone) {
29539                         if (this.dropZone.destroy) {
29540                                 this.dropZone.destroy();
29541                         }
29542                 }
29543         },
29544
29545 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
29546         getName: function() {
29547                 return this.name;
29548         },
29549
29550 /**     Loads the View from a JSON string representing the Records to put into the Store. */
29551         setValue: function(v) {
29552                 if (!this.store) {
29553                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
29554                 }
29555                 var data = {};
29556                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
29557                 this.store.proxy = new Roo.data.MemoryProxy(data);
29558                 this.store.load();
29559         },
29560
29561 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
29562         getValue: function() {
29563                 var result = '(';
29564                 this.store.each(function(rec) {
29565                         result += rec.id + ',';
29566                 });
29567                 return result.substr(0, result.length - 1) + ')';
29568         },
29569         
29570         getIds: function() {
29571                 var i = 0, result = new Array(this.store.getCount());
29572                 this.store.each(function(rec) {
29573                         result[i++] = rec.id;
29574                 });
29575                 return result;
29576         },
29577         
29578         isDirty: function() {
29579                 return this.isDirtyFlag;
29580         },
29581
29582 /**
29583  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
29584  *      whole Element becomes the target, and this causes the drop gesture to append.
29585  */
29586     getTargetFromEvent : function(e) {
29587                 var target = e.getTarget();
29588                 while ((target !== null) && (target.parentNode != this.el.dom)) {
29589                 target = target.parentNode;
29590                 }
29591                 if (!target) {
29592                         target = this.el.dom.lastChild || this.el.dom;
29593                 }
29594                 return target;
29595     },
29596
29597 /**
29598  *      Create the drag data which consists of an object which has the property "ddel" as
29599  *      the drag proxy element. 
29600  */
29601     getDragData : function(e) {
29602         var target = this.findItemFromChild(e.getTarget());
29603                 if(target) {
29604                         this.handleSelection(e);
29605                         var selNodes = this.getSelectedNodes();
29606             var dragData = {
29607                 source: this,
29608                 copy: this.copy || (this.allowCopy && e.ctrlKey),
29609                 nodes: selNodes,
29610                 records: []
29611                         };
29612                         var selectedIndices = this.getSelectedIndexes();
29613                         for (var i = 0; i < selectedIndices.length; i++) {
29614                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
29615                         }
29616                         if (selNodes.length == 1) {
29617                                 dragData.ddel = target.cloneNode(true); // the div element
29618                         } else {
29619                                 var div = document.createElement('div'); // create the multi element drag "ghost"
29620                                 div.className = 'multi-proxy';
29621                                 for (var i = 0, len = selNodes.length; i < len; i++) {
29622                                         div.appendChild(selNodes[i].cloneNode(true));
29623                                 }
29624                                 dragData.ddel = div;
29625                         }
29626             //console.log(dragData)
29627             //console.log(dragData.ddel.innerHTML)
29628                         return dragData;
29629                 }
29630         //console.log('nodragData')
29631                 return false;
29632     },
29633     
29634 /**     Specify to which ddGroup items in this DDView may be dragged. */
29635     setDraggable: function(ddGroup) {
29636         if (ddGroup instanceof Array) {
29637                 Roo.each(ddGroup, this.setDraggable, this);
29638                 return;
29639         }
29640         if (this.dragZone) {
29641                 this.dragZone.addToGroup(ddGroup);
29642         } else {
29643                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
29644                                 containerScroll: true,
29645                                 ddGroup: ddGroup 
29646
29647                         });
29648 //                      Draggability implies selection. DragZone's mousedown selects the element.
29649                         if (!this.multiSelect) { this.singleSelect = true; }
29650
29651 //                      Wire the DragZone's handlers up to methods in *this*
29652                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
29653                 }
29654     },
29655
29656 /**     Specify from which ddGroup this DDView accepts drops. */
29657     setDroppable: function(ddGroup) {
29658         if (ddGroup instanceof Array) {
29659                 Roo.each(ddGroup, this.setDroppable, this);
29660                 return;
29661         }
29662         if (this.dropZone) {
29663                 this.dropZone.addToGroup(ddGroup);
29664         } else {
29665                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
29666                                 containerScroll: true,
29667                                 ddGroup: ddGroup
29668                         });
29669
29670 //                      Wire the DropZone's handlers up to methods in *this*
29671                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
29672                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
29673                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
29674                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
29675                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
29676                 }
29677     },
29678
29679 /**     Decide whether to drop above or below a View node. */
29680     getDropPoint : function(e, n, dd){
29681         if (n == this.el.dom) { return "above"; }
29682                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
29683                 var c = t + (b - t) / 2;
29684                 var y = Roo.lib.Event.getPageY(e);
29685                 if(y <= c) {
29686                         return "above";
29687                 }else{
29688                         return "below";
29689                 }
29690     },
29691
29692     onNodeEnter : function(n, dd, e, data){
29693                 return false;
29694     },
29695     
29696     onNodeOver : function(n, dd, e, data){
29697                 var pt = this.getDropPoint(e, n, dd);
29698                 // set the insert point style on the target node
29699                 var dragElClass = this.dropNotAllowed;
29700                 if (pt) {
29701                         var targetElClass;
29702                         if (pt == "above"){
29703                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
29704                                 targetElClass = "x-view-drag-insert-above";
29705                         } else {
29706                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
29707                                 targetElClass = "x-view-drag-insert-below";
29708                         }
29709                         if (this.lastInsertClass != targetElClass){
29710                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
29711                                 this.lastInsertClass = targetElClass;
29712                         }
29713                 }
29714                 return dragElClass;
29715         },
29716
29717     onNodeOut : function(n, dd, e, data){
29718                 this.removeDropIndicators(n);
29719     },
29720
29721     onNodeDrop : function(n, dd, e, data){
29722         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
29723                 return false;
29724         }
29725         var pt = this.getDropPoint(e, n, dd);
29726                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
29727                 if (pt == "below") { insertAt++; }
29728                 for (var i = 0; i < data.records.length; i++) {
29729                         var r = data.records[i];
29730                         var dup = this.store.getById(r.id);
29731                         if (dup && (dd != this.dragZone)) {
29732                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
29733                         } else {
29734                                 if (data.copy) {
29735                                         this.store.insert(insertAt++, r.copy());
29736                                 } else {
29737                                         data.source.isDirtyFlag = true;
29738                                         r.store.remove(r);
29739                                         this.store.insert(insertAt++, r);
29740                                 }
29741                                 this.isDirtyFlag = true;
29742                         }
29743                 }
29744                 this.dragZone.cachedTarget = null;
29745                 return true;
29746     },
29747
29748     removeDropIndicators : function(n){
29749                 if(n){
29750                         Roo.fly(n).removeClass([
29751                                 "x-view-drag-insert-above",
29752                                 "x-view-drag-insert-below"]);
29753                         this.lastInsertClass = "_noclass";
29754                 }
29755     },
29756
29757 /**
29758  *      Utility method. Add a delete option to the DDView's context menu.
29759  *      @param {String} imageUrl The URL of the "delete" icon image.
29760  */
29761         setDeletable: function(imageUrl) {
29762                 if (!this.singleSelect && !this.multiSelect) {
29763                         this.singleSelect = true;
29764                 }
29765                 var c = this.getContextMenu();
29766                 this.contextMenu.on("itemclick", function(item) {
29767                         switch (item.id) {
29768                                 case "delete":
29769                                         this.remove(this.getSelectedIndexes());
29770                                         break;
29771                         }
29772                 }, this);
29773                 this.contextMenu.add({
29774                         icon: imageUrl,
29775                         id: "delete",
29776                         text: 'Delete'
29777                 });
29778         },
29779         
29780 /**     Return the context menu for this DDView. */
29781         getContextMenu: function() {
29782                 if (!this.contextMenu) {
29783 //                      Create the View's context menu
29784                         this.contextMenu = new Roo.menu.Menu({
29785                                 id: this.id + "-contextmenu"
29786                         });
29787                         this.el.on("contextmenu", this.showContextMenu, this);
29788                 }
29789                 return this.contextMenu;
29790         },
29791         
29792         disableContextMenu: function() {
29793                 if (this.contextMenu) {
29794                         this.el.un("contextmenu", this.showContextMenu, this);
29795                 }
29796         },
29797
29798         showContextMenu: function(e, item) {
29799         item = this.findItemFromChild(e.getTarget());
29800                 if (item) {
29801                         e.stopEvent();
29802                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
29803                         this.contextMenu.showAt(e.getXY());
29804             }
29805     },
29806
29807 /**
29808  *      Remove {@link Roo.data.Record}s at the specified indices.
29809  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
29810  */
29811     remove: function(selectedIndices) {
29812                 selectedIndices = [].concat(selectedIndices);
29813                 for (var i = 0; i < selectedIndices.length; i++) {
29814                         var rec = this.store.getAt(selectedIndices[i]);
29815                         this.store.remove(rec);
29816                 }
29817     },
29818
29819 /**
29820  *      Double click fires the event, but also, if this is draggable, and there is only one other
29821  *      related DropZone, it transfers the selected node.
29822  */
29823     onDblClick : function(e){
29824         var item = this.findItemFromChild(e.getTarget());
29825         if(item){
29826             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
29827                 return false;
29828             }
29829             if (this.dragGroup) {
29830                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
29831                     while (targets.indexOf(this.dropZone) > -1) {
29832                             targets.remove(this.dropZone);
29833                                 }
29834                     if (targets.length == 1) {
29835                                         this.dragZone.cachedTarget = null;
29836                         var el = Roo.get(targets[0].getEl());
29837                         var box = el.getBox(true);
29838                         targets[0].onNodeDrop(el.dom, {
29839                                 target: el.dom,
29840                                 xy: [box.x, box.y + box.height - 1]
29841                         }, null, this.getDragData(e));
29842                     }
29843                 }
29844         }
29845     },
29846     
29847     handleSelection: function(e) {
29848                 this.dragZone.cachedTarget = null;
29849         var item = this.findItemFromChild(e.getTarget());
29850         if (!item) {
29851                 this.clearSelections(true);
29852                 return;
29853         }
29854                 if (item && (this.multiSelect || this.singleSelect)){
29855                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
29856                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
29857                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
29858                                 this.unselect(item);
29859                         } else {
29860                                 this.select(item, this.multiSelect && e.ctrlKey);
29861                                 this.lastSelection = item;
29862                         }
29863                 }
29864     },
29865
29866     onItemClick : function(item, index, e){
29867                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
29868                         return false;
29869                 }
29870                 return true;
29871     },
29872
29873     unselect : function(nodeInfo, suppressEvent){
29874                 var node = this.getNode(nodeInfo);
29875                 if(node && this.isSelected(node)){
29876                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
29877                                 Roo.fly(node).removeClass(this.selectedClass);
29878                                 this.selections.remove(node);
29879                                 if(!suppressEvent){
29880                                         this.fireEvent("selectionchange", this, this.selections);
29881                                 }
29882                         }
29883                 }
29884     }
29885 });
29886 /*
29887  * Based on:
29888  * Ext JS Library 1.1.1
29889  * Copyright(c) 2006-2007, Ext JS, LLC.
29890  *
29891  * Originally Released Under LGPL - original licence link has changed is not relivant.
29892  *
29893  * Fork - LGPL
29894  * <script type="text/javascript">
29895  */
29896  
29897 /**
29898  * @class Roo.LayoutManager
29899  * @extends Roo.util.Observable
29900  * Base class for layout managers.
29901  */
29902 Roo.LayoutManager = function(container, config){
29903     Roo.LayoutManager.superclass.constructor.call(this);
29904     this.el = Roo.get(container);
29905     // ie scrollbar fix
29906     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
29907         document.body.scroll = "no";
29908     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
29909         this.el.position('relative');
29910     }
29911     this.id = this.el.id;
29912     this.el.addClass("x-layout-container");
29913     /** false to disable window resize monitoring @type Boolean */
29914     this.monitorWindowResize = true;
29915     this.regions = {};
29916     this.addEvents({
29917         /**
29918          * @event layout
29919          * Fires when a layout is performed. 
29920          * @param {Roo.LayoutManager} this
29921          */
29922         "layout" : true,
29923         /**
29924          * @event regionresized
29925          * Fires when the user resizes a region. 
29926          * @param {Roo.LayoutRegion} region The resized region
29927          * @param {Number} newSize The new size (width for east/west, height for north/south)
29928          */
29929         "regionresized" : true,
29930         /**
29931          * @event regioncollapsed
29932          * Fires when a region is collapsed. 
29933          * @param {Roo.LayoutRegion} region The collapsed region
29934          */
29935         "regioncollapsed" : true,
29936         /**
29937          * @event regionexpanded
29938          * Fires when a region is expanded.  
29939          * @param {Roo.LayoutRegion} region The expanded region
29940          */
29941         "regionexpanded" : true
29942     });
29943     this.updating = false;
29944     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
29945 };
29946
29947 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
29948     /**
29949      * Returns true if this layout is currently being updated
29950      * @return {Boolean}
29951      */
29952     isUpdating : function(){
29953         return this.updating; 
29954     },
29955     
29956     /**
29957      * Suspend the LayoutManager from doing auto-layouts while
29958      * making multiple add or remove calls
29959      */
29960     beginUpdate : function(){
29961         this.updating = true;    
29962     },
29963     
29964     /**
29965      * Restore auto-layouts and optionally disable the manager from performing a layout
29966      * @param {Boolean} noLayout true to disable a layout update 
29967      */
29968     endUpdate : function(noLayout){
29969         this.updating = false;
29970         if(!noLayout){
29971             this.layout();
29972         }    
29973     },
29974     
29975     layout: function(){
29976         
29977     },
29978     
29979     onRegionResized : function(region, newSize){
29980         this.fireEvent("regionresized", region, newSize);
29981         this.layout();
29982     },
29983     
29984     onRegionCollapsed : function(region){
29985         this.fireEvent("regioncollapsed", region);
29986     },
29987     
29988     onRegionExpanded : function(region){
29989         this.fireEvent("regionexpanded", region);
29990     },
29991         
29992     /**
29993      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
29994      * performs box-model adjustments.
29995      * @return {Object} The size as an object {width: (the width), height: (the height)}
29996      */
29997     getViewSize : function(){
29998         var size;
29999         if(this.el.dom != document.body){
30000             size = this.el.getSize();
30001         }else{
30002             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
30003         }
30004         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
30005         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
30006         return size;
30007     },
30008     
30009     /**
30010      * Returns the Element this layout is bound to.
30011      * @return {Roo.Element}
30012      */
30013     getEl : function(){
30014         return this.el;
30015     },
30016     
30017     /**
30018      * Returns the specified region.
30019      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
30020      * @return {Roo.LayoutRegion}
30021      */
30022     getRegion : function(target){
30023         return this.regions[target.toLowerCase()];
30024     },
30025     
30026     onWindowResize : function(){
30027         if(this.monitorWindowResize){
30028             this.layout();
30029         }
30030     }
30031 });/*
30032  * Based on:
30033  * Ext JS Library 1.1.1
30034  * Copyright(c) 2006-2007, Ext JS, LLC.
30035  *
30036  * Originally Released Under LGPL - original licence link has changed is not relivant.
30037  *
30038  * Fork - LGPL
30039  * <script type="text/javascript">
30040  */
30041 /**
30042  * @class Roo.BorderLayout
30043  * @extends Roo.LayoutManager
30044  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
30045  * please see: <br><br>
30046  * <a href="http://www.jackslocum.com/yui/2006/10/19/cross-browser-web-20-layouts-with-yahoo-ui/">Cross Browser Layouts - Part 1</a><br>
30047  * <a href="http://www.jackslocum.com/yui/2006/10/28/cross-browser-web-20-layouts-part-2-ajax-feed-viewer-20/">Cross Browser Layouts - Part 2</a><br><br>
30048  * Example:
30049  <pre><code>
30050  var layout = new Roo.BorderLayout(document.body, {
30051     north: {
30052         initialSize: 25,
30053         titlebar: false
30054     },
30055     west: {
30056         split:true,
30057         initialSize: 200,
30058         minSize: 175,
30059         maxSize: 400,
30060         titlebar: true,
30061         collapsible: true
30062     },
30063     east: {
30064         split:true,
30065         initialSize: 202,
30066         minSize: 175,
30067         maxSize: 400,
30068         titlebar: true,
30069         collapsible: true
30070     },
30071     south: {
30072         split:true,
30073         initialSize: 100,
30074         minSize: 100,
30075         maxSize: 200,
30076         titlebar: true,
30077         collapsible: true
30078     },
30079     center: {
30080         titlebar: true,
30081         autoScroll:true,
30082         resizeTabs: true,
30083         minTabWidth: 50,
30084         preferredTabWidth: 150
30085     }
30086 });
30087
30088 // shorthand
30089 var CP = Roo.ContentPanel;
30090
30091 layout.beginUpdate();
30092 layout.add("north", new CP("north", "North"));
30093 layout.add("south", new CP("south", {title: "South", closable: true}));
30094 layout.add("west", new CP("west", {title: "West"}));
30095 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
30096 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
30097 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
30098 layout.getRegion("center").showPanel("center1");
30099 layout.endUpdate();
30100 </code></pre>
30101
30102 <b>The container the layout is rendered into can be either the body element or any other element.
30103 If it is not the body element, the container needs to either be an absolute positioned element,
30104 or you will need to add "position:relative" to the css of the container.  You will also need to specify
30105 the container size if it is not the body element.</b>
30106
30107 * @constructor
30108 * Create a new BorderLayout
30109 * @param {String/HTMLElement/Element} container The container this layout is bound to
30110 * @param {Object} config Configuration options
30111  */
30112 Roo.BorderLayout = function(container, config){
30113     config = config || {};
30114     Roo.BorderLayout.superclass.constructor.call(this, container, config);
30115     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
30116     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
30117         var target = this.factory.validRegions[i];
30118         if(config[target]){
30119             this.addRegion(target, config[target]);
30120         }
30121     }
30122 };
30123
30124 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
30125     /**
30126      * Creates and adds a new region if it doesn't already exist.
30127      * @param {String} target The target region key (north, south, east, west or center).
30128      * @param {Object} config The regions config object
30129      * @return {BorderLayoutRegion} The new region
30130      */
30131     addRegion : function(target, config){
30132         if(!this.regions[target]){
30133             var r = this.factory.create(target, this, config);
30134             this.bindRegion(target, r);
30135         }
30136         return this.regions[target];
30137     },
30138
30139     // private (kinda)
30140     bindRegion : function(name, r){
30141         this.regions[name] = r;
30142         r.on("visibilitychange", this.layout, this);
30143         r.on("paneladded", this.layout, this);
30144         r.on("panelremoved", this.layout, this);
30145         r.on("invalidated", this.layout, this);
30146         r.on("resized", this.onRegionResized, this);
30147         r.on("collapsed", this.onRegionCollapsed, this);
30148         r.on("expanded", this.onRegionExpanded, this);
30149     },
30150
30151     /**
30152      * Performs a layout update.
30153      */
30154     layout : function(){
30155         if(this.updating) return;
30156         var size = this.getViewSize();
30157         var w = size.width;
30158         var h = size.height;
30159         var centerW = w;
30160         var centerH = h;
30161         var centerY = 0;
30162         var centerX = 0;
30163         //var x = 0, y = 0;
30164
30165         var rs = this.regions;
30166         var north = rs["north"];
30167         var south = rs["south"]; 
30168         var west = rs["west"];
30169         var east = rs["east"];
30170         var center = rs["center"];
30171         //if(this.hideOnLayout){ // not supported anymore
30172             //c.el.setStyle("display", "none");
30173         //}
30174         if(north && north.isVisible()){
30175             var b = north.getBox();
30176             var m = north.getMargins();
30177             b.width = w - (m.left+m.right);
30178             b.x = m.left;
30179             b.y = m.top;
30180             centerY = b.height + b.y + m.bottom;
30181             centerH -= centerY;
30182             north.updateBox(this.safeBox(b));
30183         }
30184         if(south && south.isVisible()){
30185             var b = south.getBox();
30186             var m = south.getMargins();
30187             b.width = w - (m.left+m.right);
30188             b.x = m.left;
30189             var totalHeight = (b.height + m.top + m.bottom);
30190             b.y = h - totalHeight + m.top;
30191             centerH -= totalHeight;
30192             south.updateBox(this.safeBox(b));
30193         }
30194         if(west && west.isVisible()){
30195             var b = west.getBox();
30196             var m = west.getMargins();
30197             b.height = centerH - (m.top+m.bottom);
30198             b.x = m.left;
30199             b.y = centerY + m.top;
30200             var totalWidth = (b.width + m.left + m.right);
30201             centerX += totalWidth;
30202             centerW -= totalWidth;
30203             west.updateBox(this.safeBox(b));
30204         }
30205         if(east && east.isVisible()){
30206             var b = east.getBox();
30207             var m = east.getMargins();
30208             b.height = centerH - (m.top+m.bottom);
30209             var totalWidth = (b.width + m.left + m.right);
30210             b.x = w - totalWidth + m.left;
30211             b.y = centerY + m.top;
30212             centerW -= totalWidth;
30213             east.updateBox(this.safeBox(b));
30214         }
30215         if(center){
30216             var m = center.getMargins();
30217             var centerBox = {
30218                 x: centerX + m.left,
30219                 y: centerY + m.top,
30220                 width: centerW - (m.left+m.right),
30221                 height: centerH - (m.top+m.bottom)
30222             };
30223             //if(this.hideOnLayout){
30224                 //center.el.setStyle("display", "block");
30225             //}
30226             center.updateBox(this.safeBox(centerBox));
30227         }
30228         this.el.repaint();
30229         this.fireEvent("layout", this);
30230     },
30231
30232     // private
30233     safeBox : function(box){
30234         box.width = Math.max(0, box.width);
30235         box.height = Math.max(0, box.height);
30236         return box;
30237     },
30238
30239     /**
30240      * Adds a ContentPanel (or subclass) to this layout.
30241      * @param {String} target The target region key (north, south, east, west or center).
30242      * @param {Roo.ContentPanel} panel The panel to add
30243      * @return {Roo.ContentPanel} The added panel
30244      */
30245     add : function(target, panel){
30246          
30247         target = target.toLowerCase();
30248         return this.regions[target].add(panel);
30249     },
30250
30251     /**
30252      * Remove a ContentPanel (or subclass) to this layout.
30253      * @param {String} target The target region key (north, south, east, west or center).
30254      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
30255      * @return {Roo.ContentPanel} The removed panel
30256      */
30257     remove : function(target, panel){
30258         target = target.toLowerCase();
30259         return this.regions[target].remove(panel);
30260     },
30261
30262     /**
30263      * Searches all regions for a panel with the specified id
30264      * @param {String} panelId
30265      * @return {Roo.ContentPanel} The panel or null if it wasn't found
30266      */
30267     findPanel : function(panelId){
30268         var rs = this.regions;
30269         for(var target in rs){
30270             if(typeof rs[target] != "function"){
30271                 var p = rs[target].getPanel(panelId);
30272                 if(p){
30273                     return p;
30274                 }
30275             }
30276         }
30277         return null;
30278     },
30279
30280     /**
30281      * Searches all regions for a panel with the specified id and activates (shows) it.
30282      * @param {String/ContentPanel} panelId The panels id or the panel itself
30283      * @return {Roo.ContentPanel} The shown panel or null
30284      */
30285     showPanel : function(panelId) {
30286       var rs = this.regions;
30287       for(var target in rs){
30288          var r = rs[target];
30289          if(typeof r != "function"){
30290             if(r.hasPanel(panelId)){
30291                return r.showPanel(panelId);
30292             }
30293          }
30294       }
30295       return null;
30296    },
30297
30298    /**
30299      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
30300      * @param {Roo.state.Provider} provider (optional) An alternate state provider
30301      */
30302     restoreState : function(provider){
30303         if(!provider){
30304             provider = Roo.state.Manager;
30305         }
30306         var sm = new Roo.LayoutStateManager();
30307         sm.init(this, provider);
30308     },
30309
30310     /**
30311      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
30312      * object should contain properties for each region to add ContentPanels to, and each property's value should be
30313      * a valid ContentPanel config object.  Example:
30314      * <pre><code>
30315 // Create the main layout
30316 var layout = new Roo.BorderLayout('main-ct', {
30317     west: {
30318         split:true,
30319         minSize: 175,
30320         titlebar: true
30321     },
30322     center: {
30323         title:'Components'
30324     }
30325 }, 'main-ct');
30326
30327 // Create and add multiple ContentPanels at once via configs
30328 layout.batchAdd({
30329    west: {
30330        id: 'source-files',
30331        autoCreate:true,
30332        title:'Ext Source Files',
30333        autoScroll:true,
30334        fitToFrame:true
30335    },
30336    center : {
30337        el: cview,
30338        autoScroll:true,
30339        fitToFrame:true,
30340        toolbar: tb,
30341        resizeEl:'cbody'
30342    }
30343 });
30344 </code></pre>
30345      * @param {Object} regions An object containing ContentPanel configs by region name
30346      */
30347     batchAdd : function(regions){
30348         this.beginUpdate();
30349         for(var rname in regions){
30350             var lr = this.regions[rname];
30351             if(lr){
30352                 this.addTypedPanels(lr, regions[rname]);
30353             }
30354         }
30355         this.endUpdate();
30356     },
30357
30358     // private
30359     addTypedPanels : function(lr, ps){
30360         if(typeof ps == 'string'){
30361             lr.add(new Roo.ContentPanel(ps));
30362         }
30363         else if(ps instanceof Array){
30364             for(var i =0, len = ps.length; i < len; i++){
30365                 this.addTypedPanels(lr, ps[i]);
30366             }
30367         }
30368         else if(!ps.events){ // raw config?
30369             var el = ps.el;
30370             delete ps.el; // prevent conflict
30371             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
30372         }
30373         else {  // panel object assumed!
30374             lr.add(ps);
30375         }
30376     },
30377     /**
30378      * Adds a xtype elements to the layout.
30379      * <pre><code>
30380
30381 layout.addxtype({
30382        xtype : 'ContentPanel',
30383        region: 'west',
30384        items: [ .... ]
30385    }
30386 );
30387
30388 layout.addxtype({
30389         xtype : 'NestedLayoutPanel',
30390         region: 'west',
30391         layout: {
30392            center: { },
30393            west: { }   
30394         },
30395         items : [ ... list of content panels or nested layout panels.. ]
30396    }
30397 );
30398 </code></pre>
30399      * @param {Object} cfg Xtype definition of item to add.
30400      */
30401     addxtype : function(cfg)
30402     {
30403         // basically accepts a pannel...
30404         // can accept a layout region..!?!?
30405         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
30406         
30407         if (!cfg.xtype.match(/Panel$/)) {
30408             return false;
30409         }
30410         var ret = false;
30411         
30412         if (typeof(cfg.region) == 'undefined') {
30413             Roo.log("Failed to add Panel, region was not set");
30414             Roo.log(cfg);
30415             return false;
30416         }
30417         var region = cfg.region;
30418         delete cfg.region;
30419         
30420           
30421         var xitems = [];
30422         if (cfg.items) {
30423             xitems = cfg.items;
30424             delete cfg.items;
30425         }
30426         var nb = false;
30427         
30428         switch(cfg.xtype) 
30429         {
30430             case 'ContentPanel':  // ContentPanel (el, cfg)
30431             case 'ScrollPanel':  // ContentPanel (el, cfg)
30432                 if(cfg.autoCreate) {
30433                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
30434                 } else {
30435                     var el = this.el.createChild();
30436                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
30437                 }
30438                 
30439                 this.add(region, ret);
30440                 break;
30441             
30442             
30443             case 'TreePanel': // our new panel!
30444                 cfg.el = this.el.createChild();
30445                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
30446                 this.add(region, ret);
30447                 break;
30448             
30449             case 'NestedLayoutPanel': 
30450                 // create a new Layout (which is  a Border Layout...
30451                 var el = this.el.createChild();
30452                 var clayout = cfg.layout;
30453                 delete cfg.layout;
30454                 clayout.items   = clayout.items  || [];
30455                 // replace this exitems with the clayout ones..
30456                 xitems = clayout.items;
30457                  
30458                 
30459                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
30460                     cfg.background = false;
30461                 }
30462                 var layout = new Roo.BorderLayout(el, clayout);
30463                 
30464                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
30465                 //console.log('adding nested layout panel '  + cfg.toSource());
30466                 this.add(region, ret);
30467                 nb = {}; /// find first...
30468                 break;
30469                 
30470             case 'GridPanel': 
30471             
30472                 // needs grid and region
30473                 
30474                 //var el = this.getRegion(region).el.createChild();
30475                 var el = this.el.createChild();
30476                 // create the grid first...
30477                 
30478                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
30479                 delete cfg.grid;
30480                 if (region == 'center' && this.active ) {
30481                     cfg.background = false;
30482                 }
30483                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
30484                 
30485                 this.add(region, ret);
30486                 if (cfg.background) {
30487                     ret.on('activate', function(gp) {
30488                         if (!gp.grid.rendered) {
30489                             gp.grid.render();
30490                         }
30491                     });
30492                 } else {
30493                     grid.render();
30494                 }
30495                 break;
30496            
30497                
30498                 
30499                 
30500             default: 
30501                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
30502                 return null;
30503              // GridPanel (grid, cfg)
30504             
30505         }
30506         this.beginUpdate();
30507         // add children..
30508         var region = '';
30509         var abn = {};
30510         Roo.each(xitems, function(i)  {
30511             region = nb && i.region ? i.region : false;
30512             
30513             var add = ret.addxtype(i);
30514            
30515             if (region) {
30516                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
30517                 if (!i.background) {
30518                     abn[region] = nb[region] ;
30519                 }
30520             }
30521             
30522         });
30523         this.endUpdate();
30524
30525         // make the last non-background panel active..
30526         //if (nb) { Roo.log(abn); }
30527         if (nb) {
30528             
30529             for(var r in abn) {
30530                 region = this.getRegion(r);
30531                 if (region) {
30532                     // tried using nb[r], but it does not work..
30533                      
30534                     region.showPanel(abn[r]);
30535                    
30536                 }
30537             }
30538         }
30539         return ret;
30540         
30541     }
30542 });
30543
30544 /**
30545  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
30546  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
30547  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
30548  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
30549  * <pre><code>
30550 // shorthand
30551 var CP = Roo.ContentPanel;
30552
30553 var layout = Roo.BorderLayout.create({
30554     north: {
30555         initialSize: 25,
30556         titlebar: false,
30557         panels: [new CP("north", "North")]
30558     },
30559     west: {
30560         split:true,
30561         initialSize: 200,
30562         minSize: 175,
30563         maxSize: 400,
30564         titlebar: true,
30565         collapsible: true,
30566         panels: [new CP("west", {title: "West"})]
30567     },
30568     east: {
30569         split:true,
30570         initialSize: 202,
30571         minSize: 175,
30572         maxSize: 400,
30573         titlebar: true,
30574         collapsible: true,
30575         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
30576     },
30577     south: {
30578         split:true,
30579         initialSize: 100,
30580         minSize: 100,
30581         maxSize: 200,
30582         titlebar: true,
30583         collapsible: true,
30584         panels: [new CP("south", {title: "South", closable: true})]
30585     },
30586     center: {
30587         titlebar: true,
30588         autoScroll:true,
30589         resizeTabs: true,
30590         minTabWidth: 50,
30591         preferredTabWidth: 150,
30592         panels: [
30593             new CP("center1", {title: "Close Me", closable: true}),
30594             new CP("center2", {title: "Center Panel", closable: false})
30595         ]
30596     }
30597 }, document.body);
30598
30599 layout.getRegion("center").showPanel("center1");
30600 </code></pre>
30601  * @param config
30602  * @param targetEl
30603  */
30604 Roo.BorderLayout.create = function(config, targetEl){
30605     var layout = new Roo.BorderLayout(targetEl || document.body, config);
30606     layout.beginUpdate();
30607     var regions = Roo.BorderLayout.RegionFactory.validRegions;
30608     for(var j = 0, jlen = regions.length; j < jlen; j++){
30609         var lr = regions[j];
30610         if(layout.regions[lr] && config[lr].panels){
30611             var r = layout.regions[lr];
30612             var ps = config[lr].panels;
30613             layout.addTypedPanels(r, ps);
30614         }
30615     }
30616     layout.endUpdate();
30617     return layout;
30618 };
30619
30620 // private
30621 Roo.BorderLayout.RegionFactory = {
30622     // private
30623     validRegions : ["north","south","east","west","center"],
30624
30625     // private
30626     create : function(target, mgr, config){
30627         target = target.toLowerCase();
30628         if(config.lightweight || config.basic){
30629             return new Roo.BasicLayoutRegion(mgr, config, target);
30630         }
30631         switch(target){
30632             case "north":
30633                 return new Roo.NorthLayoutRegion(mgr, config);
30634             case "south":
30635                 return new Roo.SouthLayoutRegion(mgr, config);
30636             case "east":
30637                 return new Roo.EastLayoutRegion(mgr, config);
30638             case "west":
30639                 return new Roo.WestLayoutRegion(mgr, config);
30640             case "center":
30641                 return new Roo.CenterLayoutRegion(mgr, config);
30642         }
30643         throw 'Layout region "'+target+'" not supported.';
30644     }
30645 };/*
30646  * Based on:
30647  * Ext JS Library 1.1.1
30648  * Copyright(c) 2006-2007, Ext JS, LLC.
30649  *
30650  * Originally Released Under LGPL - original licence link has changed is not relivant.
30651  *
30652  * Fork - LGPL
30653  * <script type="text/javascript">
30654  */
30655  
30656 /**
30657  * @class Roo.BasicLayoutRegion
30658  * @extends Roo.util.Observable
30659  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
30660  * and does not have a titlebar, tabs or any other features. All it does is size and position 
30661  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
30662  */
30663 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
30664     this.mgr = mgr;
30665     this.position  = pos;
30666     this.events = {
30667         /**
30668          * @scope Roo.BasicLayoutRegion
30669          */
30670         
30671         /**
30672          * @event beforeremove
30673          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
30674          * @param {Roo.LayoutRegion} this
30675          * @param {Roo.ContentPanel} panel The panel
30676          * @param {Object} e The cancel event object
30677          */
30678         "beforeremove" : true,
30679         /**
30680          * @event invalidated
30681          * Fires when the layout for this region is changed.
30682          * @param {Roo.LayoutRegion} this
30683          */
30684         "invalidated" : true,
30685         /**
30686          * @event visibilitychange
30687          * Fires when this region is shown or hidden 
30688          * @param {Roo.LayoutRegion} this
30689          * @param {Boolean} visibility true or false
30690          */
30691         "visibilitychange" : true,
30692         /**
30693          * @event paneladded
30694          * Fires when a panel is added. 
30695          * @param {Roo.LayoutRegion} this
30696          * @param {Roo.ContentPanel} panel The panel
30697          */
30698         "paneladded" : true,
30699         /**
30700          * @event panelremoved
30701          * Fires when a panel is removed. 
30702          * @param {Roo.LayoutRegion} this
30703          * @param {Roo.ContentPanel} panel The panel
30704          */
30705         "panelremoved" : true,
30706         /**
30707          * @event collapsed
30708          * Fires when this region is collapsed.
30709          * @param {Roo.LayoutRegion} this
30710          */
30711         "collapsed" : true,
30712         /**
30713          * @event expanded
30714          * Fires when this region is expanded.
30715          * @param {Roo.LayoutRegion} this
30716          */
30717         "expanded" : true,
30718         /**
30719          * @event slideshow
30720          * Fires when this region is slid into view.
30721          * @param {Roo.LayoutRegion} this
30722          */
30723         "slideshow" : true,
30724         /**
30725          * @event slidehide
30726          * Fires when this region slides out of view. 
30727          * @param {Roo.LayoutRegion} this
30728          */
30729         "slidehide" : true,
30730         /**
30731          * @event panelactivated
30732          * Fires when a panel is activated. 
30733          * @param {Roo.LayoutRegion} this
30734          * @param {Roo.ContentPanel} panel The activated panel
30735          */
30736         "panelactivated" : true,
30737         /**
30738          * @event resized
30739          * Fires when the user resizes this region. 
30740          * @param {Roo.LayoutRegion} this
30741          * @param {Number} newSize The new size (width for east/west, height for north/south)
30742          */
30743         "resized" : true
30744     };
30745     /** A collection of panels in this region. @type Roo.util.MixedCollection */
30746     this.panels = new Roo.util.MixedCollection();
30747     this.panels.getKey = this.getPanelId.createDelegate(this);
30748     this.box = null;
30749     this.activePanel = null;
30750     // ensure listeners are added...
30751     
30752     if (config.listeners || config.events) {
30753         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
30754             listeners : config.listeners || {},
30755             events : config.events || {}
30756         });
30757     }
30758     
30759     if(skipConfig !== true){
30760         this.applyConfig(config);
30761     }
30762 };
30763
30764 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
30765     getPanelId : function(p){
30766         return p.getId();
30767     },
30768     
30769     applyConfig : function(config){
30770         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30771         this.config = config;
30772         
30773     },
30774     
30775     /**
30776      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
30777      * the width, for horizontal (north, south) the height.
30778      * @param {Number} newSize The new width or height
30779      */
30780     resizeTo : function(newSize){
30781         var el = this.el ? this.el :
30782                  (this.activePanel ? this.activePanel.getEl() : null);
30783         if(el){
30784             switch(this.position){
30785                 case "east":
30786                 case "west":
30787                     el.setWidth(newSize);
30788                     this.fireEvent("resized", this, newSize);
30789                 break;
30790                 case "north":
30791                 case "south":
30792                     el.setHeight(newSize);
30793                     this.fireEvent("resized", this, newSize);
30794                 break;                
30795             }
30796         }
30797     },
30798     
30799     getBox : function(){
30800         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
30801     },
30802     
30803     getMargins : function(){
30804         return this.margins;
30805     },
30806     
30807     updateBox : function(box){
30808         this.box = box;
30809         var el = this.activePanel.getEl();
30810         el.dom.style.left = box.x + "px";
30811         el.dom.style.top = box.y + "px";
30812         this.activePanel.setSize(box.width, box.height);
30813     },
30814     
30815     /**
30816      * Returns the container element for this region.
30817      * @return {Roo.Element}
30818      */
30819     getEl : function(){
30820         return this.activePanel;
30821     },
30822     
30823     /**
30824      * Returns true if this region is currently visible.
30825      * @return {Boolean}
30826      */
30827     isVisible : function(){
30828         return this.activePanel ? true : false;
30829     },
30830     
30831     setActivePanel : function(panel){
30832         panel = this.getPanel(panel);
30833         if(this.activePanel && this.activePanel != panel){
30834             this.activePanel.setActiveState(false);
30835             this.activePanel.getEl().setLeftTop(-10000,-10000);
30836         }
30837         this.activePanel = panel;
30838         panel.setActiveState(true);
30839         if(this.box){
30840             panel.setSize(this.box.width, this.box.height);
30841         }
30842         this.fireEvent("panelactivated", this, panel);
30843         this.fireEvent("invalidated");
30844     },
30845     
30846     /**
30847      * Show the specified panel.
30848      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
30849      * @return {Roo.ContentPanel} The shown panel or null
30850      */
30851     showPanel : function(panel){
30852         if(panel = this.getPanel(panel)){
30853             this.setActivePanel(panel);
30854         }
30855         return panel;
30856     },
30857     
30858     /**
30859      * Get the active panel for this region.
30860      * @return {Roo.ContentPanel} The active panel or null
30861      */
30862     getActivePanel : function(){
30863         return this.activePanel;
30864     },
30865     
30866     /**
30867      * Add the passed ContentPanel(s)
30868      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30869      * @return {Roo.ContentPanel} The panel added (if only one was added)
30870      */
30871     add : function(panel){
30872         if(arguments.length > 1){
30873             for(var i = 0, len = arguments.length; i < len; i++) {
30874                 this.add(arguments[i]);
30875             }
30876             return null;
30877         }
30878         if(this.hasPanel(panel)){
30879             this.showPanel(panel);
30880             return panel;
30881         }
30882         var el = panel.getEl();
30883         if(el.dom.parentNode != this.mgr.el.dom){
30884             this.mgr.el.dom.appendChild(el.dom);
30885         }
30886         if(panel.setRegion){
30887             panel.setRegion(this);
30888         }
30889         this.panels.add(panel);
30890         el.setStyle("position", "absolute");
30891         if(!panel.background){
30892             this.setActivePanel(panel);
30893             if(this.config.initialSize && this.panels.getCount()==1){
30894                 this.resizeTo(this.config.initialSize);
30895             }
30896         }
30897         this.fireEvent("paneladded", this, panel);
30898         return panel;
30899     },
30900     
30901     /**
30902      * Returns true if the panel is in this region.
30903      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30904      * @return {Boolean}
30905      */
30906     hasPanel : function(panel){
30907         if(typeof panel == "object"){ // must be panel obj
30908             panel = panel.getId();
30909         }
30910         return this.getPanel(panel) ? true : false;
30911     },
30912     
30913     /**
30914      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30915      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30916      * @param {Boolean} preservePanel Overrides the config preservePanel option
30917      * @return {Roo.ContentPanel} The panel that was removed
30918      */
30919     remove : function(panel, preservePanel){
30920         panel = this.getPanel(panel);
30921         if(!panel){
30922             return null;
30923         }
30924         var e = {};
30925         this.fireEvent("beforeremove", this, panel, e);
30926         if(e.cancel === true){
30927             return null;
30928         }
30929         var panelId = panel.getId();
30930         this.panels.removeKey(panelId);
30931         return panel;
30932     },
30933     
30934     /**
30935      * Returns the panel specified or null if it's not in this region.
30936      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30937      * @return {Roo.ContentPanel}
30938      */
30939     getPanel : function(id){
30940         if(typeof id == "object"){ // must be panel obj
30941             return id;
30942         }
30943         return this.panels.get(id);
30944     },
30945     
30946     /**
30947      * Returns this regions position (north/south/east/west/center).
30948      * @return {String} 
30949      */
30950     getPosition: function(){
30951         return this.position;    
30952     }
30953 });/*
30954  * Based on:
30955  * Ext JS Library 1.1.1
30956  * Copyright(c) 2006-2007, Ext JS, LLC.
30957  *
30958  * Originally Released Under LGPL - original licence link has changed is not relivant.
30959  *
30960  * Fork - LGPL
30961  * <script type="text/javascript">
30962  */
30963  
30964 /**
30965  * @class Roo.LayoutRegion
30966  * @extends Roo.BasicLayoutRegion
30967  * This class represents a region in a layout manager.
30968  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
30969  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
30970  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
30971  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
30972  * @cfg {Object}    cmargins        Margins for the element when collapsed (defaults to: north/south {top: 2, left: 0, right:0, bottom: 2} or east/west {top: 0, left: 2, right:2, bottom: 0})
30973  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
30974  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
30975  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
30976  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
30977  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
30978  * @cfg {String}    title           The title for the region (overrides panel titles)
30979  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
30980  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
30981  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
30982  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
30983  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
30984  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
30985  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
30986  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
30987  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
30988  * @cfg {Boolean}   showPin         True to show a pin button
30989  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
30990  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
30991  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
30992  * @cfg {Number}    width           For East/West panels
30993  * @cfg {Number}    height          For North/South panels
30994  * @cfg {Boolean}   split           To show the splitter
30995  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
30996  */
30997 Roo.LayoutRegion = function(mgr, config, pos){
30998     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
30999     var dh = Roo.DomHelper;
31000     /** This region's container element 
31001     * @type Roo.Element */
31002     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
31003     /** This region's title element 
31004     * @type Roo.Element */
31005
31006     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
31007         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
31008         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
31009     ]}, true);
31010     this.titleEl.enableDisplayMode();
31011     /** This region's title text element 
31012     * @type HTMLElement */
31013     this.titleTextEl = this.titleEl.dom.firstChild;
31014     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
31015     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
31016     this.closeBtn.enableDisplayMode();
31017     this.closeBtn.on("click", this.closeClicked, this);
31018     this.closeBtn.hide();
31019
31020     this.createBody(config);
31021     this.visible = true;
31022     this.collapsed = false;
31023
31024     if(config.hideWhenEmpty){
31025         this.hide();
31026         this.on("paneladded", this.validateVisibility, this);
31027         this.on("panelremoved", this.validateVisibility, this);
31028     }
31029     this.applyConfig(config);
31030 };
31031
31032 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
31033
31034     createBody : function(){
31035         /** This region's body element 
31036         * @type Roo.Element */
31037         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
31038     },
31039
31040     applyConfig : function(c){
31041         if(c.collapsible && this.position != "center" && !this.collapsedEl){
31042             var dh = Roo.DomHelper;
31043             if(c.titlebar !== false){
31044                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
31045                 this.collapseBtn.on("click", this.collapse, this);
31046                 this.collapseBtn.enableDisplayMode();
31047
31048                 if(c.showPin === true || this.showPin){
31049                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
31050                     this.stickBtn.enableDisplayMode();
31051                     this.stickBtn.on("click", this.expand, this);
31052                     this.stickBtn.hide();
31053                 }
31054             }
31055             /** This region's collapsed element
31056             * @type Roo.Element */
31057             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
31058                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
31059             ]}, true);
31060             if(c.floatable !== false){
31061                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
31062                this.collapsedEl.on("click", this.collapseClick, this);
31063             }
31064
31065             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
31066                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
31067                    id: "message", unselectable: "on", style:{"float":"left"}});
31068                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
31069              }
31070             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
31071             this.expandBtn.on("click", this.expand, this);
31072         }
31073         if(this.collapseBtn){
31074             this.collapseBtn.setVisible(c.collapsible == true);
31075         }
31076         this.cmargins = c.cmargins || this.cmargins ||
31077                          (this.position == "west" || this.position == "east" ?
31078                              {top: 0, left: 2, right:2, bottom: 0} :
31079                              {top: 2, left: 0, right:0, bottom: 2});
31080         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31081         this.bottomTabs = c.tabPosition != "top";
31082         this.autoScroll = c.autoScroll || false;
31083         if(this.autoScroll){
31084             this.bodyEl.setStyle("overflow", "auto");
31085         }else{
31086             this.bodyEl.setStyle("overflow", "hidden");
31087         }
31088         //if(c.titlebar !== false){
31089             if((!c.titlebar && !c.title) || c.titlebar === false){
31090                 this.titleEl.hide();
31091             }else{
31092                 this.titleEl.show();
31093                 if(c.title){
31094                     this.titleTextEl.innerHTML = c.title;
31095                 }
31096             }
31097         //}
31098         this.duration = c.duration || .30;
31099         this.slideDuration = c.slideDuration || .45;
31100         this.config = c;
31101         if(c.collapsed){
31102             this.collapse(true);
31103         }
31104         if(c.hidden){
31105             this.hide();
31106         }
31107     },
31108     /**
31109      * Returns true if this region is currently visible.
31110      * @return {Boolean}
31111      */
31112     isVisible : function(){
31113         return this.visible;
31114     },
31115
31116     /**
31117      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
31118      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
31119      */
31120     setCollapsedTitle : function(title){
31121         title = title || "&#160;";
31122         if(this.collapsedTitleTextEl){
31123             this.collapsedTitleTextEl.innerHTML = title;
31124         }
31125     },
31126
31127     getBox : function(){
31128         var b;
31129         if(!this.collapsed){
31130             b = this.el.getBox(false, true);
31131         }else{
31132             b = this.collapsedEl.getBox(false, true);
31133         }
31134         return b;
31135     },
31136
31137     getMargins : function(){
31138         return this.collapsed ? this.cmargins : this.margins;
31139     },
31140
31141     highlight : function(){
31142         this.el.addClass("x-layout-panel-dragover");
31143     },
31144
31145     unhighlight : function(){
31146         this.el.removeClass("x-layout-panel-dragover");
31147     },
31148
31149     updateBox : function(box){
31150         this.box = box;
31151         if(!this.collapsed){
31152             this.el.dom.style.left = box.x + "px";
31153             this.el.dom.style.top = box.y + "px";
31154             this.updateBody(box.width, box.height);
31155         }else{
31156             this.collapsedEl.dom.style.left = box.x + "px";
31157             this.collapsedEl.dom.style.top = box.y + "px";
31158             this.collapsedEl.setSize(box.width, box.height);
31159         }
31160         if(this.tabs){
31161             this.tabs.autoSizeTabs();
31162         }
31163     },
31164
31165     updateBody : function(w, h){
31166         if(w !== null){
31167             this.el.setWidth(w);
31168             w -= this.el.getBorderWidth("rl");
31169             if(this.config.adjustments){
31170                 w += this.config.adjustments[0];
31171             }
31172         }
31173         if(h !== null){
31174             this.el.setHeight(h);
31175             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
31176             h -= this.el.getBorderWidth("tb");
31177             if(this.config.adjustments){
31178                 h += this.config.adjustments[1];
31179             }
31180             this.bodyEl.setHeight(h);
31181             if(this.tabs){
31182                 h = this.tabs.syncHeight(h);
31183             }
31184         }
31185         if(this.panelSize){
31186             w = w !== null ? w : this.panelSize.width;
31187             h = h !== null ? h : this.panelSize.height;
31188         }
31189         if(this.activePanel){
31190             var el = this.activePanel.getEl();
31191             w = w !== null ? w : el.getWidth();
31192             h = h !== null ? h : el.getHeight();
31193             this.panelSize = {width: w, height: h};
31194             this.activePanel.setSize(w, h);
31195         }
31196         if(Roo.isIE && this.tabs){
31197             this.tabs.el.repaint();
31198         }
31199     },
31200
31201     /**
31202      * Returns the container element for this region.
31203      * @return {Roo.Element}
31204      */
31205     getEl : function(){
31206         return this.el;
31207     },
31208
31209     /**
31210      * Hides this region.
31211      */
31212     hide : function(){
31213         if(!this.collapsed){
31214             this.el.dom.style.left = "-2000px";
31215             this.el.hide();
31216         }else{
31217             this.collapsedEl.dom.style.left = "-2000px";
31218             this.collapsedEl.hide();
31219         }
31220         this.visible = false;
31221         this.fireEvent("visibilitychange", this, false);
31222     },
31223
31224     /**
31225      * Shows this region if it was previously hidden.
31226      */
31227     show : function(){
31228         if(!this.collapsed){
31229             this.el.show();
31230         }else{
31231             this.collapsedEl.show();
31232         }
31233         this.visible = true;
31234         this.fireEvent("visibilitychange", this, true);
31235     },
31236
31237     closeClicked : function(){
31238         if(this.activePanel){
31239             this.remove(this.activePanel);
31240         }
31241     },
31242
31243     collapseClick : function(e){
31244         if(this.isSlid){
31245            e.stopPropagation();
31246            this.slideIn();
31247         }else{
31248            e.stopPropagation();
31249            this.slideOut();
31250         }
31251     },
31252
31253     /**
31254      * Collapses this region.
31255      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
31256      */
31257     collapse : function(skipAnim){
31258         if(this.collapsed) return;
31259         this.collapsed = true;
31260         if(this.split){
31261             this.split.el.hide();
31262         }
31263         if(this.config.animate && skipAnim !== true){
31264             this.fireEvent("invalidated", this);
31265             this.animateCollapse();
31266         }else{
31267             this.el.setLocation(-20000,-20000);
31268             this.el.hide();
31269             this.collapsedEl.show();
31270             this.fireEvent("collapsed", this);
31271             this.fireEvent("invalidated", this);
31272         }
31273     },
31274
31275     animateCollapse : function(){
31276         // overridden
31277     },
31278
31279     /**
31280      * Expands this region if it was previously collapsed.
31281      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
31282      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
31283      */
31284     expand : function(e, skipAnim){
31285         if(e) e.stopPropagation();
31286         if(!this.collapsed || this.el.hasActiveFx()) return;
31287         if(this.isSlid){
31288             this.afterSlideIn();
31289             skipAnim = true;
31290         }
31291         this.collapsed = false;
31292         if(this.config.animate && skipAnim !== true){
31293             this.animateExpand();
31294         }else{
31295             this.el.show();
31296             if(this.split){
31297                 this.split.el.show();
31298             }
31299             this.collapsedEl.setLocation(-2000,-2000);
31300             this.collapsedEl.hide();
31301             this.fireEvent("invalidated", this);
31302             this.fireEvent("expanded", this);
31303         }
31304     },
31305
31306     animateExpand : function(){
31307         // overridden
31308     },
31309
31310     initTabs : function()
31311     {
31312         this.bodyEl.setStyle("overflow", "hidden");
31313         var ts = new Roo.TabPanel(
31314                 this.bodyEl.dom,
31315                 {
31316                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
31317                     disableTooltips: this.config.disableTabTips,
31318                     toolbar : this.config.toolbar
31319                 }
31320         );
31321         if(this.config.hideTabs){
31322             ts.stripWrap.setDisplayed(false);
31323         }
31324         this.tabs = ts;
31325         ts.resizeTabs = this.config.resizeTabs === true;
31326         ts.minTabWidth = this.config.minTabWidth || 40;
31327         ts.maxTabWidth = this.config.maxTabWidth || 250;
31328         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
31329         ts.monitorResize = false;
31330         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
31331         ts.bodyEl.addClass('x-layout-tabs-body');
31332         this.panels.each(this.initPanelAsTab, this);
31333     },
31334
31335     initPanelAsTab : function(panel){
31336         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
31337                     this.config.closeOnTab && panel.isClosable());
31338         if(panel.tabTip !== undefined){
31339             ti.setTooltip(panel.tabTip);
31340         }
31341         ti.on("activate", function(){
31342               this.setActivePanel(panel);
31343         }, this);
31344         if(this.config.closeOnTab){
31345             ti.on("beforeclose", function(t, e){
31346                 e.cancel = true;
31347                 this.remove(panel);
31348             }, this);
31349         }
31350         return ti;
31351     },
31352
31353     updatePanelTitle : function(panel, title){
31354         if(this.activePanel == panel){
31355             this.updateTitle(title);
31356         }
31357         if(this.tabs){
31358             var ti = this.tabs.getTab(panel.getEl().id);
31359             ti.setText(title);
31360             if(panel.tabTip !== undefined){
31361                 ti.setTooltip(panel.tabTip);
31362             }
31363         }
31364     },
31365
31366     updateTitle : function(title){
31367         if(this.titleTextEl && !this.config.title){
31368             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
31369         }
31370     },
31371
31372     setActivePanel : function(panel){
31373         panel = this.getPanel(panel);
31374         if(this.activePanel && this.activePanel != panel){
31375             this.activePanel.setActiveState(false);
31376         }
31377         this.activePanel = panel;
31378         panel.setActiveState(true);
31379         if(this.panelSize){
31380             panel.setSize(this.panelSize.width, this.panelSize.height);
31381         }
31382         if(this.closeBtn){
31383             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
31384         }
31385         this.updateTitle(panel.getTitle());
31386         if(this.tabs){
31387             this.fireEvent("invalidated", this);
31388         }
31389         this.fireEvent("panelactivated", this, panel);
31390     },
31391
31392     /**
31393      * Shows the specified panel.
31394      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
31395      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
31396      */
31397     showPanel : function(panel){
31398         if(panel = this.getPanel(panel)){
31399             if(this.tabs){
31400                 var tab = this.tabs.getTab(panel.getEl().id);
31401                 if(tab.isHidden()){
31402                     this.tabs.unhideTab(tab.id);
31403                 }
31404                 tab.activate();
31405             }else{
31406                 this.setActivePanel(panel);
31407             }
31408         }
31409         return panel;
31410     },
31411
31412     /**
31413      * Get the active panel for this region.
31414      * @return {Roo.ContentPanel} The active panel or null
31415      */
31416     getActivePanel : function(){
31417         return this.activePanel;
31418     },
31419
31420     validateVisibility : function(){
31421         if(this.panels.getCount() < 1){
31422             this.updateTitle("&#160;");
31423             this.closeBtn.hide();
31424             this.hide();
31425         }else{
31426             if(!this.isVisible()){
31427                 this.show();
31428             }
31429         }
31430     },
31431
31432     /**
31433      * Adds the passed ContentPanel(s) to this region.
31434      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
31435      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
31436      */
31437     add : function(panel){
31438         if(arguments.length > 1){
31439             for(var i = 0, len = arguments.length; i < len; i++) {
31440                 this.add(arguments[i]);
31441             }
31442             return null;
31443         }
31444         if(this.hasPanel(panel)){
31445             this.showPanel(panel);
31446             return panel;
31447         }
31448         panel.setRegion(this);
31449         this.panels.add(panel);
31450         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
31451             this.bodyEl.dom.appendChild(panel.getEl().dom);
31452             if(panel.background !== true){
31453                 this.setActivePanel(panel);
31454             }
31455             this.fireEvent("paneladded", this, panel);
31456             return panel;
31457         }
31458         if(!this.tabs){
31459             this.initTabs();
31460         }else{
31461             this.initPanelAsTab(panel);
31462         }
31463         if(panel.background !== true){
31464             this.tabs.activate(panel.getEl().id);
31465         }
31466         this.fireEvent("paneladded", this, panel);
31467         return panel;
31468     },
31469
31470     /**
31471      * Hides the tab for the specified panel.
31472      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
31473      */
31474     hidePanel : function(panel){
31475         if(this.tabs && (panel = this.getPanel(panel))){
31476             this.tabs.hideTab(panel.getEl().id);
31477         }
31478     },
31479
31480     /**
31481      * Unhides the tab for a previously hidden panel.
31482      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
31483      */
31484     unhidePanel : function(panel){
31485         if(this.tabs && (panel = this.getPanel(panel))){
31486             this.tabs.unhideTab(panel.getEl().id);
31487         }
31488     },
31489
31490     clearPanels : function(){
31491         while(this.panels.getCount() > 0){
31492              this.remove(this.panels.first());
31493         }
31494     },
31495
31496     /**
31497      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
31498      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
31499      * @param {Boolean} preservePanel Overrides the config preservePanel option
31500      * @return {Roo.ContentPanel} The panel that was removed
31501      */
31502     remove : function(panel, preservePanel){
31503         panel = this.getPanel(panel);
31504         if(!panel){
31505             return null;
31506         }
31507         var e = {};
31508         this.fireEvent("beforeremove", this, panel, e);
31509         if(e.cancel === true){
31510             return null;
31511         }
31512         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
31513         var panelId = panel.getId();
31514         this.panels.removeKey(panelId);
31515         if(preservePanel){
31516             document.body.appendChild(panel.getEl().dom);
31517         }
31518         if(this.tabs){
31519             this.tabs.removeTab(panel.getEl().id);
31520         }else if (!preservePanel){
31521             this.bodyEl.dom.removeChild(panel.getEl().dom);
31522         }
31523         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
31524             var p = this.panels.first();
31525             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
31526             tempEl.appendChild(p.getEl().dom);
31527             this.bodyEl.update("");
31528             this.bodyEl.dom.appendChild(p.getEl().dom);
31529             tempEl = null;
31530             this.updateTitle(p.getTitle());
31531             this.tabs = null;
31532             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
31533             this.setActivePanel(p);
31534         }
31535         panel.setRegion(null);
31536         if(this.activePanel == panel){
31537             this.activePanel = null;
31538         }
31539         if(this.config.autoDestroy !== false && preservePanel !== true){
31540             try{panel.destroy();}catch(e){}
31541         }
31542         this.fireEvent("panelremoved", this, panel);
31543         return panel;
31544     },
31545
31546     /**
31547      * Returns the TabPanel component used by this region
31548      * @return {Roo.TabPanel}
31549      */
31550     getTabs : function(){
31551         return this.tabs;
31552     },
31553
31554     createTool : function(parentEl, className){
31555         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
31556             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
31557         btn.addClassOnOver("x-layout-tools-button-over");
31558         return btn;
31559     }
31560 });/*
31561  * Based on:
31562  * Ext JS Library 1.1.1
31563  * Copyright(c) 2006-2007, Ext JS, LLC.
31564  *
31565  * Originally Released Under LGPL - original licence link has changed is not relivant.
31566  *
31567  * Fork - LGPL
31568  * <script type="text/javascript">
31569  */
31570  
31571
31572
31573 /**
31574  * @class Roo.SplitLayoutRegion
31575  * @extends Roo.LayoutRegion
31576  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
31577  */
31578 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
31579     this.cursor = cursor;
31580     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
31581 };
31582
31583 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
31584     splitTip : "Drag to resize.",
31585     collapsibleSplitTip : "Drag to resize. Double click to hide.",
31586     useSplitTips : false,
31587
31588     applyConfig : function(config){
31589         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
31590         if(config.split){
31591             if(!this.split){
31592                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
31593                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
31594                 /** The SplitBar for this region 
31595                 * @type Roo.SplitBar */
31596                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
31597                 this.split.on("moved", this.onSplitMove, this);
31598                 this.split.useShim = config.useShim === true;
31599                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
31600                 if(this.useSplitTips){
31601                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
31602                 }
31603                 if(config.collapsible){
31604                     this.split.el.on("dblclick", this.collapse,  this);
31605                 }
31606             }
31607             if(typeof config.minSize != "undefined"){
31608                 this.split.minSize = config.minSize;
31609             }
31610             if(typeof config.maxSize != "undefined"){
31611                 this.split.maxSize = config.maxSize;
31612             }
31613             if(config.hideWhenEmpty || config.hidden || config.collapsed){
31614                 this.hideSplitter();
31615             }
31616         }
31617     },
31618
31619     getHMaxSize : function(){
31620          var cmax = this.config.maxSize || 10000;
31621          var center = this.mgr.getRegion("center");
31622          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
31623     },
31624
31625     getVMaxSize : function(){
31626          var cmax = this.config.maxSize || 10000;
31627          var center = this.mgr.getRegion("center");
31628          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
31629     },
31630
31631     onSplitMove : function(split, newSize){
31632         this.fireEvent("resized", this, newSize);
31633     },
31634     
31635     /** 
31636      * Returns the {@link Roo.SplitBar} for this region.
31637      * @return {Roo.SplitBar}
31638      */
31639     getSplitBar : function(){
31640         return this.split;
31641     },
31642     
31643     hide : function(){
31644         this.hideSplitter();
31645         Roo.SplitLayoutRegion.superclass.hide.call(this);
31646     },
31647
31648     hideSplitter : function(){
31649         if(this.split){
31650             this.split.el.setLocation(-2000,-2000);
31651             this.split.el.hide();
31652         }
31653     },
31654
31655     show : function(){
31656         if(this.split){
31657             this.split.el.show();
31658         }
31659         Roo.SplitLayoutRegion.superclass.show.call(this);
31660     },
31661     
31662     beforeSlide: function(){
31663         if(Roo.isGecko){// firefox overflow auto bug workaround
31664             this.bodyEl.clip();
31665             if(this.tabs) this.tabs.bodyEl.clip();
31666             if(this.activePanel){
31667                 this.activePanel.getEl().clip();
31668                 
31669                 if(this.activePanel.beforeSlide){
31670                     this.activePanel.beforeSlide();
31671                 }
31672             }
31673         }
31674     },
31675     
31676     afterSlide : function(){
31677         if(Roo.isGecko){// firefox overflow auto bug workaround
31678             this.bodyEl.unclip();
31679             if(this.tabs) this.tabs.bodyEl.unclip();
31680             if(this.activePanel){
31681                 this.activePanel.getEl().unclip();
31682                 if(this.activePanel.afterSlide){
31683                     this.activePanel.afterSlide();
31684                 }
31685             }
31686         }
31687     },
31688
31689     initAutoHide : function(){
31690         if(this.autoHide !== false){
31691             if(!this.autoHideHd){
31692                 var st = new Roo.util.DelayedTask(this.slideIn, this);
31693                 this.autoHideHd = {
31694                     "mouseout": function(e){
31695                         if(!e.within(this.el, true)){
31696                             st.delay(500);
31697                         }
31698                     },
31699                     "mouseover" : function(e){
31700                         st.cancel();
31701                     },
31702                     scope : this
31703                 };
31704             }
31705             this.el.on(this.autoHideHd);
31706         }
31707     },
31708
31709     clearAutoHide : function(){
31710         if(this.autoHide !== false){
31711             this.el.un("mouseout", this.autoHideHd.mouseout);
31712             this.el.un("mouseover", this.autoHideHd.mouseover);
31713         }
31714     },
31715
31716     clearMonitor : function(){
31717         Roo.get(document).un("click", this.slideInIf, this);
31718     },
31719
31720     // these names are backwards but not changed for compat
31721     slideOut : function(){
31722         if(this.isSlid || this.el.hasActiveFx()){
31723             return;
31724         }
31725         this.isSlid = true;
31726         if(this.collapseBtn){
31727             this.collapseBtn.hide();
31728         }
31729         this.closeBtnState = this.closeBtn.getStyle('display');
31730         this.closeBtn.hide();
31731         if(this.stickBtn){
31732             this.stickBtn.show();
31733         }
31734         this.el.show();
31735         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
31736         this.beforeSlide();
31737         this.el.setStyle("z-index", 10001);
31738         this.el.slideIn(this.getSlideAnchor(), {
31739             callback: function(){
31740                 this.afterSlide();
31741                 this.initAutoHide();
31742                 Roo.get(document).on("click", this.slideInIf, this);
31743                 this.fireEvent("slideshow", this);
31744             },
31745             scope: this,
31746             block: true
31747         });
31748     },
31749
31750     afterSlideIn : function(){
31751         this.clearAutoHide();
31752         this.isSlid = false;
31753         this.clearMonitor();
31754         this.el.setStyle("z-index", "");
31755         if(this.collapseBtn){
31756             this.collapseBtn.show();
31757         }
31758         this.closeBtn.setStyle('display', this.closeBtnState);
31759         if(this.stickBtn){
31760             this.stickBtn.hide();
31761         }
31762         this.fireEvent("slidehide", this);
31763     },
31764
31765     slideIn : function(cb){
31766         if(!this.isSlid || this.el.hasActiveFx()){
31767             Roo.callback(cb);
31768             return;
31769         }
31770         this.isSlid = false;
31771         this.beforeSlide();
31772         this.el.slideOut(this.getSlideAnchor(), {
31773             callback: function(){
31774                 this.el.setLeftTop(-10000, -10000);
31775                 this.afterSlide();
31776                 this.afterSlideIn();
31777                 Roo.callback(cb);
31778             },
31779             scope: this,
31780             block: true
31781         });
31782     },
31783     
31784     slideInIf : function(e){
31785         if(!e.within(this.el)){
31786             this.slideIn();
31787         }
31788     },
31789
31790     animateCollapse : function(){
31791         this.beforeSlide();
31792         this.el.setStyle("z-index", 20000);
31793         var anchor = this.getSlideAnchor();
31794         this.el.slideOut(anchor, {
31795             callback : function(){
31796                 this.el.setStyle("z-index", "");
31797                 this.collapsedEl.slideIn(anchor, {duration:.3});
31798                 this.afterSlide();
31799                 this.el.setLocation(-10000,-10000);
31800                 this.el.hide();
31801                 this.fireEvent("collapsed", this);
31802             },
31803             scope: this,
31804             block: true
31805         });
31806     },
31807
31808     animateExpand : function(){
31809         this.beforeSlide();
31810         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
31811         this.el.setStyle("z-index", 20000);
31812         this.collapsedEl.hide({
31813             duration:.1
31814         });
31815         this.el.slideIn(this.getSlideAnchor(), {
31816             callback : function(){
31817                 this.el.setStyle("z-index", "");
31818                 this.afterSlide();
31819                 if(this.split){
31820                     this.split.el.show();
31821                 }
31822                 this.fireEvent("invalidated", this);
31823                 this.fireEvent("expanded", this);
31824             },
31825             scope: this,
31826             block: true
31827         });
31828     },
31829
31830     anchors : {
31831         "west" : "left",
31832         "east" : "right",
31833         "north" : "top",
31834         "south" : "bottom"
31835     },
31836
31837     sanchors : {
31838         "west" : "l",
31839         "east" : "r",
31840         "north" : "t",
31841         "south" : "b"
31842     },
31843
31844     canchors : {
31845         "west" : "tl-tr",
31846         "east" : "tr-tl",
31847         "north" : "tl-bl",
31848         "south" : "bl-tl"
31849     },
31850
31851     getAnchor : function(){
31852         return this.anchors[this.position];
31853     },
31854
31855     getCollapseAnchor : function(){
31856         return this.canchors[this.position];
31857     },
31858
31859     getSlideAnchor : function(){
31860         return this.sanchors[this.position];
31861     },
31862
31863     getAlignAdj : function(){
31864         var cm = this.cmargins;
31865         switch(this.position){
31866             case "west":
31867                 return [0, 0];
31868             break;
31869             case "east":
31870                 return [0, 0];
31871             break;
31872             case "north":
31873                 return [0, 0];
31874             break;
31875             case "south":
31876                 return [0, 0];
31877             break;
31878         }
31879     },
31880
31881     getExpandAdj : function(){
31882         var c = this.collapsedEl, cm = this.cmargins;
31883         switch(this.position){
31884             case "west":
31885                 return [-(cm.right+c.getWidth()+cm.left), 0];
31886             break;
31887             case "east":
31888                 return [cm.right+c.getWidth()+cm.left, 0];
31889             break;
31890             case "north":
31891                 return [0, -(cm.top+cm.bottom+c.getHeight())];
31892             break;
31893             case "south":
31894                 return [0, cm.top+cm.bottom+c.getHeight()];
31895             break;
31896         }
31897     }
31898 });/*
31899  * Based on:
31900  * Ext JS Library 1.1.1
31901  * Copyright(c) 2006-2007, Ext JS, LLC.
31902  *
31903  * Originally Released Under LGPL - original licence link has changed is not relivant.
31904  *
31905  * Fork - LGPL
31906  * <script type="text/javascript">
31907  */
31908 /*
31909  * These classes are private internal classes
31910  */
31911 Roo.CenterLayoutRegion = function(mgr, config){
31912     Roo.LayoutRegion.call(this, mgr, config, "center");
31913     this.visible = true;
31914     this.minWidth = config.minWidth || 20;
31915     this.minHeight = config.minHeight || 20;
31916 };
31917
31918 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
31919     hide : function(){
31920         // center panel can't be hidden
31921     },
31922     
31923     show : function(){
31924         // center panel can't be hidden
31925     },
31926     
31927     getMinWidth: function(){
31928         return this.minWidth;
31929     },
31930     
31931     getMinHeight: function(){
31932         return this.minHeight;
31933     }
31934 });
31935
31936
31937 Roo.NorthLayoutRegion = function(mgr, config){
31938     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
31939     if(this.split){
31940         this.split.placement = Roo.SplitBar.TOP;
31941         this.split.orientation = Roo.SplitBar.VERTICAL;
31942         this.split.el.addClass("x-layout-split-v");
31943     }
31944     var size = config.initialSize || config.height;
31945     if(typeof size != "undefined"){
31946         this.el.setHeight(size);
31947     }
31948 };
31949 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
31950     orientation: Roo.SplitBar.VERTICAL,
31951     getBox : function(){
31952         if(this.collapsed){
31953             return this.collapsedEl.getBox();
31954         }
31955         var box = this.el.getBox();
31956         if(this.split){
31957             box.height += this.split.el.getHeight();
31958         }
31959         return box;
31960     },
31961     
31962     updateBox : function(box){
31963         if(this.split && !this.collapsed){
31964             box.height -= this.split.el.getHeight();
31965             this.split.el.setLeft(box.x);
31966             this.split.el.setTop(box.y+box.height);
31967             this.split.el.setWidth(box.width);
31968         }
31969         if(this.collapsed){
31970             this.updateBody(box.width, null);
31971         }
31972         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31973     }
31974 });
31975
31976 Roo.SouthLayoutRegion = function(mgr, config){
31977     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
31978     if(this.split){
31979         this.split.placement = Roo.SplitBar.BOTTOM;
31980         this.split.orientation = Roo.SplitBar.VERTICAL;
31981         this.split.el.addClass("x-layout-split-v");
31982     }
31983     var size = config.initialSize || config.height;
31984     if(typeof size != "undefined"){
31985         this.el.setHeight(size);
31986     }
31987 };
31988 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
31989     orientation: Roo.SplitBar.VERTICAL,
31990     getBox : function(){
31991         if(this.collapsed){
31992             return this.collapsedEl.getBox();
31993         }
31994         var box = this.el.getBox();
31995         if(this.split){
31996             var sh = this.split.el.getHeight();
31997             box.height += sh;
31998             box.y -= sh;
31999         }
32000         return box;
32001     },
32002     
32003     updateBox : function(box){
32004         if(this.split && !this.collapsed){
32005             var sh = this.split.el.getHeight();
32006             box.height -= sh;
32007             box.y += sh;
32008             this.split.el.setLeft(box.x);
32009             this.split.el.setTop(box.y-sh);
32010             this.split.el.setWidth(box.width);
32011         }
32012         if(this.collapsed){
32013             this.updateBody(box.width, null);
32014         }
32015         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32016     }
32017 });
32018
32019 Roo.EastLayoutRegion = function(mgr, config){
32020     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
32021     if(this.split){
32022         this.split.placement = Roo.SplitBar.RIGHT;
32023         this.split.orientation = Roo.SplitBar.HORIZONTAL;
32024         this.split.el.addClass("x-layout-split-h");
32025     }
32026     var size = config.initialSize || config.width;
32027     if(typeof size != "undefined"){
32028         this.el.setWidth(size);
32029     }
32030 };
32031 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
32032     orientation: Roo.SplitBar.HORIZONTAL,
32033     getBox : function(){
32034         if(this.collapsed){
32035             return this.collapsedEl.getBox();
32036         }
32037         var box = this.el.getBox();
32038         if(this.split){
32039             var sw = this.split.el.getWidth();
32040             box.width += sw;
32041             box.x -= sw;
32042         }
32043         return box;
32044     },
32045
32046     updateBox : function(box){
32047         if(this.split && !this.collapsed){
32048             var sw = this.split.el.getWidth();
32049             box.width -= sw;
32050             this.split.el.setLeft(box.x);
32051             this.split.el.setTop(box.y);
32052             this.split.el.setHeight(box.height);
32053             box.x += sw;
32054         }
32055         if(this.collapsed){
32056             this.updateBody(null, box.height);
32057         }
32058         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32059     }
32060 });
32061
32062 Roo.WestLayoutRegion = function(mgr, config){
32063     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
32064     if(this.split){
32065         this.split.placement = Roo.SplitBar.LEFT;
32066         this.split.orientation = Roo.SplitBar.HORIZONTAL;
32067         this.split.el.addClass("x-layout-split-h");
32068     }
32069     var size = config.initialSize || config.width;
32070     if(typeof size != "undefined"){
32071         this.el.setWidth(size);
32072     }
32073 };
32074 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
32075     orientation: Roo.SplitBar.HORIZONTAL,
32076     getBox : function(){
32077         if(this.collapsed){
32078             return this.collapsedEl.getBox();
32079         }
32080         var box = this.el.getBox();
32081         if(this.split){
32082             box.width += this.split.el.getWidth();
32083         }
32084         return box;
32085     },
32086     
32087     updateBox : function(box){
32088         if(this.split && !this.collapsed){
32089             var sw = this.split.el.getWidth();
32090             box.width -= sw;
32091             this.split.el.setLeft(box.x+box.width);
32092             this.split.el.setTop(box.y);
32093             this.split.el.setHeight(box.height);
32094         }
32095         if(this.collapsed){
32096             this.updateBody(null, box.height);
32097         }
32098         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32099     }
32100 });
32101 /*
32102  * Based on:
32103  * Ext JS Library 1.1.1
32104  * Copyright(c) 2006-2007, Ext JS, LLC.
32105  *
32106  * Originally Released Under LGPL - original licence link has changed is not relivant.
32107  *
32108  * Fork - LGPL
32109  * <script type="text/javascript">
32110  */
32111  
32112  
32113 /*
32114  * Private internal class for reading and applying state
32115  */
32116 Roo.LayoutStateManager = function(layout){
32117      // default empty state
32118      this.state = {
32119         north: {},
32120         south: {},
32121         east: {},
32122         west: {}       
32123     };
32124 };
32125
32126 Roo.LayoutStateManager.prototype = {
32127     init : function(layout, provider){
32128         this.provider = provider;
32129         var state = provider.get(layout.id+"-layout-state");
32130         if(state){
32131             var wasUpdating = layout.isUpdating();
32132             if(!wasUpdating){
32133                 layout.beginUpdate();
32134             }
32135             for(var key in state){
32136                 if(typeof state[key] != "function"){
32137                     var rstate = state[key];
32138                     var r = layout.getRegion(key);
32139                     if(r && rstate){
32140                         if(rstate.size){
32141                             r.resizeTo(rstate.size);
32142                         }
32143                         if(rstate.collapsed == true){
32144                             r.collapse(true);
32145                         }else{
32146                             r.expand(null, true);
32147                         }
32148                     }
32149                 }
32150             }
32151             if(!wasUpdating){
32152                 layout.endUpdate();
32153             }
32154             this.state = state; 
32155         }
32156         this.layout = layout;
32157         layout.on("regionresized", this.onRegionResized, this);
32158         layout.on("regioncollapsed", this.onRegionCollapsed, this);
32159         layout.on("regionexpanded", this.onRegionExpanded, this);
32160     },
32161     
32162     storeState : function(){
32163         this.provider.set(this.layout.id+"-layout-state", this.state);
32164     },
32165     
32166     onRegionResized : function(region, newSize){
32167         this.state[region.getPosition()].size = newSize;
32168         this.storeState();
32169     },
32170     
32171     onRegionCollapsed : function(region){
32172         this.state[region.getPosition()].collapsed = true;
32173         this.storeState();
32174     },
32175     
32176     onRegionExpanded : function(region){
32177         this.state[region.getPosition()].collapsed = false;
32178         this.storeState();
32179     }
32180 };/*
32181  * Based on:
32182  * Ext JS Library 1.1.1
32183  * Copyright(c) 2006-2007, Ext JS, LLC.
32184  *
32185  * Originally Released Under LGPL - original licence link has changed is not relivant.
32186  *
32187  * Fork - LGPL
32188  * <script type="text/javascript">
32189  */
32190 /**
32191  * @class Roo.ContentPanel
32192  * @extends Roo.util.Observable
32193  * A basic ContentPanel element.
32194  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
32195  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
32196  * @cfg {Boolean/Object} autoCreate True to auto generate the DOM element for this panel, or a {@link Roo.DomHelper} config of the element to create
32197  * @cfg {Boolean}   closable      True if the panel can be closed/removed
32198  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
32199  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
32200  * @cfg {Toolbar}   toolbar       A toolbar for this panel
32201  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
32202  * @cfg {String} title          The title for this panel
32203  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
32204  * @cfg {String} url            Calls {@link #setUrl} with this value
32205  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
32206  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
32207  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
32208  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
32209
32210  * @constructor
32211  * Create a new ContentPanel.
32212  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
32213  * @param {String/Object} config A string to set only the title or a config object
32214  * @param {String} content (optional) Set the HTML content for this panel
32215  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
32216  */
32217 Roo.ContentPanel = function(el, config, content){
32218     
32219      
32220     /*
32221     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
32222         config = el;
32223         el = Roo.id();
32224     }
32225     if (config && config.parentLayout) { 
32226         el = config.parentLayout.el.createChild(); 
32227     }
32228     */
32229     if(el.autoCreate){ // xtype is available if this is called from factory
32230         config = el;
32231         el = Roo.id();
32232     }
32233     this.el = Roo.get(el);
32234     if(!this.el && config && config.autoCreate){
32235         if(typeof config.autoCreate == "object"){
32236             if(!config.autoCreate.id){
32237                 config.autoCreate.id = config.id||el;
32238             }
32239             this.el = Roo.DomHelper.append(document.body,
32240                         config.autoCreate, true);
32241         }else{
32242             this.el = Roo.DomHelper.append(document.body,
32243                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
32244         }
32245     }
32246     this.closable = false;
32247     this.loaded = false;
32248     this.active = false;
32249     if(typeof config == "string"){
32250         this.title = config;
32251     }else{
32252         Roo.apply(this, config);
32253     }
32254     
32255     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
32256         this.wrapEl = this.el.wrap();
32257         this.toolbar.container = this.el.insertSibling(false, 'before');
32258         this.toolbar = new Roo.Toolbar(this.toolbar);
32259     }
32260     
32261     
32262     
32263     if(this.resizeEl){
32264         this.resizeEl = Roo.get(this.resizeEl, true);
32265     }else{
32266         this.resizeEl = this.el;
32267     }
32268     this.addEvents({
32269         /**
32270          * @event activate
32271          * Fires when this panel is activated. 
32272          * @param {Roo.ContentPanel} this
32273          */
32274         "activate" : true,
32275         /**
32276          * @event deactivate
32277          * Fires when this panel is activated. 
32278          * @param {Roo.ContentPanel} this
32279          */
32280         "deactivate" : true,
32281
32282         /**
32283          * @event resize
32284          * Fires when this panel is resized if fitToFrame is true.
32285          * @param {Roo.ContentPanel} this
32286          * @param {Number} width The width after any component adjustments
32287          * @param {Number} height The height after any component adjustments
32288          */
32289         "resize" : true,
32290         
32291          /**
32292          * @event render
32293          * Fires when this tab is created
32294          * @param {Roo.ContentPanel} this
32295          */
32296         "render" : true
32297         
32298         
32299         
32300     });
32301     if(this.autoScroll){
32302         this.resizeEl.setStyle("overflow", "auto");
32303     } else {
32304         // fix randome scrolling
32305         this.el.on('scroll', function() {
32306             Roo.log('fix random scolling');
32307             this.scrollTo('top',0); 
32308         });
32309     }
32310     content = content || this.content;
32311     if(content){
32312         this.setContent(content);
32313     }
32314     if(config && config.url){
32315         this.setUrl(this.url, this.params, this.loadOnce);
32316     }
32317     
32318     
32319     
32320     Roo.ContentPanel.superclass.constructor.call(this);
32321     
32322     this.fireEvent('render', this);
32323 };
32324
32325 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
32326     tabTip:'',
32327     setRegion : function(region){
32328         this.region = region;
32329         if(region){
32330            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
32331         }else{
32332            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
32333         } 
32334     },
32335     
32336     /**
32337      * Returns the toolbar for this Panel if one was configured. 
32338      * @return {Roo.Toolbar} 
32339      */
32340     getToolbar : function(){
32341         return this.toolbar;
32342     },
32343     
32344     setActiveState : function(active){
32345         this.active = active;
32346         if(!active){
32347             this.fireEvent("deactivate", this);
32348         }else{
32349             this.fireEvent("activate", this);
32350         }
32351     },
32352     /**
32353      * Updates this panel's element
32354      * @param {String} content The new content
32355      * @param {Boolean} loadScripts (optional) true to look for and process scripts
32356     */
32357     setContent : function(content, loadScripts){
32358         this.el.update(content, loadScripts);
32359     },
32360
32361     ignoreResize : function(w, h){
32362         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
32363             return true;
32364         }else{
32365             this.lastSize = {width: w, height: h};
32366             return false;
32367         }
32368     },
32369     /**
32370      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
32371      * @return {Roo.UpdateManager} The UpdateManager
32372      */
32373     getUpdateManager : function(){
32374         return this.el.getUpdateManager();
32375     },
32376      /**
32377      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
32378      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
32379 <pre><code>
32380 panel.load({
32381     url: "your-url.php",
32382     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
32383     callback: yourFunction,
32384     scope: yourObject, //(optional scope)
32385     discardUrl: false,
32386     nocache: false,
32387     text: "Loading...",
32388     timeout: 30,
32389     scripts: false
32390 });
32391 </code></pre>
32392      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
32393      * are shorthand for <i>disableCaching</i>, <i>indicatorText</i> and <i>loadScripts</i> and are used to set their associated property on this panel UpdateManager instance.
32394      * @param {String/Object} params (optional) The parameters to pass as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
32395      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
32396      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
32397      * @return {Roo.ContentPanel} this
32398      */
32399     load : function(){
32400         var um = this.el.getUpdateManager();
32401         um.update.apply(um, arguments);
32402         return this;
32403     },
32404
32405
32406     /**
32407      * Set a URL to be used to load the content for this panel. When this panel is activated, the content will be loaded from that URL.
32408      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
32409      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
32410      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this panel is activated. (Defaults to false)
32411      * @return {Roo.UpdateManager} The UpdateManager
32412      */
32413     setUrl : function(url, params, loadOnce){
32414         if(this.refreshDelegate){
32415             this.removeListener("activate", this.refreshDelegate);
32416         }
32417         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
32418         this.on("activate", this.refreshDelegate);
32419         return this.el.getUpdateManager();
32420     },
32421     
32422     _handleRefresh : function(url, params, loadOnce){
32423         if(!loadOnce || !this.loaded){
32424             var updater = this.el.getUpdateManager();
32425             updater.update(url, params, this._setLoaded.createDelegate(this));
32426         }
32427     },
32428     
32429     _setLoaded : function(){
32430         this.loaded = true;
32431     }, 
32432     
32433     /**
32434      * Returns this panel's id
32435      * @return {String} 
32436      */
32437     getId : function(){
32438         return this.el.id;
32439     },
32440     
32441     /** 
32442      * Returns this panel's element - used by regiosn to add.
32443      * @return {Roo.Element} 
32444      */
32445     getEl : function(){
32446         return this.wrapEl || this.el;
32447     },
32448     
32449     adjustForComponents : function(width, height){
32450         if(this.resizeEl != this.el){
32451             width -= this.el.getFrameWidth('lr');
32452             height -= this.el.getFrameWidth('tb');
32453         }
32454         if(this.toolbar){
32455             var te = this.toolbar.getEl();
32456             height -= te.getHeight();
32457             te.setWidth(width);
32458         }
32459         if(this.adjustments){
32460             width += this.adjustments[0];
32461             height += this.adjustments[1];
32462         }
32463         return {"width": width, "height": height};
32464     },
32465     
32466     setSize : function(width, height){
32467         if(this.fitToFrame && !this.ignoreResize(width, height)){
32468             if(this.fitContainer && this.resizeEl != this.el){
32469                 this.el.setSize(width, height);
32470             }
32471             var size = this.adjustForComponents(width, height);
32472             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
32473             this.fireEvent('resize', this, size.width, size.height);
32474         }
32475     },
32476     
32477     /**
32478      * Returns this panel's title
32479      * @return {String} 
32480      */
32481     getTitle : function(){
32482         return this.title;
32483     },
32484     
32485     /**
32486      * Set this panel's title
32487      * @param {String} title
32488      */
32489     setTitle : function(title){
32490         this.title = title;
32491         if(this.region){
32492             this.region.updatePanelTitle(this, title);
32493         }
32494     },
32495     
32496     /**
32497      * Returns true is this panel was configured to be closable
32498      * @return {Boolean} 
32499      */
32500     isClosable : function(){
32501         return this.closable;
32502     },
32503     
32504     beforeSlide : function(){
32505         this.el.clip();
32506         this.resizeEl.clip();
32507     },
32508     
32509     afterSlide : function(){
32510         this.el.unclip();
32511         this.resizeEl.unclip();
32512     },
32513     
32514     /**
32515      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
32516      *   Will fail silently if the {@link #setUrl} method has not been called.
32517      *   This does not activate the panel, just updates its content.
32518      */
32519     refresh : function(){
32520         if(this.refreshDelegate){
32521            this.loaded = false;
32522            this.refreshDelegate();
32523         }
32524     },
32525     
32526     /**
32527      * Destroys this panel
32528      */
32529     destroy : function(){
32530         this.el.removeAllListeners();
32531         var tempEl = document.createElement("span");
32532         tempEl.appendChild(this.el.dom);
32533         tempEl.innerHTML = "";
32534         this.el.remove();
32535         this.el = null;
32536     },
32537     
32538     /**
32539      * form - if the content panel contains a form - this is a reference to it.
32540      * @type {Roo.form.Form}
32541      */
32542     form : false,
32543     /**
32544      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
32545      *    This contains a reference to it.
32546      * @type {Roo.View}
32547      */
32548     view : false,
32549     
32550       /**
32551      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
32552      * <pre><code>
32553
32554 layout.addxtype({
32555        xtype : 'Form',
32556        items: [ .... ]
32557    }
32558 );
32559
32560 </code></pre>
32561      * @param {Object} cfg Xtype definition of item to add.
32562      */
32563     
32564     addxtype : function(cfg) {
32565         // add form..
32566         if (cfg.xtype.match(/^Form$/)) {
32567             var el = this.el.createChild();
32568
32569             this.form = new  Roo.form.Form(cfg);
32570             
32571             
32572             if ( this.form.allItems.length) this.form.render(el.dom);
32573             return this.form;
32574         }
32575         // should only have one of theses..
32576         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
32577             // views..
32578             cfg.el = this.el.appendChild(document.createElement("div"));
32579             // factory?
32580             
32581             var ret = new Roo.factory(cfg);
32582             ret.render && ret.render(false, ''); // render blank..
32583             this.view = ret;
32584             return ret;
32585         }
32586         return false;
32587     }
32588 });
32589
32590 /**
32591  * @class Roo.GridPanel
32592  * @extends Roo.ContentPanel
32593  * @constructor
32594  * Create a new GridPanel.
32595  * @param {Roo.grid.Grid} grid The grid for this panel
32596  * @param {String/Object} config A string to set only the panel's title, or a config object
32597  */
32598 Roo.GridPanel = function(grid, config){
32599     
32600   
32601     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
32602         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
32603         
32604     this.wrapper.dom.appendChild(grid.getGridEl().dom);
32605     
32606     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
32607     
32608     if(this.toolbar){
32609         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
32610     }
32611     // xtype created footer. - not sure if will work as we normally have to render first..
32612     if (this.footer && !this.footer.el && this.footer.xtype) {
32613         
32614         this.footer.container = this.grid.getView().getFooterPanel(true);
32615         this.footer.dataSource = this.grid.dataSource;
32616         this.footer = Roo.factory(this.footer, Roo);
32617         
32618     }
32619     
32620     grid.monitorWindowResize = false; // turn off autosizing
32621     grid.autoHeight = false;
32622     grid.autoWidth = false;
32623     this.grid = grid;
32624     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
32625 };
32626
32627 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
32628     getId : function(){
32629         return this.grid.id;
32630     },
32631     
32632     /**
32633      * Returns the grid for this panel
32634      * @return {Roo.grid.Grid} 
32635      */
32636     getGrid : function(){
32637         return this.grid;    
32638     },
32639     
32640     setSize : function(width, height){
32641         if(!this.ignoreResize(width, height)){
32642             var grid = this.grid;
32643             var size = this.adjustForComponents(width, height);
32644             grid.getGridEl().setSize(size.width, size.height);
32645             grid.autoSize();
32646         }
32647     },
32648     
32649     beforeSlide : function(){
32650         this.grid.getView().scroller.clip();
32651     },
32652     
32653     afterSlide : function(){
32654         this.grid.getView().scroller.unclip();
32655     },
32656     
32657     destroy : function(){
32658         this.grid.destroy();
32659         delete this.grid;
32660         Roo.GridPanel.superclass.destroy.call(this); 
32661     }
32662 });
32663
32664
32665 /**
32666  * @class Roo.NestedLayoutPanel
32667  * @extends Roo.ContentPanel
32668  * @constructor
32669  * Create a new NestedLayoutPanel.
32670  * 
32671  * 
32672  * @param {Roo.BorderLayout} layout The layout for this panel
32673  * @param {String/Object} config A string to set only the title or a config object
32674  */
32675 Roo.NestedLayoutPanel = function(layout, config)
32676 {
32677     // construct with only one argument..
32678     /* FIXME - implement nicer consturctors
32679     if (layout.layout) {
32680         config = layout;
32681         layout = config.layout;
32682         delete config.layout;
32683     }
32684     if (layout.xtype && !layout.getEl) {
32685         // then layout needs constructing..
32686         layout = Roo.factory(layout, Roo);
32687     }
32688     */
32689     
32690     
32691     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
32692     
32693     layout.monitorWindowResize = false; // turn off autosizing
32694     this.layout = layout;
32695     this.layout.getEl().addClass("x-layout-nested-layout");
32696     
32697     
32698     
32699     
32700 };
32701
32702 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
32703
32704     setSize : function(width, height){
32705         if(!this.ignoreResize(width, height)){
32706             var size = this.adjustForComponents(width, height);
32707             var el = this.layout.getEl();
32708             el.setSize(size.width, size.height);
32709             var touch = el.dom.offsetWidth;
32710             this.layout.layout();
32711             // ie requires a double layout on the first pass
32712             if(Roo.isIE && !this.initialized){
32713                 this.initialized = true;
32714                 this.layout.layout();
32715             }
32716         }
32717     },
32718     
32719     // activate all subpanels if not currently active..
32720     
32721     setActiveState : function(active){
32722         this.active = active;
32723         if(!active){
32724             this.fireEvent("deactivate", this);
32725             return;
32726         }
32727         
32728         this.fireEvent("activate", this);
32729         // not sure if this should happen before or after..
32730         if (!this.layout) {
32731             return; // should not happen..
32732         }
32733         var reg = false;
32734         for (var r in this.layout.regions) {
32735             reg = this.layout.getRegion(r);
32736             if (reg.getActivePanel()) {
32737                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
32738                 reg.setActivePanel(reg.getActivePanel());
32739                 continue;
32740             }
32741             if (!reg.panels.length) {
32742                 continue;
32743             }
32744             reg.showPanel(reg.getPanel(0));
32745         }
32746         
32747         
32748         
32749         
32750     },
32751     
32752     /**
32753      * Returns the nested BorderLayout for this panel
32754      * @return {Roo.BorderLayout} 
32755      */
32756     getLayout : function(){
32757         return this.layout;
32758     },
32759     
32760      /**
32761      * Adds a xtype elements to the layout of the nested panel
32762      * <pre><code>
32763
32764 panel.addxtype({
32765        xtype : 'ContentPanel',
32766        region: 'west',
32767        items: [ .... ]
32768    }
32769 );
32770
32771 panel.addxtype({
32772         xtype : 'NestedLayoutPanel',
32773         region: 'west',
32774         layout: {
32775            center: { },
32776            west: { }   
32777         },
32778         items : [ ... list of content panels or nested layout panels.. ]
32779    }
32780 );
32781 </code></pre>
32782      * @param {Object} cfg Xtype definition of item to add.
32783      */
32784     addxtype : function(cfg) {
32785         return this.layout.addxtype(cfg);
32786     
32787     }
32788 });
32789
32790 Roo.ScrollPanel = function(el, config, content){
32791     config = config || {};
32792     config.fitToFrame = true;
32793     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
32794     
32795     this.el.dom.style.overflow = "hidden";
32796     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
32797     this.el.removeClass("x-layout-inactive-content");
32798     this.el.on("mousewheel", this.onWheel, this);
32799
32800     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
32801     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
32802     up.unselectable(); down.unselectable();
32803     up.on("click", this.scrollUp, this);
32804     down.on("click", this.scrollDown, this);
32805     up.addClassOnOver("x-scroller-btn-over");
32806     down.addClassOnOver("x-scroller-btn-over");
32807     up.addClassOnClick("x-scroller-btn-click");
32808     down.addClassOnClick("x-scroller-btn-click");
32809     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
32810
32811     this.resizeEl = this.el;
32812     this.el = wrap; this.up = up; this.down = down;
32813 };
32814
32815 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
32816     increment : 100,
32817     wheelIncrement : 5,
32818     scrollUp : function(){
32819         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
32820     },
32821
32822     scrollDown : function(){
32823         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
32824     },
32825
32826     afterScroll : function(){
32827         var el = this.resizeEl;
32828         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
32829         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
32830         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
32831     },
32832
32833     setSize : function(){
32834         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
32835         this.afterScroll();
32836     },
32837
32838     onWheel : function(e){
32839         var d = e.getWheelDelta();
32840         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
32841         this.afterScroll();
32842         e.stopEvent();
32843     },
32844
32845     setContent : function(content, loadScripts){
32846         this.resizeEl.update(content, loadScripts);
32847     }
32848
32849 });
32850
32851
32852
32853
32854
32855
32856
32857
32858
32859 /**
32860  * @class Roo.TreePanel
32861  * @extends Roo.ContentPanel
32862  * @constructor
32863  * Create a new TreePanel. - defaults to fit/scoll contents.
32864  * @param {String/Object} config A string to set only the panel's title, or a config object
32865  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
32866  */
32867 Roo.TreePanel = function(config){
32868     var el = config.el;
32869     var tree = config.tree;
32870     delete config.tree; 
32871     delete config.el; // hopefull!
32872     
32873     // wrapper for IE7 strict & safari scroll issue
32874     
32875     var treeEl = el.createChild();
32876     config.resizeEl = treeEl;
32877     
32878     
32879     
32880     Roo.TreePanel.superclass.constructor.call(this, el, config);
32881  
32882  
32883     this.tree = new Roo.tree.TreePanel(treeEl , tree);
32884     //console.log(tree);
32885     this.on('activate', function()
32886     {
32887         if (this.tree.rendered) {
32888             return;
32889         }
32890         //console.log('render tree');
32891         this.tree.render();
32892     });
32893     
32894     this.on('resize',  function (cp, w, h) {
32895             this.tree.innerCt.setWidth(w);
32896             this.tree.innerCt.setHeight(h);
32897             this.tree.innerCt.setStyle('overflow-y', 'auto');
32898     });
32899
32900         
32901     
32902 };
32903
32904 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
32905     fitToFrame : true,
32906     autoScroll : true
32907 });
32908
32909
32910
32911
32912
32913
32914
32915
32916
32917
32918
32919 /*
32920  * Based on:
32921  * Ext JS Library 1.1.1
32922  * Copyright(c) 2006-2007, Ext JS, LLC.
32923  *
32924  * Originally Released Under LGPL - original licence link has changed is not relivant.
32925  *
32926  * Fork - LGPL
32927  * <script type="text/javascript">
32928  */
32929  
32930
32931 /**
32932  * @class Roo.ReaderLayout
32933  * @extends Roo.BorderLayout
32934  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
32935  * center region containing two nested regions (a top one for a list view and one for item preview below),
32936  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
32937  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
32938  * expedites the setup of the overall layout and regions for this common application style.
32939  * Example:
32940  <pre><code>
32941 var reader = new Roo.ReaderLayout();
32942 var CP = Roo.ContentPanel;  // shortcut for adding
32943
32944 reader.beginUpdate();
32945 reader.add("north", new CP("north", "North"));
32946 reader.add("west", new CP("west", {title: "West"}));
32947 reader.add("east", new CP("east", {title: "East"}));
32948
32949 reader.regions.listView.add(new CP("listView", "List"));
32950 reader.regions.preview.add(new CP("preview", "Preview"));
32951 reader.endUpdate();
32952 </code></pre>
32953 * @constructor
32954 * Create a new ReaderLayout
32955 * @param {Object} config Configuration options
32956 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
32957 * document.body if omitted)
32958 */
32959 Roo.ReaderLayout = function(config, renderTo){
32960     var c = config || {size:{}};
32961     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
32962         north: c.north !== false ? Roo.apply({
32963             split:false,
32964             initialSize: 32,
32965             titlebar: false
32966         }, c.north) : false,
32967         west: c.west !== false ? Roo.apply({
32968             split:true,
32969             initialSize: 200,
32970             minSize: 175,
32971             maxSize: 400,
32972             titlebar: true,
32973             collapsible: true,
32974             animate: true,
32975             margins:{left:5,right:0,bottom:5,top:5},
32976             cmargins:{left:5,right:5,bottom:5,top:5}
32977         }, c.west) : false,
32978         east: c.east !== false ? Roo.apply({
32979             split:true,
32980             initialSize: 200,
32981             minSize: 175,
32982             maxSize: 400,
32983             titlebar: true,
32984             collapsible: true,
32985             animate: true,
32986             margins:{left:0,right:5,bottom:5,top:5},
32987             cmargins:{left:5,right:5,bottom:5,top:5}
32988         }, c.east) : false,
32989         center: Roo.apply({
32990             tabPosition: 'top',
32991             autoScroll:false,
32992             closeOnTab: true,
32993             titlebar:false,
32994             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
32995         }, c.center)
32996     });
32997
32998     this.el.addClass('x-reader');
32999
33000     this.beginUpdate();
33001
33002     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
33003         south: c.preview !== false ? Roo.apply({
33004             split:true,
33005             initialSize: 200,
33006             minSize: 100,
33007             autoScroll:true,
33008             collapsible:true,
33009             titlebar: true,
33010             cmargins:{top:5,left:0, right:0, bottom:0}
33011         }, c.preview) : false,
33012         center: Roo.apply({
33013             autoScroll:false,
33014             titlebar:false,
33015             minHeight:200
33016         }, c.listView)
33017     });
33018     this.add('center', new Roo.NestedLayoutPanel(inner,
33019             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
33020
33021     this.endUpdate();
33022
33023     this.regions.preview = inner.getRegion('south');
33024     this.regions.listView = inner.getRegion('center');
33025 };
33026
33027 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
33028  * Based on:
33029  * Ext JS Library 1.1.1
33030  * Copyright(c) 2006-2007, Ext JS, LLC.
33031  *
33032  * Originally Released Under LGPL - original licence link has changed is not relivant.
33033  *
33034  * Fork - LGPL
33035  * <script type="text/javascript">
33036  */
33037  
33038 /**
33039  * @class Roo.grid.Grid
33040  * @extends Roo.util.Observable
33041  * This class represents the primary interface of a component based grid control.
33042  * <br><br>Usage:<pre><code>
33043  var grid = new Roo.grid.Grid("my-container-id", {
33044      ds: myDataStore,
33045      cm: myColModel,
33046      selModel: mySelectionModel,
33047      autoSizeColumns: true,
33048      monitorWindowResize: false,
33049      trackMouseOver: true
33050  });
33051  // set any options
33052  grid.render();
33053  * </code></pre>
33054  * <b>Common Problems:</b><br/>
33055  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
33056  * element will correct this<br/>
33057  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
33058  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
33059  * are unpredictable.<br/>
33060  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
33061  * grid to calculate dimensions/offsets.<br/>
33062   * @constructor
33063  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
33064  * The container MUST have some type of size defined for the grid to fill. The container will be
33065  * automatically set to position relative if it isn't already.
33066  * @param {Object} config A config object that sets properties on this grid.
33067  */
33068 Roo.grid.Grid = function(container, config){
33069         // initialize the container
33070         this.container = Roo.get(container);
33071         this.container.update("");
33072         this.container.setStyle("overflow", "hidden");
33073     this.container.addClass('x-grid-container');
33074
33075     this.id = this.container.id;
33076
33077     Roo.apply(this, config);
33078     // check and correct shorthanded configs
33079     if(this.ds){
33080         this.dataSource = this.ds;
33081         delete this.ds;
33082     }
33083     if(this.cm){
33084         this.colModel = this.cm;
33085         delete this.cm;
33086     }
33087     if(this.sm){
33088         this.selModel = this.sm;
33089         delete this.sm;
33090     }
33091
33092     if (this.selModel) {
33093         this.selModel = Roo.factory(this.selModel, Roo.grid);
33094         this.sm = this.selModel;
33095         this.sm.xmodule = this.xmodule || false;
33096     }
33097     if (typeof(this.colModel.config) == 'undefined') {
33098         this.colModel = new Roo.grid.ColumnModel(this.colModel);
33099         this.cm = this.colModel;
33100         this.cm.xmodule = this.xmodule || false;
33101     }
33102     if (this.dataSource) {
33103         this.dataSource= Roo.factory(this.dataSource, Roo.data);
33104         this.ds = this.dataSource;
33105         this.ds.xmodule = this.xmodule || false;
33106          
33107     }
33108     
33109     
33110     
33111     if(this.width){
33112         this.container.setWidth(this.width);
33113     }
33114
33115     if(this.height){
33116         this.container.setHeight(this.height);
33117     }
33118     /** @private */
33119         this.addEvents({
33120         // raw events
33121         /**
33122          * @event click
33123          * The raw click event for the entire grid.
33124          * @param {Roo.EventObject} e
33125          */
33126         "click" : true,
33127         /**
33128          * @event dblclick
33129          * The raw dblclick event for the entire grid.
33130          * @param {Roo.EventObject} e
33131          */
33132         "dblclick" : true,
33133         /**
33134          * @event contextmenu
33135          * The raw contextmenu event for the entire grid.
33136          * @param {Roo.EventObject} e
33137          */
33138         "contextmenu" : true,
33139         /**
33140          * @event mousedown
33141          * The raw mousedown event for the entire grid.
33142          * @param {Roo.EventObject} e
33143          */
33144         "mousedown" : true,
33145         /**
33146          * @event mouseup
33147          * The raw mouseup event for the entire grid.
33148          * @param {Roo.EventObject} e
33149          */
33150         "mouseup" : true,
33151         /**
33152          * @event mouseover
33153          * The raw mouseover event for the entire grid.
33154          * @param {Roo.EventObject} e
33155          */
33156         "mouseover" : true,
33157         /**
33158          * @event mouseout
33159          * The raw mouseout event for the entire grid.
33160          * @param {Roo.EventObject} e
33161          */
33162         "mouseout" : true,
33163         /**
33164          * @event keypress
33165          * The raw keypress event for the entire grid.
33166          * @param {Roo.EventObject} e
33167          */
33168         "keypress" : true,
33169         /**
33170          * @event keydown
33171          * The raw keydown event for the entire grid.
33172          * @param {Roo.EventObject} e
33173          */
33174         "keydown" : true,
33175
33176         // custom events
33177
33178         /**
33179          * @event cellclick
33180          * Fires when a cell is clicked
33181          * @param {Grid} this
33182          * @param {Number} rowIndex
33183          * @param {Number} columnIndex
33184          * @param {Roo.EventObject} e
33185          */
33186         "cellclick" : true,
33187         /**
33188          * @event celldblclick
33189          * Fires when a cell is double clicked
33190          * @param {Grid} this
33191          * @param {Number} rowIndex
33192          * @param {Number} columnIndex
33193          * @param {Roo.EventObject} e
33194          */
33195         "celldblclick" : true,
33196         /**
33197          * @event rowclick
33198          * Fires when a row is clicked
33199          * @param {Grid} this
33200          * @param {Number} rowIndex
33201          * @param {Roo.EventObject} e
33202          */
33203         "rowclick" : true,
33204         /**
33205          * @event rowdblclick
33206          * Fires when a row is double clicked
33207          * @param {Grid} this
33208          * @param {Number} rowIndex
33209          * @param {Roo.EventObject} e
33210          */
33211         "rowdblclick" : true,
33212         /**
33213          * @event headerclick
33214          * Fires when a header is clicked
33215          * @param {Grid} this
33216          * @param {Number} columnIndex
33217          * @param {Roo.EventObject} e
33218          */
33219         "headerclick" : true,
33220         /**
33221          * @event headerdblclick
33222          * Fires when a header cell is double clicked
33223          * @param {Grid} this
33224          * @param {Number} columnIndex
33225          * @param {Roo.EventObject} e
33226          */
33227         "headerdblclick" : true,
33228         /**
33229          * @event rowcontextmenu
33230          * Fires when a row is right clicked
33231          * @param {Grid} this
33232          * @param {Number} rowIndex
33233          * @param {Roo.EventObject} e
33234          */
33235         "rowcontextmenu" : true,
33236         /**
33237          * @event cellcontextmenu
33238          * Fires when a cell is right clicked
33239          * @param {Grid} this
33240          * @param {Number} rowIndex
33241          * @param {Number} cellIndex
33242          * @param {Roo.EventObject} e
33243          */
33244          "cellcontextmenu" : true,
33245         /**
33246          * @event headercontextmenu
33247          * Fires when a header is right clicked
33248          * @param {Grid} this
33249          * @param {Number} columnIndex
33250          * @param {Roo.EventObject} e
33251          */
33252         "headercontextmenu" : true,
33253         /**
33254          * @event bodyscroll
33255          * Fires when the body element is scrolled
33256          * @param {Number} scrollLeft
33257          * @param {Number} scrollTop
33258          */
33259         "bodyscroll" : true,
33260         /**
33261          * @event columnresize
33262          * Fires when the user resizes a column
33263          * @param {Number} columnIndex
33264          * @param {Number} newSize
33265          */
33266         "columnresize" : true,
33267         /**
33268          * @event columnmove
33269          * Fires when the user moves a column
33270          * @param {Number} oldIndex
33271          * @param {Number} newIndex
33272          */
33273         "columnmove" : true,
33274         /**
33275          * @event startdrag
33276          * Fires when row(s) start being dragged
33277          * @param {Grid} this
33278          * @param {Roo.GridDD} dd The drag drop object
33279          * @param {event} e The raw browser event
33280          */
33281         "startdrag" : true,
33282         /**
33283          * @event enddrag
33284          * Fires when a drag operation is complete
33285          * @param {Grid} this
33286          * @param {Roo.GridDD} dd The drag drop object
33287          * @param {event} e The raw browser event
33288          */
33289         "enddrag" : true,
33290         /**
33291          * @event dragdrop
33292          * Fires when dragged row(s) are dropped on a valid DD target
33293          * @param {Grid} this
33294          * @param {Roo.GridDD} dd The drag drop object
33295          * @param {String} targetId The target drag drop object
33296          * @param {event} e The raw browser event
33297          */
33298         "dragdrop" : true,
33299         /**
33300          * @event dragover
33301          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
33302          * @param {Grid} this
33303          * @param {Roo.GridDD} dd The drag drop object
33304          * @param {String} targetId The target drag drop object
33305          * @param {event} e The raw browser event
33306          */
33307         "dragover" : true,
33308         /**
33309          * @event dragenter
33310          *  Fires when the dragged row(s) first cross another DD target while being dragged
33311          * @param {Grid} this
33312          * @param {Roo.GridDD} dd The drag drop object
33313          * @param {String} targetId The target drag drop object
33314          * @param {event} e The raw browser event
33315          */
33316         "dragenter" : true,
33317         /**
33318          * @event dragout
33319          * Fires when the dragged row(s) leave another DD target while being dragged
33320          * @param {Grid} this
33321          * @param {Roo.GridDD} dd The drag drop object
33322          * @param {String} targetId The target drag drop object
33323          * @param {event} e The raw browser event
33324          */
33325         "dragout" : true,
33326         /**
33327          * @event rowclass
33328          * Fires when a row is rendered, so you can change add a style to it.
33329          * @param {GridView} gridview   The grid view
33330          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
33331          */
33332         'rowclass' : true,
33333
33334         /**
33335          * @event render
33336          * Fires when the grid is rendered
33337          * @param {Grid} grid
33338          */
33339         'render' : true
33340     });
33341
33342     Roo.grid.Grid.superclass.constructor.call(this);
33343 };
33344 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
33345     
33346     /**
33347      * @cfg {String} ddGroup - drag drop group.
33348      */
33349
33350     /**
33351      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
33352      */
33353     minColumnWidth : 25,
33354
33355     /**
33356      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
33357      * <b>on initial render.</b> It is more efficient to explicitly size the columns
33358      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
33359      */
33360     autoSizeColumns : false,
33361
33362     /**
33363      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
33364      */
33365     autoSizeHeaders : true,
33366
33367     /**
33368      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
33369      */
33370     monitorWindowResize : true,
33371
33372     /**
33373      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
33374      * rows measured to get a columns size. Default is 0 (all rows).
33375      */
33376     maxRowsToMeasure : 0,
33377
33378     /**
33379      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
33380      */
33381     trackMouseOver : true,
33382
33383     /**
33384     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
33385     */
33386     
33387     /**
33388     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
33389     */
33390     enableDragDrop : false,
33391     
33392     /**
33393     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
33394     */
33395     enableColumnMove : true,
33396     
33397     /**
33398     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
33399     */
33400     enableColumnHide : true,
33401     
33402     /**
33403     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
33404     */
33405     enableRowHeightSync : false,
33406     
33407     /**
33408     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
33409     */
33410     stripeRows : true,
33411     
33412     /**
33413     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
33414     */
33415     autoHeight : false,
33416
33417     /**
33418      * @cfg {String} autoExpandColumn The id (or dataIndex) of a column in this grid that should expand to fill unused space. This id can not be 0. Default is false.
33419      */
33420     autoExpandColumn : false,
33421
33422     /**
33423     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
33424     * Default is 50.
33425     */
33426     autoExpandMin : 50,
33427
33428     /**
33429     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
33430     */
33431     autoExpandMax : 1000,
33432
33433     /**
33434     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
33435     */
33436     view : null,
33437
33438     /**
33439     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
33440     */
33441     loadMask : false,
33442     /**
33443     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
33444     */
33445     dropTarget: false,
33446     
33447    
33448     
33449     // private
33450     rendered : false,
33451
33452     /**
33453     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
33454     * of a fixed width. Default is false.
33455     */
33456     /**
33457     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
33458     */
33459     /**
33460      * Called once after all setup has been completed and the grid is ready to be rendered.
33461      * @return {Roo.grid.Grid} this
33462      */
33463     render : function()
33464     {
33465         var c = this.container;
33466         // try to detect autoHeight/width mode
33467         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
33468             this.autoHeight = true;
33469         }
33470         var view = this.getView();
33471         view.init(this);
33472
33473         c.on("click", this.onClick, this);
33474         c.on("dblclick", this.onDblClick, this);
33475         c.on("contextmenu", this.onContextMenu, this);
33476         c.on("keydown", this.onKeyDown, this);
33477
33478         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
33479
33480         this.getSelectionModel().init(this);
33481
33482         view.render();
33483
33484         if(this.loadMask){
33485             this.loadMask = new Roo.LoadMask(this.container,
33486                     Roo.apply({store:this.dataSource}, this.loadMask));
33487         }
33488         
33489         
33490         if (this.toolbar && this.toolbar.xtype) {
33491             this.toolbar.container = this.getView().getHeaderPanel(true);
33492             this.toolbar = new Roo.Toolbar(this.toolbar);
33493         }
33494         if (this.footer && this.footer.xtype) {
33495             this.footer.dataSource = this.getDataSource();
33496             this.footer.container = this.getView().getFooterPanel(true);
33497             this.footer = Roo.factory(this.footer, Roo);
33498         }
33499         if (this.dropTarget && this.dropTarget.xtype) {
33500             delete this.dropTarget.xtype;
33501             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
33502         }
33503         
33504         
33505         this.rendered = true;
33506         this.fireEvent('render', this);
33507         return this;
33508     },
33509
33510         /**
33511          * Reconfigures the grid to use a different Store and Column Model.
33512          * The View will be bound to the new objects and refreshed.
33513          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
33514          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
33515          */
33516     reconfigure : function(dataSource, colModel){
33517         if(this.loadMask){
33518             this.loadMask.destroy();
33519             this.loadMask = new Roo.LoadMask(this.container,
33520                     Roo.apply({store:dataSource}, this.loadMask));
33521         }
33522         this.view.bind(dataSource, colModel);
33523         this.dataSource = dataSource;
33524         this.colModel = colModel;
33525         this.view.refresh(true);
33526     },
33527
33528     // private
33529     onKeyDown : function(e){
33530         this.fireEvent("keydown", e);
33531     },
33532
33533     /**
33534      * Destroy this grid.
33535      * @param {Boolean} removeEl True to remove the element
33536      */
33537     destroy : function(removeEl, keepListeners){
33538         if(this.loadMask){
33539             this.loadMask.destroy();
33540         }
33541         var c = this.container;
33542         c.removeAllListeners();
33543         this.view.destroy();
33544         this.colModel.purgeListeners();
33545         if(!keepListeners){
33546             this.purgeListeners();
33547         }
33548         c.update("");
33549         if(removeEl === true){
33550             c.remove();
33551         }
33552     },
33553
33554     // private
33555     processEvent : function(name, e){
33556         this.fireEvent(name, e);
33557         var t = e.getTarget();
33558         var v = this.view;
33559         var header = v.findHeaderIndex(t);
33560         if(header !== false){
33561             this.fireEvent("header" + name, this, header, e);
33562         }else{
33563             var row = v.findRowIndex(t);
33564             var cell = v.findCellIndex(t);
33565             if(row !== false){
33566                 this.fireEvent("row" + name, this, row, e);
33567                 if(cell !== false){
33568                     this.fireEvent("cell" + name, this, row, cell, e);
33569                 }
33570             }
33571         }
33572     },
33573
33574     // private
33575     onClick : function(e){
33576         this.processEvent("click", e);
33577     },
33578
33579     // private
33580     onContextMenu : function(e, t){
33581         this.processEvent("contextmenu", e);
33582     },
33583
33584     // private
33585     onDblClick : function(e){
33586         this.processEvent("dblclick", e);
33587     },
33588
33589     // private
33590     walkCells : function(row, col, step, fn, scope){
33591         var cm = this.colModel, clen = cm.getColumnCount();
33592         var ds = this.dataSource, rlen = ds.getCount(), first = true;
33593         if(step < 0){
33594             if(col < 0){
33595                 row--;
33596                 first = false;
33597             }
33598             while(row >= 0){
33599                 if(!first){
33600                     col = clen-1;
33601                 }
33602                 first = false;
33603                 while(col >= 0){
33604                     if(fn.call(scope || this, row, col, cm) === true){
33605                         return [row, col];
33606                     }
33607                     col--;
33608                 }
33609                 row--;
33610             }
33611         } else {
33612             if(col >= clen){
33613                 row++;
33614                 first = false;
33615             }
33616             while(row < rlen){
33617                 if(!first){
33618                     col = 0;
33619                 }
33620                 first = false;
33621                 while(col < clen){
33622                     if(fn.call(scope || this, row, col, cm) === true){
33623                         return [row, col];
33624                     }
33625                     col++;
33626                 }
33627                 row++;
33628             }
33629         }
33630         return null;
33631     },
33632
33633     // private
33634     getSelections : function(){
33635         return this.selModel.getSelections();
33636     },
33637
33638     /**
33639      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
33640      * but if manual update is required this method will initiate it.
33641      */
33642     autoSize : function(){
33643         if(this.rendered){
33644             this.view.layout();
33645             if(this.view.adjustForScroll){
33646                 this.view.adjustForScroll();
33647             }
33648         }
33649     },
33650
33651     /**
33652      * Returns the grid's underlying element.
33653      * @return {Element} The element
33654      */
33655     getGridEl : function(){
33656         return this.container;
33657     },
33658
33659     // private for compatibility, overridden by editor grid
33660     stopEditing : function(){},
33661
33662     /**
33663      * Returns the grid's SelectionModel.
33664      * @return {SelectionModel}
33665      */
33666     getSelectionModel : function(){
33667         if(!this.selModel){
33668             this.selModel = new Roo.grid.RowSelectionModel();
33669         }
33670         return this.selModel;
33671     },
33672
33673     /**
33674      * Returns the grid's DataSource.
33675      * @return {DataSource}
33676      */
33677     getDataSource : function(){
33678         return this.dataSource;
33679     },
33680
33681     /**
33682      * Returns the grid's ColumnModel.
33683      * @return {ColumnModel}
33684      */
33685     getColumnModel : function(){
33686         return this.colModel;
33687     },
33688
33689     /**
33690      * Returns the grid's GridView object.
33691      * @return {GridView}
33692      */
33693     getView : function(){
33694         if(!this.view){
33695             this.view = new Roo.grid.GridView(this.viewConfig);
33696         }
33697         return this.view;
33698     },
33699     /**
33700      * Called to get grid's drag proxy text, by default returns this.ddText.
33701      * @return {String}
33702      */
33703     getDragDropText : function(){
33704         var count = this.selModel.getCount();
33705         return String.format(this.ddText, count, count == 1 ? '' : 's');
33706     }
33707 });
33708 /**
33709  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
33710  * %0 is replaced with the number of selected rows.
33711  * @type String
33712  */
33713 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
33714  * Based on:
33715  * Ext JS Library 1.1.1
33716  * Copyright(c) 2006-2007, Ext JS, LLC.
33717  *
33718  * Originally Released Under LGPL - original licence link has changed is not relivant.
33719  *
33720  * Fork - LGPL
33721  * <script type="text/javascript">
33722  */
33723  
33724 Roo.grid.AbstractGridView = function(){
33725         this.grid = null;
33726         
33727         this.events = {
33728             "beforerowremoved" : true,
33729             "beforerowsinserted" : true,
33730             "beforerefresh" : true,
33731             "rowremoved" : true,
33732             "rowsinserted" : true,
33733             "rowupdated" : true,
33734             "refresh" : true
33735         };
33736     Roo.grid.AbstractGridView.superclass.constructor.call(this);
33737 };
33738
33739 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
33740     rowClass : "x-grid-row",
33741     cellClass : "x-grid-cell",
33742     tdClass : "x-grid-td",
33743     hdClass : "x-grid-hd",
33744     splitClass : "x-grid-hd-split",
33745     
33746         init: function(grid){
33747         this.grid = grid;
33748                 var cid = this.grid.getGridEl().id;
33749         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
33750         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
33751         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
33752         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
33753         },
33754         
33755         getColumnRenderers : function(){
33756         var renderers = [];
33757         var cm = this.grid.colModel;
33758         var colCount = cm.getColumnCount();
33759         for(var i = 0; i < colCount; i++){
33760             renderers[i] = cm.getRenderer(i);
33761         }
33762         return renderers;
33763     },
33764     
33765     getColumnIds : function(){
33766         var ids = [];
33767         var cm = this.grid.colModel;
33768         var colCount = cm.getColumnCount();
33769         for(var i = 0; i < colCount; i++){
33770             ids[i] = cm.getColumnId(i);
33771         }
33772         return ids;
33773     },
33774     
33775     getDataIndexes : function(){
33776         if(!this.indexMap){
33777             this.indexMap = this.buildIndexMap();
33778         }
33779         return this.indexMap.colToData;
33780     },
33781     
33782     getColumnIndexByDataIndex : function(dataIndex){
33783         if(!this.indexMap){
33784             this.indexMap = this.buildIndexMap();
33785         }
33786         return this.indexMap.dataToCol[dataIndex];
33787     },
33788     
33789     /**
33790      * Set a css style for a column dynamically. 
33791      * @param {Number} colIndex The index of the column
33792      * @param {String} name The css property name
33793      * @param {String} value The css value
33794      */
33795     setCSSStyle : function(colIndex, name, value){
33796         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
33797         Roo.util.CSS.updateRule(selector, name, value);
33798     },
33799     
33800     generateRules : function(cm){
33801         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
33802         Roo.util.CSS.removeStyleSheet(rulesId);
33803         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33804             var cid = cm.getColumnId(i);
33805             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
33806                          this.tdSelector, cid, " {\n}\n",
33807                          this.hdSelector, cid, " {\n}\n",
33808                          this.splitSelector, cid, " {\n}\n");
33809         }
33810         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33811     }
33812 });/*
33813  * Based on:
33814  * Ext JS Library 1.1.1
33815  * Copyright(c) 2006-2007, Ext JS, LLC.
33816  *
33817  * Originally Released Under LGPL - original licence link has changed is not relivant.
33818  *
33819  * Fork - LGPL
33820  * <script type="text/javascript">
33821  */
33822
33823 // private
33824 // This is a support class used internally by the Grid components
33825 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
33826     this.grid = grid;
33827     this.view = grid.getView();
33828     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33829     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
33830     if(hd2){
33831         this.setHandleElId(Roo.id(hd));
33832         this.setOuterHandleElId(Roo.id(hd2));
33833     }
33834     this.scroll = false;
33835 };
33836 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
33837     maxDragWidth: 120,
33838     getDragData : function(e){
33839         var t = Roo.lib.Event.getTarget(e);
33840         var h = this.view.findHeaderCell(t);
33841         if(h){
33842             return {ddel: h.firstChild, header:h};
33843         }
33844         return false;
33845     },
33846
33847     onInitDrag : function(e){
33848         this.view.headersDisabled = true;
33849         var clone = this.dragData.ddel.cloneNode(true);
33850         clone.id = Roo.id();
33851         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
33852         this.proxy.update(clone);
33853         return true;
33854     },
33855
33856     afterValidDrop : function(){
33857         var v = this.view;
33858         setTimeout(function(){
33859             v.headersDisabled = false;
33860         }, 50);
33861     },
33862
33863     afterInvalidDrop : function(){
33864         var v = this.view;
33865         setTimeout(function(){
33866             v.headersDisabled = false;
33867         }, 50);
33868     }
33869 });
33870 /*
33871  * Based on:
33872  * Ext JS Library 1.1.1
33873  * Copyright(c) 2006-2007, Ext JS, LLC.
33874  *
33875  * Originally Released Under LGPL - original licence link has changed is not relivant.
33876  *
33877  * Fork - LGPL
33878  * <script type="text/javascript">
33879  */
33880 // private
33881 // This is a support class used internally by the Grid components
33882 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
33883     this.grid = grid;
33884     this.view = grid.getView();
33885     // split the proxies so they don't interfere with mouse events
33886     this.proxyTop = Roo.DomHelper.append(document.body, {
33887         cls:"col-move-top", html:"&#160;"
33888     }, true);
33889     this.proxyBottom = Roo.DomHelper.append(document.body, {
33890         cls:"col-move-bottom", html:"&#160;"
33891     }, true);
33892     this.proxyTop.hide = this.proxyBottom.hide = function(){
33893         this.setLeftTop(-100,-100);
33894         this.setStyle("visibility", "hidden");
33895     };
33896     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33897     // temporarily disabled
33898     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
33899     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
33900 };
33901 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
33902     proxyOffsets : [-4, -9],
33903     fly: Roo.Element.fly,
33904
33905     getTargetFromEvent : function(e){
33906         var t = Roo.lib.Event.getTarget(e);
33907         var cindex = this.view.findCellIndex(t);
33908         if(cindex !== false){
33909             return this.view.getHeaderCell(cindex);
33910         }
33911         return null;
33912     },
33913
33914     nextVisible : function(h){
33915         var v = this.view, cm = this.grid.colModel;
33916         h = h.nextSibling;
33917         while(h){
33918             if(!cm.isHidden(v.getCellIndex(h))){
33919                 return h;
33920             }
33921             h = h.nextSibling;
33922         }
33923         return null;
33924     },
33925
33926     prevVisible : function(h){
33927         var v = this.view, cm = this.grid.colModel;
33928         h = h.prevSibling;
33929         while(h){
33930             if(!cm.isHidden(v.getCellIndex(h))){
33931                 return h;
33932             }
33933             h = h.prevSibling;
33934         }
33935         return null;
33936     },
33937
33938     positionIndicator : function(h, n, e){
33939         var x = Roo.lib.Event.getPageX(e);
33940         var r = Roo.lib.Dom.getRegion(n.firstChild);
33941         var px, pt, py = r.top + this.proxyOffsets[1];
33942         if((r.right - x) <= (r.right-r.left)/2){
33943             px = r.right+this.view.borderWidth;
33944             pt = "after";
33945         }else{
33946             px = r.left;
33947             pt = "before";
33948         }
33949         var oldIndex = this.view.getCellIndex(h);
33950         var newIndex = this.view.getCellIndex(n);
33951
33952         if(this.grid.colModel.isFixed(newIndex)){
33953             return false;
33954         }
33955
33956         var locked = this.grid.colModel.isLocked(newIndex);
33957
33958         if(pt == "after"){
33959             newIndex++;
33960         }
33961         if(oldIndex < newIndex){
33962             newIndex--;
33963         }
33964         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
33965             return false;
33966         }
33967         px +=  this.proxyOffsets[0];
33968         this.proxyTop.setLeftTop(px, py);
33969         this.proxyTop.show();
33970         if(!this.bottomOffset){
33971             this.bottomOffset = this.view.mainHd.getHeight();
33972         }
33973         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
33974         this.proxyBottom.show();
33975         return pt;
33976     },
33977
33978     onNodeEnter : function(n, dd, e, data){
33979         if(data.header != n){
33980             this.positionIndicator(data.header, n, e);
33981         }
33982     },
33983
33984     onNodeOver : function(n, dd, e, data){
33985         var result = false;
33986         if(data.header != n){
33987             result = this.positionIndicator(data.header, n, e);
33988         }
33989         if(!result){
33990             this.proxyTop.hide();
33991             this.proxyBottom.hide();
33992         }
33993         return result ? this.dropAllowed : this.dropNotAllowed;
33994     },
33995
33996     onNodeOut : function(n, dd, e, data){
33997         this.proxyTop.hide();
33998         this.proxyBottom.hide();
33999     },
34000
34001     onNodeDrop : function(n, dd, e, data){
34002         var h = data.header;
34003         if(h != n){
34004             var cm = this.grid.colModel;
34005             var x = Roo.lib.Event.getPageX(e);
34006             var r = Roo.lib.Dom.getRegion(n.firstChild);
34007             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
34008             var oldIndex = this.view.getCellIndex(h);
34009             var newIndex = this.view.getCellIndex(n);
34010             var locked = cm.isLocked(newIndex);
34011             if(pt == "after"){
34012                 newIndex++;
34013             }
34014             if(oldIndex < newIndex){
34015                 newIndex--;
34016             }
34017             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
34018                 return false;
34019             }
34020             cm.setLocked(oldIndex, locked, true);
34021             cm.moveColumn(oldIndex, newIndex);
34022             this.grid.fireEvent("columnmove", oldIndex, newIndex);
34023             return true;
34024         }
34025         return false;
34026     }
34027 });
34028 /*
34029  * Based on:
34030  * Ext JS Library 1.1.1
34031  * Copyright(c) 2006-2007, Ext JS, LLC.
34032  *
34033  * Originally Released Under LGPL - original licence link has changed is not relivant.
34034  *
34035  * Fork - LGPL
34036  * <script type="text/javascript">
34037  */
34038   
34039 /**
34040  * @class Roo.grid.GridView
34041  * @extends Roo.util.Observable
34042  *
34043  * @constructor
34044  * @param {Object} config
34045  */
34046 Roo.grid.GridView = function(config){
34047     Roo.grid.GridView.superclass.constructor.call(this);
34048     this.el = null;
34049
34050     Roo.apply(this, config);
34051 };
34052
34053 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
34054
34055     
34056     rowClass : "x-grid-row",
34057
34058     cellClass : "x-grid-col",
34059
34060     tdClass : "x-grid-td",
34061
34062     hdClass : "x-grid-hd",
34063
34064     splitClass : "x-grid-split",
34065
34066     sortClasses : ["sort-asc", "sort-desc"],
34067
34068     enableMoveAnim : false,
34069
34070     hlColor: "C3DAF9",
34071
34072     dh : Roo.DomHelper,
34073
34074     fly : Roo.Element.fly,
34075
34076     css : Roo.util.CSS,
34077
34078     borderWidth: 1,
34079
34080     splitOffset: 3,
34081
34082     scrollIncrement : 22,
34083
34084     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
34085
34086     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
34087
34088     bind : function(ds, cm){
34089         if(this.ds){
34090             this.ds.un("load", this.onLoad, this);
34091             this.ds.un("datachanged", this.onDataChange, this);
34092             this.ds.un("add", this.onAdd, this);
34093             this.ds.un("remove", this.onRemove, this);
34094             this.ds.un("update", this.onUpdate, this);
34095             this.ds.un("clear", this.onClear, this);
34096         }
34097         if(ds){
34098             ds.on("load", this.onLoad, this);
34099             ds.on("datachanged", this.onDataChange, this);
34100             ds.on("add", this.onAdd, this);
34101             ds.on("remove", this.onRemove, this);
34102             ds.on("update", this.onUpdate, this);
34103             ds.on("clear", this.onClear, this);
34104         }
34105         this.ds = ds;
34106
34107         if(this.cm){
34108             this.cm.un("widthchange", this.onColWidthChange, this);
34109             this.cm.un("headerchange", this.onHeaderChange, this);
34110             this.cm.un("hiddenchange", this.onHiddenChange, this);
34111             this.cm.un("columnmoved", this.onColumnMove, this);
34112             this.cm.un("columnlockchange", this.onColumnLock, this);
34113         }
34114         if(cm){
34115             this.generateRules(cm);
34116             cm.on("widthchange", this.onColWidthChange, this);
34117             cm.on("headerchange", this.onHeaderChange, this);
34118             cm.on("hiddenchange", this.onHiddenChange, this);
34119             cm.on("columnmoved", this.onColumnMove, this);
34120             cm.on("columnlockchange", this.onColumnLock, this);
34121         }
34122         this.cm = cm;
34123     },
34124
34125     init: function(grid){
34126         Roo.grid.GridView.superclass.init.call(this, grid);
34127
34128         this.bind(grid.dataSource, grid.colModel);
34129
34130         grid.on("headerclick", this.handleHeaderClick, this);
34131
34132         if(grid.trackMouseOver){
34133             grid.on("mouseover", this.onRowOver, this);
34134             grid.on("mouseout", this.onRowOut, this);
34135         }
34136         grid.cancelTextSelection = function(){};
34137         this.gridId = grid.id;
34138
34139         var tpls = this.templates || {};
34140
34141         if(!tpls.master){
34142             tpls.master = new Roo.Template(
34143                '<div class="x-grid" hidefocus="true">',
34144                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
34145                   '<div class="x-grid-topbar"></div>',
34146                   '<div class="x-grid-scroller"><div></div></div>',
34147                   '<div class="x-grid-locked">',
34148                       '<div class="x-grid-header">{lockedHeader}</div>',
34149                       '<div class="x-grid-body">{lockedBody}</div>',
34150                   "</div>",
34151                   '<div class="x-grid-viewport">',
34152                       '<div class="x-grid-header">{header}</div>',
34153                       '<div class="x-grid-body">{body}</div>',
34154                   "</div>",
34155                   '<div class="x-grid-bottombar"></div>',
34156                  
34157                   '<div class="x-grid-resize-proxy">&#160;</div>',
34158                "</div>"
34159             );
34160             tpls.master.disableformats = true;
34161         }
34162
34163         if(!tpls.header){
34164             tpls.header = new Roo.Template(
34165                '<table border="0" cellspacing="0" cellpadding="0">',
34166                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
34167                "</table>{splits}"
34168             );
34169             tpls.header.disableformats = true;
34170         }
34171         tpls.header.compile();
34172
34173         if(!tpls.hcell){
34174             tpls.hcell = new Roo.Template(
34175                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
34176                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
34177                 "</div></td>"
34178              );
34179              tpls.hcell.disableFormats = true;
34180         }
34181         tpls.hcell.compile();
34182
34183         if(!tpls.hsplit){
34184             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
34185             tpls.hsplit.disableFormats = true;
34186         }
34187         tpls.hsplit.compile();
34188
34189         if(!tpls.body){
34190             tpls.body = new Roo.Template(
34191                '<table border="0" cellspacing="0" cellpadding="0">',
34192                "<tbody>{rows}</tbody>",
34193                "</table>"
34194             );
34195             tpls.body.disableFormats = true;
34196         }
34197         tpls.body.compile();
34198
34199         if(!tpls.row){
34200             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
34201             tpls.row.disableFormats = true;
34202         }
34203         tpls.row.compile();
34204
34205         if(!tpls.cell){
34206             tpls.cell = new Roo.Template(
34207                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
34208                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
34209                 "</td>"
34210             );
34211             tpls.cell.disableFormats = true;
34212         }
34213         tpls.cell.compile();
34214
34215         this.templates = tpls;
34216     },
34217
34218     // remap these for backwards compat
34219     onColWidthChange : function(){
34220         this.updateColumns.apply(this, arguments);
34221     },
34222     onHeaderChange : function(){
34223         this.updateHeaders.apply(this, arguments);
34224     }, 
34225     onHiddenChange : function(){
34226         this.handleHiddenChange.apply(this, arguments);
34227     },
34228     onColumnMove : function(){
34229         this.handleColumnMove.apply(this, arguments);
34230     },
34231     onColumnLock : function(){
34232         this.handleLockChange.apply(this, arguments);
34233     },
34234
34235     onDataChange : function(){
34236         this.refresh();
34237         this.updateHeaderSortState();
34238     },
34239
34240     onClear : function(){
34241         this.refresh();
34242     },
34243
34244     onUpdate : function(ds, record){
34245         this.refreshRow(record);
34246     },
34247
34248     refreshRow : function(record){
34249         var ds = this.ds, index;
34250         if(typeof record == 'number'){
34251             index = record;
34252             record = ds.getAt(index);
34253         }else{
34254             index = ds.indexOf(record);
34255         }
34256         this.insertRows(ds, index, index, true);
34257         this.onRemove(ds, record, index+1, true);
34258         this.syncRowHeights(index, index);
34259         this.layout();
34260         this.fireEvent("rowupdated", this, index, record);
34261     },
34262
34263     onAdd : function(ds, records, index){
34264         this.insertRows(ds, index, index + (records.length-1));
34265     },
34266
34267     onRemove : function(ds, record, index, isUpdate){
34268         if(isUpdate !== true){
34269             this.fireEvent("beforerowremoved", this, index, record);
34270         }
34271         var bt = this.getBodyTable(), lt = this.getLockedTable();
34272         if(bt.rows[index]){
34273             bt.firstChild.removeChild(bt.rows[index]);
34274         }
34275         if(lt.rows[index]){
34276             lt.firstChild.removeChild(lt.rows[index]);
34277         }
34278         if(isUpdate !== true){
34279             this.stripeRows(index);
34280             this.syncRowHeights(index, index);
34281             this.layout();
34282             this.fireEvent("rowremoved", this, index, record);
34283         }
34284     },
34285
34286     onLoad : function(){
34287         this.scrollToTop();
34288     },
34289
34290     /**
34291      * Scrolls the grid to the top
34292      */
34293     scrollToTop : function(){
34294         if(this.scroller){
34295             this.scroller.dom.scrollTop = 0;
34296             this.syncScroll();
34297         }
34298     },
34299
34300     /**
34301      * Gets a panel in the header of the grid that can be used for toolbars etc.
34302      * After modifying the contents of this panel a call to grid.autoSize() may be
34303      * required to register any changes in size.
34304      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
34305      * @return Roo.Element
34306      */
34307     getHeaderPanel : function(doShow){
34308         if(doShow){
34309             this.headerPanel.show();
34310         }
34311         return this.headerPanel;
34312     },
34313
34314     /**
34315      * Gets a panel in the footer of the grid that can be used for toolbars etc.
34316      * After modifying the contents of this panel a call to grid.autoSize() may be
34317      * required to register any changes in size.
34318      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
34319      * @return Roo.Element
34320      */
34321     getFooterPanel : function(doShow){
34322         if(doShow){
34323             this.footerPanel.show();
34324         }
34325         return this.footerPanel;
34326     },
34327
34328     initElements : function(){
34329         var E = Roo.Element;
34330         var el = this.grid.getGridEl().dom.firstChild;
34331         var cs = el.childNodes;
34332
34333         this.el = new E(el);
34334         
34335          this.focusEl = new E(el.firstChild);
34336         this.focusEl.swallowEvent("click", true);
34337         
34338         this.headerPanel = new E(cs[1]);
34339         this.headerPanel.enableDisplayMode("block");
34340
34341         this.scroller = new E(cs[2]);
34342         this.scrollSizer = new E(this.scroller.dom.firstChild);
34343
34344         this.lockedWrap = new E(cs[3]);
34345         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
34346         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
34347
34348         this.mainWrap = new E(cs[4]);
34349         this.mainHd = new E(this.mainWrap.dom.firstChild);
34350         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
34351
34352         this.footerPanel = new E(cs[5]);
34353         this.footerPanel.enableDisplayMode("block");
34354
34355         this.resizeProxy = new E(cs[6]);
34356
34357         this.headerSelector = String.format(
34358            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
34359            this.lockedHd.id, this.mainHd.id
34360         );
34361
34362         this.splitterSelector = String.format(
34363            '#{0} div.x-grid-split, #{1} div.x-grid-split',
34364            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
34365         );
34366     },
34367     idToCssName : function(s)
34368     {
34369         return s.replace(/[^a-z0-9]+/ig, '-');
34370     },
34371
34372     getHeaderCell : function(index){
34373         return Roo.DomQuery.select(this.headerSelector)[index];
34374     },
34375
34376     getHeaderCellMeasure : function(index){
34377         return this.getHeaderCell(index).firstChild;
34378     },
34379
34380     getHeaderCellText : function(index){
34381         return this.getHeaderCell(index).firstChild.firstChild;
34382     },
34383
34384     getLockedTable : function(){
34385         return this.lockedBody.dom.firstChild;
34386     },
34387
34388     getBodyTable : function(){
34389         return this.mainBody.dom.firstChild;
34390     },
34391
34392     getLockedRow : function(index){
34393         return this.getLockedTable().rows[index];
34394     },
34395
34396     getRow : function(index){
34397         return this.getBodyTable().rows[index];
34398     },
34399
34400     getRowComposite : function(index){
34401         if(!this.rowEl){
34402             this.rowEl = new Roo.CompositeElementLite();
34403         }
34404         var els = [], lrow, mrow;
34405         if(lrow = this.getLockedRow(index)){
34406             els.push(lrow);
34407         }
34408         if(mrow = this.getRow(index)){
34409             els.push(mrow);
34410         }
34411         this.rowEl.elements = els;
34412         return this.rowEl;
34413     },
34414     /**
34415      * Gets the 'td' of the cell
34416      * 
34417      * @param {Integer} rowIndex row to select
34418      * @param {Integer} colIndex column to select
34419      * 
34420      * @return {Object} 
34421      */
34422     getCell : function(rowIndex, colIndex){
34423         var locked = this.cm.getLockedCount();
34424         var source;
34425         if(colIndex < locked){
34426             source = this.lockedBody.dom.firstChild;
34427         }else{
34428             source = this.mainBody.dom.firstChild;
34429             colIndex -= locked;
34430         }
34431         return source.rows[rowIndex].childNodes[colIndex];
34432     },
34433
34434     getCellText : function(rowIndex, colIndex){
34435         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
34436     },
34437
34438     getCellBox : function(cell){
34439         var b = this.fly(cell).getBox();
34440         if(Roo.isOpera){ // opera fails to report the Y
34441             b.y = cell.offsetTop + this.mainBody.getY();
34442         }
34443         return b;
34444     },
34445
34446     getCellIndex : function(cell){
34447         var id = String(cell.className).match(this.cellRE);
34448         if(id){
34449             return parseInt(id[1], 10);
34450         }
34451         return 0;
34452     },
34453
34454     findHeaderIndex : function(n){
34455         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
34456         return r ? this.getCellIndex(r) : false;
34457     },
34458
34459     findHeaderCell : function(n){
34460         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
34461         return r ? r : false;
34462     },
34463
34464     findRowIndex : function(n){
34465         if(!n){
34466             return false;
34467         }
34468         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
34469         return r ? r.rowIndex : false;
34470     },
34471
34472     findCellIndex : function(node){
34473         var stop = this.el.dom;
34474         while(node && node != stop){
34475             if(this.findRE.test(node.className)){
34476                 return this.getCellIndex(node);
34477             }
34478             node = node.parentNode;
34479         }
34480         return false;
34481     },
34482
34483     getColumnId : function(index){
34484         return this.cm.getColumnId(index);
34485     },
34486
34487     getSplitters : function()
34488     {
34489         if(this.splitterSelector){
34490            return Roo.DomQuery.select(this.splitterSelector);
34491         }else{
34492             return null;
34493       }
34494     },
34495
34496     getSplitter : function(index){
34497         return this.getSplitters()[index];
34498     },
34499
34500     onRowOver : function(e, t){
34501         var row;
34502         if((row = this.findRowIndex(t)) !== false){
34503             this.getRowComposite(row).addClass("x-grid-row-over");
34504         }
34505     },
34506
34507     onRowOut : function(e, t){
34508         var row;
34509         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
34510             this.getRowComposite(row).removeClass("x-grid-row-over");
34511         }
34512     },
34513
34514     renderHeaders : function(){
34515         var cm = this.cm;
34516         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
34517         var cb = [], lb = [], sb = [], lsb = [], p = {};
34518         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34519             p.cellId = "x-grid-hd-0-" + i;
34520             p.splitId = "x-grid-csplit-0-" + i;
34521             p.id = cm.getColumnId(i);
34522             p.title = cm.getColumnTooltip(i) || "";
34523             p.value = cm.getColumnHeader(i) || "";
34524             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
34525             if(!cm.isLocked(i)){
34526                 cb[cb.length] = ct.apply(p);
34527                 sb[sb.length] = st.apply(p);
34528             }else{
34529                 lb[lb.length] = ct.apply(p);
34530                 lsb[lsb.length] = st.apply(p);
34531             }
34532         }
34533         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
34534                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
34535     },
34536
34537     updateHeaders : function(){
34538         var html = this.renderHeaders();
34539         this.lockedHd.update(html[0]);
34540         this.mainHd.update(html[1]);
34541     },
34542
34543     /**
34544      * Focuses the specified row.
34545      * @param {Number} row The row index
34546      */
34547     focusRow : function(row)
34548     {
34549         //Roo.log('GridView.focusRow');
34550         var x = this.scroller.dom.scrollLeft;
34551         this.focusCell(row, 0, false);
34552         this.scroller.dom.scrollLeft = x;
34553     },
34554
34555     /**
34556      * Focuses the specified cell.
34557      * @param {Number} row The row index
34558      * @param {Number} col The column index
34559      * @param {Boolean} hscroll false to disable horizontal scrolling
34560      */
34561     focusCell : function(row, col, hscroll)
34562     {
34563         //Roo.log('GridView.focusCell');
34564         var el = this.ensureVisible(row, col, hscroll);
34565         this.focusEl.alignTo(el, "tl-tl");
34566         if(Roo.isGecko){
34567             this.focusEl.focus();
34568         }else{
34569             this.focusEl.focus.defer(1, this.focusEl);
34570         }
34571     },
34572
34573     /**
34574      * Scrolls the specified cell into view
34575      * @param {Number} row The row index
34576      * @param {Number} col The column index
34577      * @param {Boolean} hscroll false to disable horizontal scrolling
34578      */
34579     ensureVisible : function(row, col, hscroll)
34580     {
34581         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
34582         //return null; //disable for testing.
34583         if(typeof row != "number"){
34584             row = row.rowIndex;
34585         }
34586         if(row < 0 && row >= this.ds.getCount()){
34587             return  null;
34588         }
34589         col = (col !== undefined ? col : 0);
34590         var cm = this.grid.colModel;
34591         while(cm.isHidden(col)){
34592             col++;
34593         }
34594
34595         var el = this.getCell(row, col);
34596         if(!el){
34597             return null;
34598         }
34599         var c = this.scroller.dom;
34600
34601         var ctop = parseInt(el.offsetTop, 10);
34602         var cleft = parseInt(el.offsetLeft, 10);
34603         var cbot = ctop + el.offsetHeight;
34604         var cright = cleft + el.offsetWidth;
34605         
34606         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
34607         var stop = parseInt(c.scrollTop, 10);
34608         var sleft = parseInt(c.scrollLeft, 10);
34609         var sbot = stop + ch;
34610         var sright = sleft + c.clientWidth;
34611         /*
34612         Roo.log('GridView.ensureVisible:' +
34613                 ' ctop:' + ctop +
34614                 ' c.clientHeight:' + c.clientHeight +
34615                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
34616                 ' stop:' + stop +
34617                 ' cbot:' + cbot +
34618                 ' sbot:' + sbot +
34619                 ' ch:' + ch  
34620                 );
34621         */
34622         if(ctop < stop){
34623              c.scrollTop = ctop;
34624             //Roo.log("set scrolltop to ctop DISABLE?");
34625         }else if(cbot > sbot){
34626             //Roo.log("set scrolltop to cbot-ch");
34627             c.scrollTop = cbot-ch;
34628         }
34629         
34630         if(hscroll !== false){
34631             if(cleft < sleft){
34632                 c.scrollLeft = cleft;
34633             }else if(cright > sright){
34634                 c.scrollLeft = cright-c.clientWidth;
34635             }
34636         }
34637          
34638         return el;
34639     },
34640
34641     updateColumns : function(){
34642         this.grid.stopEditing();
34643         var cm = this.grid.colModel, colIds = this.getColumnIds();
34644         //var totalWidth = cm.getTotalWidth();
34645         var pos = 0;
34646         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34647             //if(cm.isHidden(i)) continue;
34648             var w = cm.getColumnWidth(i);
34649             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
34650             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
34651         }
34652         this.updateSplitters();
34653     },
34654
34655     generateRules : function(cm){
34656         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
34657         Roo.util.CSS.removeStyleSheet(rulesId);
34658         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34659             var cid = cm.getColumnId(i);
34660             var align = '';
34661             if(cm.config[i].align){
34662                 align = 'text-align:'+cm.config[i].align+';';
34663             }
34664             var hidden = '';
34665             if(cm.isHidden(i)){
34666                 hidden = 'display:none;';
34667             }
34668             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
34669             ruleBuf.push(
34670                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
34671                     this.hdSelector, cid, " {\n", align, width, "}\n",
34672                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
34673                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
34674         }
34675         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
34676     },
34677
34678     updateSplitters : function(){
34679         var cm = this.cm, s = this.getSplitters();
34680         if(s){ // splitters not created yet
34681             var pos = 0, locked = true;
34682             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34683                 if(cm.isHidden(i)) continue;
34684                 var w = cm.getColumnWidth(i); // make sure it's a number
34685                 if(!cm.isLocked(i) && locked){
34686                     pos = 0;
34687                     locked = false;
34688                 }
34689                 pos += w;
34690                 s[i].style.left = (pos-this.splitOffset) + "px";
34691             }
34692         }
34693     },
34694
34695     handleHiddenChange : function(colModel, colIndex, hidden){
34696         if(hidden){
34697             this.hideColumn(colIndex);
34698         }else{
34699             this.unhideColumn(colIndex);
34700         }
34701     },
34702
34703     hideColumn : function(colIndex){
34704         var cid = this.getColumnId(colIndex);
34705         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
34706         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
34707         if(Roo.isSafari){
34708             this.updateHeaders();
34709         }
34710         this.updateSplitters();
34711         this.layout();
34712     },
34713
34714     unhideColumn : function(colIndex){
34715         var cid = this.getColumnId(colIndex);
34716         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
34717         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
34718
34719         if(Roo.isSafari){
34720             this.updateHeaders();
34721         }
34722         this.updateSplitters();
34723         this.layout();
34724     },
34725
34726     insertRows : function(dm, firstRow, lastRow, isUpdate){
34727         if(firstRow == 0 && lastRow == dm.getCount()-1){
34728             this.refresh();
34729         }else{
34730             if(!isUpdate){
34731                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
34732             }
34733             var s = this.getScrollState();
34734             var markup = this.renderRows(firstRow, lastRow);
34735             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
34736             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
34737             this.restoreScroll(s);
34738             if(!isUpdate){
34739                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
34740                 this.syncRowHeights(firstRow, lastRow);
34741                 this.stripeRows(firstRow);
34742                 this.layout();
34743             }
34744         }
34745     },
34746
34747     bufferRows : function(markup, target, index){
34748         var before = null, trows = target.rows, tbody = target.tBodies[0];
34749         if(index < trows.length){
34750             before = trows[index];
34751         }
34752         var b = document.createElement("div");
34753         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
34754         var rows = b.firstChild.rows;
34755         for(var i = 0, len = rows.length; i < len; i++){
34756             if(before){
34757                 tbody.insertBefore(rows[0], before);
34758             }else{
34759                 tbody.appendChild(rows[0]);
34760             }
34761         }
34762         b.innerHTML = "";
34763         b = null;
34764     },
34765
34766     deleteRows : function(dm, firstRow, lastRow){
34767         if(dm.getRowCount()<1){
34768             this.fireEvent("beforerefresh", this);
34769             this.mainBody.update("");
34770             this.lockedBody.update("");
34771             this.fireEvent("refresh", this);
34772         }else{
34773             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
34774             var bt = this.getBodyTable();
34775             var tbody = bt.firstChild;
34776             var rows = bt.rows;
34777             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
34778                 tbody.removeChild(rows[firstRow]);
34779             }
34780             this.stripeRows(firstRow);
34781             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
34782         }
34783     },
34784
34785     updateRows : function(dataSource, firstRow, lastRow){
34786         var s = this.getScrollState();
34787         this.refresh();
34788         this.restoreScroll(s);
34789     },
34790
34791     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
34792         if(!noRefresh){
34793            this.refresh();
34794         }
34795         this.updateHeaderSortState();
34796     },
34797
34798     getScrollState : function(){
34799         
34800         var sb = this.scroller.dom;
34801         return {left: sb.scrollLeft, top: sb.scrollTop};
34802     },
34803
34804     stripeRows : function(startRow){
34805         if(!this.grid.stripeRows || this.ds.getCount() < 1){
34806             return;
34807         }
34808         startRow = startRow || 0;
34809         var rows = this.getBodyTable().rows;
34810         var lrows = this.getLockedTable().rows;
34811         var cls = ' x-grid-row-alt ';
34812         for(var i = startRow, len = rows.length; i < len; i++){
34813             var row = rows[i], lrow = lrows[i];
34814             var isAlt = ((i+1) % 2 == 0);
34815             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
34816             if(isAlt == hasAlt){
34817                 continue;
34818             }
34819             if(isAlt){
34820                 row.className += " x-grid-row-alt";
34821             }else{
34822                 row.className = row.className.replace("x-grid-row-alt", "");
34823             }
34824             if(lrow){
34825                 lrow.className = row.className;
34826             }
34827         }
34828     },
34829
34830     restoreScroll : function(state){
34831         //Roo.log('GridView.restoreScroll');
34832         var sb = this.scroller.dom;
34833         sb.scrollLeft = state.left;
34834         sb.scrollTop = state.top;
34835         this.syncScroll();
34836     },
34837
34838     syncScroll : function(){
34839         //Roo.log('GridView.syncScroll');
34840         var sb = this.scroller.dom;
34841         var sh = this.mainHd.dom;
34842         var bs = this.mainBody.dom;
34843         var lv = this.lockedBody.dom;
34844         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
34845         lv.scrollTop = bs.scrollTop = sb.scrollTop;
34846     },
34847
34848     handleScroll : function(e){
34849         this.syncScroll();
34850         var sb = this.scroller.dom;
34851         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
34852         e.stopEvent();
34853     },
34854
34855     handleWheel : function(e){
34856         var d = e.getWheelDelta();
34857         this.scroller.dom.scrollTop -= d*22;
34858         // set this here to prevent jumpy scrolling on large tables
34859         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
34860         e.stopEvent();
34861     },
34862
34863     renderRows : function(startRow, endRow){
34864         // pull in all the crap needed to render rows
34865         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
34866         var colCount = cm.getColumnCount();
34867
34868         if(ds.getCount() < 1){
34869             return ["", ""];
34870         }
34871
34872         // build a map for all the columns
34873         var cs = [];
34874         for(var i = 0; i < colCount; i++){
34875             var name = cm.getDataIndex(i);
34876             cs[i] = {
34877                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
34878                 renderer : cm.getRenderer(i),
34879                 id : cm.getColumnId(i),
34880                 locked : cm.isLocked(i)
34881             };
34882         }
34883
34884         startRow = startRow || 0;
34885         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
34886
34887         // records to render
34888         var rs = ds.getRange(startRow, endRow);
34889
34890         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
34891     },
34892
34893     // As much as I hate to duplicate code, this was branched because FireFox really hates
34894     // [].join("") on strings. The performance difference was substantial enough to
34895     // branch this function
34896     doRender : Roo.isGecko ?
34897             function(cs, rs, ds, startRow, colCount, stripe){
34898                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34899                 // buffers
34900                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34901                 
34902                 var hasListener = this.grid.hasListener('rowclass');
34903                 var rowcfg = {};
34904                 for(var j = 0, len = rs.length; j < len; j++){
34905                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
34906                     for(var i = 0; i < colCount; i++){
34907                         c = cs[i];
34908                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34909                         p.id = c.id;
34910                         p.css = p.attr = "";
34911                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34912                         if(p.value == undefined || p.value === "") p.value = "&#160;";
34913                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
34914                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
34915                         }
34916                         var markup = ct.apply(p);
34917                         if(!c.locked){
34918                             cb+= markup;
34919                         }else{
34920                             lcb+= markup;
34921                         }
34922                     }
34923                     var alt = [];
34924                     if(stripe && ((rowIndex+1) % 2 == 0)){
34925                         alt.push("x-grid-row-alt")
34926                     }
34927                     if(r.dirty){
34928                         alt.push(  " x-grid-dirty-row");
34929                     }
34930                     rp.cells = lcb;
34931                     if(this.getRowClass){
34932                         alt.push(this.getRowClass(r, rowIndex));
34933                     }
34934                     if (hasListener) {
34935                         rowcfg = {
34936                              
34937                             record: r,
34938                             rowIndex : rowIndex,
34939                             rowClass : ''
34940                         }
34941                         this.grid.fireEvent('rowclass', this, rowcfg);
34942                         alt.push(rowcfg.rowClass);
34943                     }
34944                     rp.alt = alt.join(" ");
34945                     lbuf+= rt.apply(rp);
34946                     rp.cells = cb;
34947                     buf+=  rt.apply(rp);
34948                 }
34949                 return [lbuf, buf];
34950             } :
34951             function(cs, rs, ds, startRow, colCount, stripe){
34952                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34953                 // buffers
34954                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34955                 var hasListener = this.grid.hasListener('rowclass');
34956  
34957                 var rowcfg = {};
34958                 for(var j = 0, len = rs.length; j < len; j++){
34959                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
34960                     for(var i = 0; i < colCount; i++){
34961                         c = cs[i];
34962                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34963                         p.id = c.id;
34964                         p.css = p.attr = "";
34965                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34966                         if(p.value == undefined || p.value === "") p.value = "&#160;";
34967                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
34968                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
34969                         }
34970                         
34971                         var markup = ct.apply(p);
34972                         if(!c.locked){
34973                             cb[cb.length] = markup;
34974                         }else{
34975                             lcb[lcb.length] = markup;
34976                         }
34977                     }
34978                     var alt = [];
34979                     if(stripe && ((rowIndex+1) % 2 == 0)){
34980                         alt.push( "x-grid-row-alt");
34981                     }
34982                     if(r.dirty){
34983                         alt.push(" x-grid-dirty-row");
34984                     }
34985                     rp.cells = lcb;
34986                     if(this.getRowClass){
34987                         alt.push( this.getRowClass(r, rowIndex));
34988                     }
34989                     if (hasListener) {
34990                         rowcfg = {
34991                              
34992                             record: r,
34993                             rowIndex : rowIndex,
34994                             rowClass : ''
34995                         }
34996                         this.grid.fireEvent('rowclass', this, rowcfg);
34997                         alt.push(rowcfg.rowClass);
34998                     }
34999                     rp.alt = alt.join(" ");
35000                     rp.cells = lcb.join("");
35001                     lbuf[lbuf.length] = rt.apply(rp);
35002                     rp.cells = cb.join("");
35003                     buf[buf.length] =  rt.apply(rp);
35004                 }
35005                 return [lbuf.join(""), buf.join("")];
35006             },
35007
35008     renderBody : function(){
35009         var markup = this.renderRows();
35010         var bt = this.templates.body;
35011         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
35012     },
35013
35014     /**
35015      * Refreshes the grid
35016      * @param {Boolean} headersToo
35017      */
35018     refresh : function(headersToo){
35019         this.fireEvent("beforerefresh", this);
35020         this.grid.stopEditing();
35021         var result = this.renderBody();
35022         this.lockedBody.update(result[0]);
35023         this.mainBody.update(result[1]);
35024         if(headersToo === true){
35025             this.updateHeaders();
35026             this.updateColumns();
35027             this.updateSplitters();
35028             this.updateHeaderSortState();
35029         }
35030         this.syncRowHeights();
35031         this.layout();
35032         this.fireEvent("refresh", this);
35033     },
35034
35035     handleColumnMove : function(cm, oldIndex, newIndex){
35036         this.indexMap = null;
35037         var s = this.getScrollState();
35038         this.refresh(true);
35039         this.restoreScroll(s);
35040         this.afterMove(newIndex);
35041     },
35042
35043     afterMove : function(colIndex){
35044         if(this.enableMoveAnim && Roo.enableFx){
35045             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
35046         }
35047         // if multisort - fix sortOrder, and reload..
35048         if (this.grid.dataSource.multiSort) {
35049             // the we can call sort again..
35050             var dm = this.grid.dataSource;
35051             var cm = this.grid.colModel;
35052             var so = [];
35053             for(var i = 0; i < cm.config.length; i++ ) {
35054                 
35055                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
35056                     continue; // dont' bother, it's not in sort list or being set.
35057                 }
35058                 
35059                 so.push(cm.config[i].dataIndex);
35060             };
35061             dm.sortOrder = so;
35062             dm.load(dm.lastOptions);
35063             
35064             
35065         }
35066         
35067     },
35068
35069     updateCell : function(dm, rowIndex, dataIndex){
35070         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
35071         if(typeof colIndex == "undefined"){ // not present in grid
35072             return;
35073         }
35074         var cm = this.grid.colModel;
35075         var cell = this.getCell(rowIndex, colIndex);
35076         var cellText = this.getCellText(rowIndex, colIndex);
35077
35078         var p = {
35079             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
35080             id : cm.getColumnId(colIndex),
35081             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
35082         };
35083         var renderer = cm.getRenderer(colIndex);
35084         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
35085         if(typeof val == "undefined" || val === "") val = "&#160;";
35086         cellText.innerHTML = val;
35087         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
35088         this.syncRowHeights(rowIndex, rowIndex);
35089     },
35090
35091     calcColumnWidth : function(colIndex, maxRowsToMeasure){
35092         var maxWidth = 0;
35093         if(this.grid.autoSizeHeaders){
35094             var h = this.getHeaderCellMeasure(colIndex);
35095             maxWidth = Math.max(maxWidth, h.scrollWidth);
35096         }
35097         var tb, index;
35098         if(this.cm.isLocked(colIndex)){
35099             tb = this.getLockedTable();
35100             index = colIndex;
35101         }else{
35102             tb = this.getBodyTable();
35103             index = colIndex - this.cm.getLockedCount();
35104         }
35105         if(tb && tb.rows){
35106             var rows = tb.rows;
35107             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
35108             for(var i = 0; i < stopIndex; i++){
35109                 var cell = rows[i].childNodes[index].firstChild;
35110                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
35111             }
35112         }
35113         return maxWidth + /*margin for error in IE*/ 5;
35114     },
35115     /**
35116      * Autofit a column to its content.
35117      * @param {Number} colIndex
35118      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
35119      */
35120      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
35121          if(this.cm.isHidden(colIndex)){
35122              return; // can't calc a hidden column
35123          }
35124         if(forceMinSize){
35125             var cid = this.cm.getColumnId(colIndex);
35126             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
35127            if(this.grid.autoSizeHeaders){
35128                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
35129            }
35130         }
35131         var newWidth = this.calcColumnWidth(colIndex);
35132         this.cm.setColumnWidth(colIndex,
35133             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
35134         if(!suppressEvent){
35135             this.grid.fireEvent("columnresize", colIndex, newWidth);
35136         }
35137     },
35138
35139     /**
35140      * Autofits all columns to their content and then expands to fit any extra space in the grid
35141      */
35142      autoSizeColumns : function(){
35143         var cm = this.grid.colModel;
35144         var colCount = cm.getColumnCount();
35145         for(var i = 0; i < colCount; i++){
35146             this.autoSizeColumn(i, true, true);
35147         }
35148         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
35149             this.fitColumns();
35150         }else{
35151             this.updateColumns();
35152             this.layout();
35153         }
35154     },
35155
35156     /**
35157      * Autofits all columns to the grid's width proportionate with their current size
35158      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
35159      */
35160     fitColumns : function(reserveScrollSpace){
35161         var cm = this.grid.colModel;
35162         var colCount = cm.getColumnCount();
35163         var cols = [];
35164         var width = 0;
35165         var i, w;
35166         for (i = 0; i < colCount; i++){
35167             if(!cm.isHidden(i) && !cm.isFixed(i)){
35168                 w = cm.getColumnWidth(i);
35169                 cols.push(i);
35170                 cols.push(w);
35171                 width += w;
35172             }
35173         }
35174         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
35175         if(reserveScrollSpace){
35176             avail -= 17;
35177         }
35178         var frac = (avail - cm.getTotalWidth())/width;
35179         while (cols.length){
35180             w = cols.pop();
35181             i = cols.pop();
35182             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
35183         }
35184         this.updateColumns();
35185         this.layout();
35186     },
35187
35188     onRowSelect : function(rowIndex){
35189         var row = this.getRowComposite(rowIndex);
35190         row.addClass("x-grid-row-selected");
35191     },
35192
35193     onRowDeselect : function(rowIndex){
35194         var row = this.getRowComposite(rowIndex);
35195         row.removeClass("x-grid-row-selected");
35196     },
35197
35198     onCellSelect : function(row, col){
35199         var cell = this.getCell(row, col);
35200         if(cell){
35201             Roo.fly(cell).addClass("x-grid-cell-selected");
35202         }
35203     },
35204
35205     onCellDeselect : function(row, col){
35206         var cell = this.getCell(row, col);
35207         if(cell){
35208             Roo.fly(cell).removeClass("x-grid-cell-selected");
35209         }
35210     },
35211
35212     updateHeaderSortState : function(){
35213         
35214         // sort state can be single { field: xxx, direction : yyy}
35215         // or   { xxx=>ASC , yyy : DESC ..... }
35216         
35217         var mstate = {};
35218         if (!this.ds.multiSort) { 
35219             var state = this.ds.getSortState();
35220             if(!state){
35221                 return;
35222             }
35223             mstate[state.field] = state.direction;
35224             // FIXME... - this is not used here.. but might be elsewhere..
35225             this.sortState = state;
35226             
35227         } else {
35228             mstate = this.ds.sortToggle;
35229         }
35230         //remove existing sort classes..
35231         
35232         var sc = this.sortClasses;
35233         var hds = this.el.select(this.headerSelector).removeClass(sc);
35234         
35235         for(var f in mstate) {
35236         
35237             var sortColumn = this.cm.findColumnIndex(f);
35238             
35239             if(sortColumn != -1){
35240                 var sortDir = mstate[f];        
35241                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
35242             }
35243         }
35244         
35245          
35246         
35247     },
35248
35249
35250     handleHeaderClick : function(g, index){
35251         if(this.headersDisabled){
35252             return;
35253         }
35254         var dm = g.dataSource, cm = g.colModel;
35255         if(!cm.isSortable(index)){
35256             return;
35257         }
35258         g.stopEditing();
35259         
35260         if (dm.multiSort) {
35261             // update the sortOrder
35262             var so = [];
35263             for(var i = 0; i < cm.config.length; i++ ) {
35264                 
35265                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
35266                     continue; // dont' bother, it's not in sort list or being set.
35267                 }
35268                 
35269                 so.push(cm.config[i].dataIndex);
35270             };
35271             dm.sortOrder = so;
35272         }
35273         
35274         
35275         dm.sort(cm.getDataIndex(index));
35276     },
35277
35278
35279     destroy : function(){
35280         if(this.colMenu){
35281             this.colMenu.removeAll();
35282             Roo.menu.MenuMgr.unregister(this.colMenu);
35283             this.colMenu.getEl().remove();
35284             delete this.colMenu;
35285         }
35286         if(this.hmenu){
35287             this.hmenu.removeAll();
35288             Roo.menu.MenuMgr.unregister(this.hmenu);
35289             this.hmenu.getEl().remove();
35290             delete this.hmenu;
35291         }
35292         if(this.grid.enableColumnMove){
35293             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
35294             if(dds){
35295                 for(var dd in dds){
35296                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
35297                         var elid = dds[dd].dragElId;
35298                         dds[dd].unreg();
35299                         Roo.get(elid).remove();
35300                     } else if(dds[dd].config.isTarget){
35301                         dds[dd].proxyTop.remove();
35302                         dds[dd].proxyBottom.remove();
35303                         dds[dd].unreg();
35304                     }
35305                     if(Roo.dd.DDM.locationCache[dd]){
35306                         delete Roo.dd.DDM.locationCache[dd];
35307                     }
35308                 }
35309                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
35310             }
35311         }
35312         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
35313         this.bind(null, null);
35314         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
35315     },
35316
35317     handleLockChange : function(){
35318         this.refresh(true);
35319     },
35320
35321     onDenyColumnLock : function(){
35322
35323     },
35324
35325     onDenyColumnHide : function(){
35326
35327     },
35328
35329     handleHdMenuClick : function(item){
35330         var index = this.hdCtxIndex;
35331         var cm = this.cm, ds = this.ds;
35332         switch(item.id){
35333             case "asc":
35334                 ds.sort(cm.getDataIndex(index), "ASC");
35335                 break;
35336             case "desc":
35337                 ds.sort(cm.getDataIndex(index), "DESC");
35338                 break;
35339             case "lock":
35340                 var lc = cm.getLockedCount();
35341                 if(cm.getColumnCount(true) <= lc+1){
35342                     this.onDenyColumnLock();
35343                     return;
35344                 }
35345                 if(lc != index){
35346                     cm.setLocked(index, true, true);
35347                     cm.moveColumn(index, lc);
35348                     this.grid.fireEvent("columnmove", index, lc);
35349                 }else{
35350                     cm.setLocked(index, true);
35351                 }
35352             break;
35353             case "unlock":
35354                 var lc = cm.getLockedCount();
35355                 if((lc-1) != index){
35356                     cm.setLocked(index, false, true);
35357                     cm.moveColumn(index, lc-1);
35358                     this.grid.fireEvent("columnmove", index, lc-1);
35359                 }else{
35360                     cm.setLocked(index, false);
35361                 }
35362             break;
35363             default:
35364                 index = cm.getIndexById(item.id.substr(4));
35365                 if(index != -1){
35366                     if(item.checked && cm.getColumnCount(true) <= 1){
35367                         this.onDenyColumnHide();
35368                         return false;
35369                     }
35370                     cm.setHidden(index, item.checked);
35371                 }
35372         }
35373         return true;
35374     },
35375
35376     beforeColMenuShow : function(){
35377         var cm = this.cm,  colCount = cm.getColumnCount();
35378         this.colMenu.removeAll();
35379         for(var i = 0; i < colCount; i++){
35380             this.colMenu.add(new Roo.menu.CheckItem({
35381                 id: "col-"+cm.getColumnId(i),
35382                 text: cm.getColumnHeader(i),
35383                 checked: !cm.isHidden(i),
35384                 hideOnClick:false
35385             }));
35386         }
35387     },
35388
35389     handleHdCtx : function(g, index, e){
35390         e.stopEvent();
35391         var hd = this.getHeaderCell(index);
35392         this.hdCtxIndex = index;
35393         var ms = this.hmenu.items, cm = this.cm;
35394         ms.get("asc").setDisabled(!cm.isSortable(index));
35395         ms.get("desc").setDisabled(!cm.isSortable(index));
35396         if(this.grid.enableColLock !== false){
35397             ms.get("lock").setDisabled(cm.isLocked(index));
35398             ms.get("unlock").setDisabled(!cm.isLocked(index));
35399         }
35400         this.hmenu.show(hd, "tl-bl");
35401     },
35402
35403     handleHdOver : function(e){
35404         var hd = this.findHeaderCell(e.getTarget());
35405         if(hd && !this.headersDisabled){
35406             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
35407                this.fly(hd).addClass("x-grid-hd-over");
35408             }
35409         }
35410     },
35411
35412     handleHdOut : function(e){
35413         var hd = this.findHeaderCell(e.getTarget());
35414         if(hd){
35415             this.fly(hd).removeClass("x-grid-hd-over");
35416         }
35417     },
35418
35419     handleSplitDblClick : function(e, t){
35420         var i = this.getCellIndex(t);
35421         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
35422             this.autoSizeColumn(i, true);
35423             this.layout();
35424         }
35425     },
35426
35427     render : function(){
35428
35429         var cm = this.cm;
35430         var colCount = cm.getColumnCount();
35431
35432         if(this.grid.monitorWindowResize === true){
35433             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35434         }
35435         var header = this.renderHeaders();
35436         var body = this.templates.body.apply({rows:""});
35437         var html = this.templates.master.apply({
35438             lockedBody: body,
35439             body: body,
35440             lockedHeader: header[0],
35441             header: header[1]
35442         });
35443
35444         //this.updateColumns();
35445
35446         this.grid.getGridEl().dom.innerHTML = html;
35447
35448         this.initElements();
35449         
35450         // a kludge to fix the random scolling effect in webkit
35451         this.el.on("scroll", function() {
35452             this.el.dom.scrollTop=0; // hopefully not recursive..
35453         },this);
35454
35455         this.scroller.on("scroll", this.handleScroll, this);
35456         this.lockedBody.on("mousewheel", this.handleWheel, this);
35457         this.mainBody.on("mousewheel", this.handleWheel, this);
35458
35459         this.mainHd.on("mouseover", this.handleHdOver, this);
35460         this.mainHd.on("mouseout", this.handleHdOut, this);
35461         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
35462                 {delegate: "."+this.splitClass});
35463
35464         this.lockedHd.on("mouseover", this.handleHdOver, this);
35465         this.lockedHd.on("mouseout", this.handleHdOut, this);
35466         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
35467                 {delegate: "."+this.splitClass});
35468
35469         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
35470             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
35471         }
35472
35473         this.updateSplitters();
35474
35475         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
35476             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
35477             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
35478         }
35479
35480         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
35481             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
35482             this.hmenu.add(
35483                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
35484                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
35485             );
35486             if(this.grid.enableColLock !== false){
35487                 this.hmenu.add('-',
35488                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
35489                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
35490                 );
35491             }
35492             if(this.grid.enableColumnHide !== false){
35493
35494                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
35495                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
35496                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
35497
35498                 this.hmenu.add('-',
35499                     {id:"columns", text: this.columnsText, menu: this.colMenu}
35500                 );
35501             }
35502             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
35503
35504             this.grid.on("headercontextmenu", this.handleHdCtx, this);
35505         }
35506
35507         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
35508             this.dd = new Roo.grid.GridDragZone(this.grid, {
35509                 ddGroup : this.grid.ddGroup || 'GridDD'
35510             });
35511         }
35512
35513         /*
35514         for(var i = 0; i < colCount; i++){
35515             if(cm.isHidden(i)){
35516                 this.hideColumn(i);
35517             }
35518             if(cm.config[i].align){
35519                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
35520                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
35521             }
35522         }*/
35523         
35524         this.updateHeaderSortState();
35525
35526         this.beforeInitialResize();
35527         this.layout(true);
35528
35529         // two part rendering gives faster view to the user
35530         this.renderPhase2.defer(1, this);
35531     },
35532
35533     renderPhase2 : function(){
35534         // render the rows now
35535         this.refresh();
35536         if(this.grid.autoSizeColumns){
35537             this.autoSizeColumns();
35538         }
35539     },
35540
35541     beforeInitialResize : function(){
35542
35543     },
35544
35545     onColumnSplitterMoved : function(i, w){
35546         this.userResized = true;
35547         var cm = this.grid.colModel;
35548         cm.setColumnWidth(i, w, true);
35549         var cid = cm.getColumnId(i);
35550         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
35551         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
35552         this.updateSplitters();
35553         this.layout();
35554         this.grid.fireEvent("columnresize", i, w);
35555     },
35556
35557     syncRowHeights : function(startIndex, endIndex){
35558         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
35559             startIndex = startIndex || 0;
35560             var mrows = this.getBodyTable().rows;
35561             var lrows = this.getLockedTable().rows;
35562             var len = mrows.length-1;
35563             endIndex = Math.min(endIndex || len, len);
35564             for(var i = startIndex; i <= endIndex; i++){
35565                 var m = mrows[i], l = lrows[i];
35566                 var h = Math.max(m.offsetHeight, l.offsetHeight);
35567                 m.style.height = l.style.height = h + "px";
35568             }
35569         }
35570     },
35571
35572     layout : function(initialRender, is2ndPass){
35573         var g = this.grid;
35574         var auto = g.autoHeight;
35575         var scrollOffset = 16;
35576         var c = g.getGridEl(), cm = this.cm,
35577                 expandCol = g.autoExpandColumn,
35578                 gv = this;
35579         //c.beginMeasure();
35580
35581         if(!c.dom.offsetWidth){ // display:none?
35582             if(initialRender){
35583                 this.lockedWrap.show();
35584                 this.mainWrap.show();
35585             }
35586             return;
35587         }
35588
35589         var hasLock = this.cm.isLocked(0);
35590
35591         var tbh = this.headerPanel.getHeight();
35592         var bbh = this.footerPanel.getHeight();
35593
35594         if(auto){
35595             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
35596             var newHeight = ch + c.getBorderWidth("tb");
35597             if(g.maxHeight){
35598                 newHeight = Math.min(g.maxHeight, newHeight);
35599             }
35600             c.setHeight(newHeight);
35601         }
35602
35603         if(g.autoWidth){
35604             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
35605         }
35606
35607         var s = this.scroller;
35608
35609         var csize = c.getSize(true);
35610
35611         this.el.setSize(csize.width, csize.height);
35612
35613         this.headerPanel.setWidth(csize.width);
35614         this.footerPanel.setWidth(csize.width);
35615
35616         var hdHeight = this.mainHd.getHeight();
35617         var vw = csize.width;
35618         var vh = csize.height - (tbh + bbh);
35619
35620         s.setSize(vw, vh);
35621
35622         var bt = this.getBodyTable();
35623         var ltWidth = hasLock ?
35624                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
35625
35626         var scrollHeight = bt.offsetHeight;
35627         var scrollWidth = ltWidth + bt.offsetWidth;
35628         var vscroll = false, hscroll = false;
35629
35630         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
35631
35632         var lw = this.lockedWrap, mw = this.mainWrap;
35633         var lb = this.lockedBody, mb = this.mainBody;
35634
35635         setTimeout(function(){
35636             var t = s.dom.offsetTop;
35637             var w = s.dom.clientWidth,
35638                 h = s.dom.clientHeight;
35639
35640             lw.setTop(t);
35641             lw.setSize(ltWidth, h);
35642
35643             mw.setLeftTop(ltWidth, t);
35644             mw.setSize(w-ltWidth, h);
35645
35646             lb.setHeight(h-hdHeight);
35647             mb.setHeight(h-hdHeight);
35648
35649             if(is2ndPass !== true && !gv.userResized && expandCol){
35650                 // high speed resize without full column calculation
35651                 
35652                 var ci = cm.getIndexById(expandCol);
35653                 if (ci < 0) {
35654                     ci = cm.findColumnIndex(expandCol);
35655                 }
35656                 ci = Math.max(0, ci); // make sure it's got at least the first col.
35657                 var expandId = cm.getColumnId(ci);
35658                 var  tw = cm.getTotalWidth(false);
35659                 var currentWidth = cm.getColumnWidth(ci);
35660                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
35661                 if(currentWidth != cw){
35662                     cm.setColumnWidth(ci, cw, true);
35663                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
35664                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
35665                     gv.updateSplitters();
35666                     gv.layout(false, true);
35667                 }
35668             }
35669
35670             if(initialRender){
35671                 lw.show();
35672                 mw.show();
35673             }
35674             //c.endMeasure();
35675         }, 10);
35676     },
35677
35678     onWindowResize : function(){
35679         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
35680             return;
35681         }
35682         this.layout();
35683     },
35684
35685     appendFooter : function(parentEl){
35686         return null;
35687     },
35688
35689     sortAscText : "Sort Ascending",
35690     sortDescText : "Sort Descending",
35691     lockText : "Lock Column",
35692     unlockText : "Unlock Column",
35693     columnsText : "Columns"
35694 });
35695
35696
35697 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
35698     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
35699     this.proxy.el.addClass('x-grid3-col-dd');
35700 };
35701
35702 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
35703     handleMouseDown : function(e){
35704
35705     },
35706
35707     callHandleMouseDown : function(e){
35708         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
35709     }
35710 });
35711 /*
35712  * Based on:
35713  * Ext JS Library 1.1.1
35714  * Copyright(c) 2006-2007, Ext JS, LLC.
35715  *
35716  * Originally Released Under LGPL - original licence link has changed is not relivant.
35717  *
35718  * Fork - LGPL
35719  * <script type="text/javascript">
35720  */
35721  
35722 // private
35723 // This is a support class used internally by the Grid components
35724 Roo.grid.SplitDragZone = function(grid, hd, hd2){
35725     this.grid = grid;
35726     this.view = grid.getView();
35727     this.proxy = this.view.resizeProxy;
35728     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
35729         "gridSplitters" + this.grid.getGridEl().id, {
35730         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
35731     });
35732     this.setHandleElId(Roo.id(hd));
35733     this.setOuterHandleElId(Roo.id(hd2));
35734     this.scroll = false;
35735 };
35736 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
35737     fly: Roo.Element.fly,
35738
35739     b4StartDrag : function(x, y){
35740         this.view.headersDisabled = true;
35741         this.proxy.setHeight(this.view.mainWrap.getHeight());
35742         var w = this.cm.getColumnWidth(this.cellIndex);
35743         var minw = Math.max(w-this.grid.minColumnWidth, 0);
35744         this.resetConstraints();
35745         this.setXConstraint(minw, 1000);
35746         this.setYConstraint(0, 0);
35747         this.minX = x - minw;
35748         this.maxX = x + 1000;
35749         this.startPos = x;
35750         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
35751     },
35752
35753
35754     handleMouseDown : function(e){
35755         ev = Roo.EventObject.setEvent(e);
35756         var t = this.fly(ev.getTarget());
35757         if(t.hasClass("x-grid-split")){
35758             this.cellIndex = this.view.getCellIndex(t.dom);
35759             this.split = t.dom;
35760             this.cm = this.grid.colModel;
35761             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
35762                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
35763             }
35764         }
35765     },
35766
35767     endDrag : function(e){
35768         this.view.headersDisabled = false;
35769         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
35770         var diff = endX - this.startPos;
35771         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
35772     },
35773
35774     autoOffset : function(){
35775         this.setDelta(0,0);
35776     }
35777 });/*
35778  * Based on:
35779  * Ext JS Library 1.1.1
35780  * Copyright(c) 2006-2007, Ext JS, LLC.
35781  *
35782  * Originally Released Under LGPL - original licence link has changed is not relivant.
35783  *
35784  * Fork - LGPL
35785  * <script type="text/javascript">
35786  */
35787  
35788 // private
35789 // This is a support class used internally by the Grid components
35790 Roo.grid.GridDragZone = function(grid, config){
35791     this.view = grid.getView();
35792     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
35793     if(this.view.lockedBody){
35794         this.setHandleElId(Roo.id(this.view.mainBody.dom));
35795         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
35796     }
35797     this.scroll = false;
35798     this.grid = grid;
35799     this.ddel = document.createElement('div');
35800     this.ddel.className = 'x-grid-dd-wrap';
35801 };
35802
35803 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
35804     ddGroup : "GridDD",
35805
35806     getDragData : function(e){
35807         var t = Roo.lib.Event.getTarget(e);
35808         var rowIndex = this.view.findRowIndex(t);
35809         if(rowIndex !== false){
35810             var sm = this.grid.selModel;
35811             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
35812               //  sm.mouseDown(e, t);
35813             //}
35814             if (e.hasModifier()){
35815                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
35816             }
35817             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
35818         }
35819         return false;
35820     },
35821
35822     onInitDrag : function(e){
35823         var data = this.dragData;
35824         this.ddel.innerHTML = this.grid.getDragDropText();
35825         this.proxy.update(this.ddel);
35826         // fire start drag?
35827     },
35828
35829     afterRepair : function(){
35830         this.dragging = false;
35831     },
35832
35833     getRepairXY : function(e, data){
35834         return false;
35835     },
35836
35837     onEndDrag : function(data, e){
35838         // fire end drag?
35839     },
35840
35841     onValidDrop : function(dd, e, id){
35842         // fire drag drop?
35843         this.hideProxy();
35844     },
35845
35846     beforeInvalidDrop : function(e, id){
35847
35848     }
35849 });/*
35850  * Based on:
35851  * Ext JS Library 1.1.1
35852  * Copyright(c) 2006-2007, Ext JS, LLC.
35853  *
35854  * Originally Released Under LGPL - original licence link has changed is not relivant.
35855  *
35856  * Fork - LGPL
35857  * <script type="text/javascript">
35858  */
35859  
35860
35861 /**
35862  * @class Roo.grid.ColumnModel
35863  * @extends Roo.util.Observable
35864  * This is the default implementation of a ColumnModel used by the Grid. It defines
35865  * the columns in the grid.
35866  * <br>Usage:<br>
35867  <pre><code>
35868  var colModel = new Roo.grid.ColumnModel([
35869         {header: "Ticker", width: 60, sortable: true, locked: true},
35870         {header: "Company Name", width: 150, sortable: true},
35871         {header: "Market Cap.", width: 100, sortable: true},
35872         {header: "$ Sales", width: 100, sortable: true, renderer: money},
35873         {header: "Employees", width: 100, sortable: true, resizable: false}
35874  ]);
35875  </code></pre>
35876  * <p>
35877  
35878  * The config options listed for this class are options which may appear in each
35879  * individual column definition.
35880  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
35881  * @constructor
35882  * @param {Object} config An Array of column config objects. See this class's
35883  * config objects for details.
35884 */
35885 Roo.grid.ColumnModel = function(config){
35886         /**
35887      * The config passed into the constructor
35888      */
35889     this.config = config;
35890     this.lookup = {};
35891
35892     // if no id, create one
35893     // if the column does not have a dataIndex mapping,
35894     // map it to the order it is in the config
35895     for(var i = 0, len = config.length; i < len; i++){
35896         var c = config[i];
35897         if(typeof c.dataIndex == "undefined"){
35898             c.dataIndex = i;
35899         }
35900         if(typeof c.renderer == "string"){
35901             c.renderer = Roo.util.Format[c.renderer];
35902         }
35903         if(typeof c.id == "undefined"){
35904             c.id = Roo.id();
35905         }
35906         if(c.editor && c.editor.xtype){
35907             c.editor  = Roo.factory(c.editor, Roo.grid);
35908         }
35909         if(c.editor && c.editor.isFormField){
35910             c.editor = new Roo.grid.GridEditor(c.editor);
35911         }
35912         this.lookup[c.id] = c;
35913     }
35914
35915     /**
35916      * The width of columns which have no width specified (defaults to 100)
35917      * @type Number
35918      */
35919     this.defaultWidth = 100;
35920
35921     /**
35922      * Default sortable of columns which have no sortable specified (defaults to false)
35923      * @type Boolean
35924      */
35925     this.defaultSortable = false;
35926
35927     this.addEvents({
35928         /**
35929              * @event widthchange
35930              * Fires when the width of a column changes.
35931              * @param {ColumnModel} this
35932              * @param {Number} columnIndex The column index
35933              * @param {Number} newWidth The new width
35934              */
35935             "widthchange": true,
35936         /**
35937              * @event headerchange
35938              * Fires when the text of a header changes.
35939              * @param {ColumnModel} this
35940              * @param {Number} columnIndex The column index
35941              * @param {Number} newText The new header text
35942              */
35943             "headerchange": true,
35944         /**
35945              * @event hiddenchange
35946              * Fires when a column is hidden or "unhidden".
35947              * @param {ColumnModel} this
35948              * @param {Number} columnIndex The column index
35949              * @param {Boolean} hidden true if hidden, false otherwise
35950              */
35951             "hiddenchange": true,
35952             /**
35953          * @event columnmoved
35954          * Fires when a column is moved.
35955          * @param {ColumnModel} this
35956          * @param {Number} oldIndex
35957          * @param {Number} newIndex
35958          */
35959         "columnmoved" : true,
35960         /**
35961          * @event columlockchange
35962          * Fires when a column's locked state is changed
35963          * @param {ColumnModel} this
35964          * @param {Number} colIndex
35965          * @param {Boolean} locked true if locked
35966          */
35967         "columnlockchange" : true
35968     });
35969     Roo.grid.ColumnModel.superclass.constructor.call(this);
35970 };
35971 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
35972     /**
35973      * @cfg {String} header The header text to display in the Grid view.
35974      */
35975     /**
35976      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
35977      * {@link Roo.data.Record} definition from which to draw the column's value. If not
35978      * specified, the column's index is used as an index into the Record's data Array.
35979      */
35980     /**
35981      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
35982      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
35983      */
35984     /**
35985      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
35986      * Defaults to the value of the {@link #defaultSortable} property.
35987      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
35988      */
35989     /**
35990      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
35991      */
35992     /**
35993      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
35994      */
35995     /**
35996      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
35997      */
35998     /**
35999      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
36000      */
36001     /**
36002      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
36003      * given the cell's data value. See {@link #setRenderer}. If not specified, the
36004      * default renderer uses the raw data value.
36005      */
36006        /**
36007      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
36008      */
36009     /**
36010      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
36011      */
36012
36013     /**
36014      * Returns the id of the column at the specified index.
36015      * @param {Number} index The column index
36016      * @return {String} the id
36017      */
36018     getColumnId : function(index){
36019         return this.config[index].id;
36020     },
36021
36022     /**
36023      * Returns the column for a specified id.
36024      * @param {String} id The column id
36025      * @return {Object} the column
36026      */
36027     getColumnById : function(id){
36028         return this.lookup[id];
36029     },
36030
36031     
36032     /**
36033      * Returns the column for a specified dataIndex.
36034      * @param {String} dataIndex The column dataIndex
36035      * @return {Object|Boolean} the column or false if not found
36036      */
36037     getColumnByDataIndex: function(dataIndex){
36038         var index = this.findColumnIndex(dataIndex);
36039         return index > -1 ? this.config[index] : false;
36040     },
36041     
36042     /**
36043      * Returns the index for a specified column id.
36044      * @param {String} id The column id
36045      * @return {Number} the index, or -1 if not found
36046      */
36047     getIndexById : function(id){
36048         for(var i = 0, len = this.config.length; i < len; i++){
36049             if(this.config[i].id == id){
36050                 return i;
36051             }
36052         }
36053         return -1;
36054     },
36055     
36056     /**
36057      * Returns the index for a specified column dataIndex.
36058      * @param {String} dataIndex The column dataIndex
36059      * @return {Number} the index, or -1 if not found
36060      */
36061     
36062     findColumnIndex : function(dataIndex){
36063         for(var i = 0, len = this.config.length; i < len; i++){
36064             if(this.config[i].dataIndex == dataIndex){
36065                 return i;
36066             }
36067         }
36068         return -1;
36069     },
36070     
36071     
36072     moveColumn : function(oldIndex, newIndex){
36073         var c = this.config[oldIndex];
36074         this.config.splice(oldIndex, 1);
36075         this.config.splice(newIndex, 0, c);
36076         this.dataMap = null;
36077         this.fireEvent("columnmoved", this, oldIndex, newIndex);
36078     },
36079
36080     isLocked : function(colIndex){
36081         return this.config[colIndex].locked === true;
36082     },
36083
36084     setLocked : function(colIndex, value, suppressEvent){
36085         if(this.isLocked(colIndex) == value){
36086             return;
36087         }
36088         this.config[colIndex].locked = value;
36089         if(!suppressEvent){
36090             this.fireEvent("columnlockchange", this, colIndex, value);
36091         }
36092     },
36093
36094     getTotalLockedWidth : function(){
36095         var totalWidth = 0;
36096         for(var i = 0; i < this.config.length; i++){
36097             if(this.isLocked(i) && !this.isHidden(i)){
36098                 this.totalWidth += this.getColumnWidth(i);
36099             }
36100         }
36101         return totalWidth;
36102     },
36103
36104     getLockedCount : function(){
36105         for(var i = 0, len = this.config.length; i < len; i++){
36106             if(!this.isLocked(i)){
36107                 return i;
36108             }
36109         }
36110     },
36111
36112     /**
36113      * Returns the number of columns.
36114      * @return {Number}
36115      */
36116     getColumnCount : function(visibleOnly){
36117         if(visibleOnly === true){
36118             var c = 0;
36119             for(var i = 0, len = this.config.length; i < len; i++){
36120                 if(!this.isHidden(i)){
36121                     c++;
36122                 }
36123             }
36124             return c;
36125         }
36126         return this.config.length;
36127     },
36128
36129     /**
36130      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
36131      * @param {Function} fn
36132      * @param {Object} scope (optional)
36133      * @return {Array} result
36134      */
36135     getColumnsBy : function(fn, scope){
36136         var r = [];
36137         for(var i = 0, len = this.config.length; i < len; i++){
36138             var c = this.config[i];
36139             if(fn.call(scope||this, c, i) === true){
36140                 r[r.length] = c;
36141             }
36142         }
36143         return r;
36144     },
36145
36146     /**
36147      * Returns true if the specified column is sortable.
36148      * @param {Number} col The column index
36149      * @return {Boolean}
36150      */
36151     isSortable : function(col){
36152         if(typeof this.config[col].sortable == "undefined"){
36153             return this.defaultSortable;
36154         }
36155         return this.config[col].sortable;
36156     },
36157
36158     /**
36159      * Returns the rendering (formatting) function defined for the column.
36160      * @param {Number} col The column index.
36161      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
36162      */
36163     getRenderer : function(col){
36164         if(!this.config[col].renderer){
36165             return Roo.grid.ColumnModel.defaultRenderer;
36166         }
36167         return this.config[col].renderer;
36168     },
36169
36170     /**
36171      * Sets the rendering (formatting) function for a column.
36172      * @param {Number} col The column index
36173      * @param {Function} fn The function to use to process the cell's raw data
36174      * to return HTML markup for the grid view. The render function is called with
36175      * the following parameters:<ul>
36176      * <li>Data value.</li>
36177      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
36178      * <li>css A CSS style string to apply to the table cell.</li>
36179      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
36180      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
36181      * <li>Row index</li>
36182      * <li>Column index</li>
36183      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
36184      */
36185     setRenderer : function(col, fn){
36186         this.config[col].renderer = fn;
36187     },
36188
36189     /**
36190      * Returns the width for the specified column.
36191      * @param {Number} col The column index
36192      * @return {Number}
36193      */
36194     getColumnWidth : function(col){
36195         return this.config[col].width * 1 || this.defaultWidth;
36196     },
36197
36198     /**
36199      * Sets the width for a column.
36200      * @param {Number} col The column index
36201      * @param {Number} width The new width
36202      */
36203     setColumnWidth : function(col, width, suppressEvent){
36204         this.config[col].width = width;
36205         this.totalWidth = null;
36206         if(!suppressEvent){
36207              this.fireEvent("widthchange", this, col, width);
36208         }
36209     },
36210
36211     /**
36212      * Returns the total width of all columns.
36213      * @param {Boolean} includeHidden True to include hidden column widths
36214      * @return {Number}
36215      */
36216     getTotalWidth : function(includeHidden){
36217         if(!this.totalWidth){
36218             this.totalWidth = 0;
36219             for(var i = 0, len = this.config.length; i < len; i++){
36220                 if(includeHidden || !this.isHidden(i)){
36221                     this.totalWidth += this.getColumnWidth(i);
36222                 }
36223             }
36224         }
36225         return this.totalWidth;
36226     },
36227
36228     /**
36229      * Returns the header for the specified column.
36230      * @param {Number} col The column index
36231      * @return {String}
36232      */
36233     getColumnHeader : function(col){
36234         return this.config[col].header;
36235     },
36236
36237     /**
36238      * Sets the header for a column.
36239      * @param {Number} col The column index
36240      * @param {String} header The new header
36241      */
36242     setColumnHeader : function(col, header){
36243         this.config[col].header = header;
36244         this.fireEvent("headerchange", this, col, header);
36245     },
36246
36247     /**
36248      * Returns the tooltip for the specified column.
36249      * @param {Number} col The column index
36250      * @return {String}
36251      */
36252     getColumnTooltip : function(col){
36253             return this.config[col].tooltip;
36254     },
36255     /**
36256      * Sets the tooltip for a column.
36257      * @param {Number} col The column index
36258      * @param {String} tooltip The new tooltip
36259      */
36260     setColumnTooltip : function(col, tooltip){
36261             this.config[col].tooltip = tooltip;
36262     },
36263
36264     /**
36265      * Returns the dataIndex for the specified column.
36266      * @param {Number} col The column index
36267      * @return {Number}
36268      */
36269     getDataIndex : function(col){
36270         return this.config[col].dataIndex;
36271     },
36272
36273     /**
36274      * Sets the dataIndex for a column.
36275      * @param {Number} col The column index
36276      * @param {Number} dataIndex The new dataIndex
36277      */
36278     setDataIndex : function(col, dataIndex){
36279         this.config[col].dataIndex = dataIndex;
36280     },
36281
36282     
36283     
36284     /**
36285      * Returns true if the cell is editable.
36286      * @param {Number} colIndex The column index
36287      * @param {Number} rowIndex The row index
36288      * @return {Boolean}
36289      */
36290     isCellEditable : function(colIndex, rowIndex){
36291         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
36292     },
36293
36294     /**
36295      * Returns the editor defined for the cell/column.
36296      * return false or null to disable editing.
36297      * @param {Number} colIndex The column index
36298      * @param {Number} rowIndex The row index
36299      * @return {Object}
36300      */
36301     getCellEditor : function(colIndex, rowIndex){
36302         return this.config[colIndex].editor;
36303     },
36304
36305     /**
36306      * Sets if a column is editable.
36307      * @param {Number} col The column index
36308      * @param {Boolean} editable True if the column is editable
36309      */
36310     setEditable : function(col, editable){
36311         this.config[col].editable = editable;
36312     },
36313
36314
36315     /**
36316      * Returns true if the column is hidden.
36317      * @param {Number} colIndex The column index
36318      * @return {Boolean}
36319      */
36320     isHidden : function(colIndex){
36321         return this.config[colIndex].hidden;
36322     },
36323
36324
36325     /**
36326      * Returns true if the column width cannot be changed
36327      */
36328     isFixed : function(colIndex){
36329         return this.config[colIndex].fixed;
36330     },
36331
36332     /**
36333      * Returns true if the column can be resized
36334      * @return {Boolean}
36335      */
36336     isResizable : function(colIndex){
36337         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
36338     },
36339     /**
36340      * Sets if a column is hidden.
36341      * @param {Number} colIndex The column index
36342      * @param {Boolean} hidden True if the column is hidden
36343      */
36344     setHidden : function(colIndex, hidden){
36345         this.config[colIndex].hidden = hidden;
36346         this.totalWidth = null;
36347         this.fireEvent("hiddenchange", this, colIndex, hidden);
36348     },
36349
36350     /**
36351      * Sets the editor for a column.
36352      * @param {Number} col The column index
36353      * @param {Object} editor The editor object
36354      */
36355     setEditor : function(col, editor){
36356         this.config[col].editor = editor;
36357     }
36358 });
36359
36360 Roo.grid.ColumnModel.defaultRenderer = function(value){
36361         if(typeof value == "string" && value.length < 1){
36362             return "&#160;";
36363         }
36364         return value;
36365 };
36366
36367 // Alias for backwards compatibility
36368 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
36369 /*
36370  * Based on:
36371  * Ext JS Library 1.1.1
36372  * Copyright(c) 2006-2007, Ext JS, LLC.
36373  *
36374  * Originally Released Under LGPL - original licence link has changed is not relivant.
36375  *
36376  * Fork - LGPL
36377  * <script type="text/javascript">
36378  */
36379
36380 /**
36381  * @class Roo.grid.AbstractSelectionModel
36382  * @extends Roo.util.Observable
36383  * Abstract base class for grid SelectionModels.  It provides the interface that should be
36384  * implemented by descendant classes.  This class should not be directly instantiated.
36385  * @constructor
36386  */
36387 Roo.grid.AbstractSelectionModel = function(){
36388     this.locked = false;
36389     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
36390 };
36391
36392 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
36393     /** @ignore Called by the grid automatically. Do not call directly. */
36394     init : function(grid){
36395         this.grid = grid;
36396         this.initEvents();
36397     },
36398
36399     /**
36400      * Locks the selections.
36401      */
36402     lock : function(){
36403         this.locked = true;
36404     },
36405
36406     /**
36407      * Unlocks the selections.
36408      */
36409     unlock : function(){
36410         this.locked = false;
36411     },
36412
36413     /**
36414      * Returns true if the selections are locked.
36415      * @return {Boolean}
36416      */
36417     isLocked : function(){
36418         return this.locked;
36419     }
36420 });/*
36421  * Based on:
36422  * Ext JS Library 1.1.1
36423  * Copyright(c) 2006-2007, Ext JS, LLC.
36424  *
36425  * Originally Released Under LGPL - original licence link has changed is not relivant.
36426  *
36427  * Fork - LGPL
36428  * <script type="text/javascript">
36429  */
36430 /**
36431  * @extends Roo.grid.AbstractSelectionModel
36432  * @class Roo.grid.RowSelectionModel
36433  * The default SelectionModel used by {@link Roo.grid.Grid}.
36434  * It supports multiple selections and keyboard selection/navigation. 
36435  * @constructor
36436  * @param {Object} config
36437  */
36438 Roo.grid.RowSelectionModel = function(config){
36439     Roo.apply(this, config);
36440     this.selections = new Roo.util.MixedCollection(false, function(o){
36441         return o.id;
36442     });
36443
36444     this.last = false;
36445     this.lastActive = false;
36446
36447     this.addEvents({
36448         /**
36449              * @event selectionchange
36450              * Fires when the selection changes
36451              * @param {SelectionModel} this
36452              */
36453             "selectionchange" : true,
36454         /**
36455              * @event afterselectionchange
36456              * Fires after the selection changes (eg. by key press or clicking)
36457              * @param {SelectionModel} this
36458              */
36459             "afterselectionchange" : true,
36460         /**
36461              * @event beforerowselect
36462              * Fires when a row is selected being selected, return false to cancel.
36463              * @param {SelectionModel} this
36464              * @param {Number} rowIndex The selected index
36465              * @param {Boolean} keepExisting False if other selections will be cleared
36466              */
36467             "beforerowselect" : true,
36468         /**
36469              * @event rowselect
36470              * Fires when a row is selected.
36471              * @param {SelectionModel} this
36472              * @param {Number} rowIndex The selected index
36473              * @param {Roo.data.Record} r The record
36474              */
36475             "rowselect" : true,
36476         /**
36477              * @event rowdeselect
36478              * Fires when a row is deselected.
36479              * @param {SelectionModel} this
36480              * @param {Number} rowIndex The selected index
36481              */
36482         "rowdeselect" : true
36483     });
36484     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
36485     this.locked = false;
36486 };
36487
36488 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
36489     /**
36490      * @cfg {Boolean} singleSelect
36491      * True to allow selection of only one row at a time (defaults to false)
36492      */
36493     singleSelect : false,
36494
36495     // private
36496     initEvents : function(){
36497
36498         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
36499             this.grid.on("mousedown", this.handleMouseDown, this);
36500         }else{ // allow click to work like normal
36501             this.grid.on("rowclick", this.handleDragableRowClick, this);
36502         }
36503
36504         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
36505             "up" : function(e){
36506                 if(!e.shiftKey){
36507                     this.selectPrevious(e.shiftKey);
36508                 }else if(this.last !== false && this.lastActive !== false){
36509                     var last = this.last;
36510                     this.selectRange(this.last,  this.lastActive-1);
36511                     this.grid.getView().focusRow(this.lastActive);
36512                     if(last !== false){
36513                         this.last = last;
36514                     }
36515                 }else{
36516                     this.selectFirstRow();
36517                 }
36518                 this.fireEvent("afterselectionchange", this);
36519             },
36520             "down" : function(e){
36521                 if(!e.shiftKey){
36522                     this.selectNext(e.shiftKey);
36523                 }else if(this.last !== false && this.lastActive !== false){
36524                     var last = this.last;
36525                     this.selectRange(this.last,  this.lastActive+1);
36526                     this.grid.getView().focusRow(this.lastActive);
36527                     if(last !== false){
36528                         this.last = last;
36529                     }
36530                 }else{
36531                     this.selectFirstRow();
36532                 }
36533                 this.fireEvent("afterselectionchange", this);
36534             },
36535             scope: this
36536         });
36537
36538         var view = this.grid.view;
36539         view.on("refresh", this.onRefresh, this);
36540         view.on("rowupdated", this.onRowUpdated, this);
36541         view.on("rowremoved", this.onRemove, this);
36542     },
36543
36544     // private
36545     onRefresh : function(){
36546         var ds = this.grid.dataSource, i, v = this.grid.view;
36547         var s = this.selections;
36548         s.each(function(r){
36549             if((i = ds.indexOfId(r.id)) != -1){
36550                 v.onRowSelect(i);
36551             }else{
36552                 s.remove(r);
36553             }
36554         });
36555     },
36556
36557     // private
36558     onRemove : function(v, index, r){
36559         this.selections.remove(r);
36560     },
36561
36562     // private
36563     onRowUpdated : function(v, index, r){
36564         if(this.isSelected(r)){
36565             v.onRowSelect(index);
36566         }
36567     },
36568
36569     /**
36570      * Select records.
36571      * @param {Array} records The records to select
36572      * @param {Boolean} keepExisting (optional) True to keep existing selections
36573      */
36574     selectRecords : function(records, keepExisting){
36575         if(!keepExisting){
36576             this.clearSelections();
36577         }
36578         var ds = this.grid.dataSource;
36579         for(var i = 0, len = records.length; i < len; i++){
36580             this.selectRow(ds.indexOf(records[i]), true);
36581         }
36582     },
36583
36584     /**
36585      * Gets the number of selected rows.
36586      * @return {Number}
36587      */
36588     getCount : function(){
36589         return this.selections.length;
36590     },
36591
36592     /**
36593      * Selects the first row in the grid.
36594      */
36595     selectFirstRow : function(){
36596         this.selectRow(0);
36597     },
36598
36599     /**
36600      * Select the last row.
36601      * @param {Boolean} keepExisting (optional) True to keep existing selections
36602      */
36603     selectLastRow : function(keepExisting){
36604         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
36605     },
36606
36607     /**
36608      * Selects the row immediately following the last selected row.
36609      * @param {Boolean} keepExisting (optional) True to keep existing selections
36610      */
36611     selectNext : function(keepExisting){
36612         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
36613             this.selectRow(this.last+1, keepExisting);
36614             this.grid.getView().focusRow(this.last);
36615         }
36616     },
36617
36618     /**
36619      * Selects the row that precedes the last selected row.
36620      * @param {Boolean} keepExisting (optional) True to keep existing selections
36621      */
36622     selectPrevious : function(keepExisting){
36623         if(this.last){
36624             this.selectRow(this.last-1, keepExisting);
36625             this.grid.getView().focusRow(this.last);
36626         }
36627     },
36628
36629     /**
36630      * Returns the selected records
36631      * @return {Array} Array of selected records
36632      */
36633     getSelections : function(){
36634         return [].concat(this.selections.items);
36635     },
36636
36637     /**
36638      * Returns the first selected record.
36639      * @return {Record}
36640      */
36641     getSelected : function(){
36642         return this.selections.itemAt(0);
36643     },
36644
36645
36646     /**
36647      * Clears all selections.
36648      */
36649     clearSelections : function(fast){
36650         if(this.locked) return;
36651         if(fast !== true){
36652             var ds = this.grid.dataSource;
36653             var s = this.selections;
36654             s.each(function(r){
36655                 this.deselectRow(ds.indexOfId(r.id));
36656             }, this);
36657             s.clear();
36658         }else{
36659             this.selections.clear();
36660         }
36661         this.last = false;
36662     },
36663
36664
36665     /**
36666      * Selects all rows.
36667      */
36668     selectAll : function(){
36669         if(this.locked) return;
36670         this.selections.clear();
36671         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
36672             this.selectRow(i, true);
36673         }
36674     },
36675
36676     /**
36677      * Returns True if there is a selection.
36678      * @return {Boolean}
36679      */
36680     hasSelection : function(){
36681         return this.selections.length > 0;
36682     },
36683
36684     /**
36685      * Returns True if the specified row is selected.
36686      * @param {Number/Record} record The record or index of the record to check
36687      * @return {Boolean}
36688      */
36689     isSelected : function(index){
36690         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
36691         return (r && this.selections.key(r.id) ? true : false);
36692     },
36693
36694     /**
36695      * Returns True if the specified record id is selected.
36696      * @param {String} id The id of record to check
36697      * @return {Boolean}
36698      */
36699     isIdSelected : function(id){
36700         return (this.selections.key(id) ? true : false);
36701     },
36702
36703     // private
36704     handleMouseDown : function(e, t){
36705         var view = this.grid.getView(), rowIndex;
36706         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
36707             return;
36708         };
36709         if(e.shiftKey && this.last !== false){
36710             var last = this.last;
36711             this.selectRange(last, rowIndex, e.ctrlKey);
36712             this.last = last; // reset the last
36713             view.focusRow(rowIndex);
36714         }else{
36715             var isSelected = this.isSelected(rowIndex);
36716             if(e.button !== 0 && isSelected){
36717                 view.focusRow(rowIndex);
36718             }else if(e.ctrlKey && isSelected){
36719                 this.deselectRow(rowIndex);
36720             }else if(!isSelected){
36721                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
36722                 view.focusRow(rowIndex);
36723             }
36724         }
36725         this.fireEvent("afterselectionchange", this);
36726     },
36727     // private
36728     handleDragableRowClick :  function(grid, rowIndex, e) 
36729     {
36730         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
36731             this.selectRow(rowIndex, false);
36732             grid.view.focusRow(rowIndex);
36733              this.fireEvent("afterselectionchange", this);
36734         }
36735     },
36736     
36737     /**
36738      * Selects multiple rows.
36739      * @param {Array} rows Array of the indexes of the row to select
36740      * @param {Boolean} keepExisting (optional) True to keep existing selections
36741      */
36742     selectRows : function(rows, keepExisting){
36743         if(!keepExisting){
36744             this.clearSelections();
36745         }
36746         for(var i = 0, len = rows.length; i < len; i++){
36747             this.selectRow(rows[i], true);
36748         }
36749     },
36750
36751     /**
36752      * Selects a range of rows. All rows in between startRow and endRow are also selected.
36753      * @param {Number} startRow The index of the first row in the range
36754      * @param {Number} endRow The index of the last row in the range
36755      * @param {Boolean} keepExisting (optional) True to retain existing selections
36756      */
36757     selectRange : function(startRow, endRow, keepExisting){
36758         if(this.locked) return;
36759         if(!keepExisting){
36760             this.clearSelections();
36761         }
36762         if(startRow <= endRow){
36763             for(var i = startRow; i <= endRow; i++){
36764                 this.selectRow(i, true);
36765             }
36766         }else{
36767             for(var i = startRow; i >= endRow; i--){
36768                 this.selectRow(i, true);
36769             }
36770         }
36771     },
36772
36773     /**
36774      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
36775      * @param {Number} startRow The index of the first row in the range
36776      * @param {Number} endRow The index of the last row in the range
36777      */
36778     deselectRange : function(startRow, endRow, preventViewNotify){
36779         if(this.locked) return;
36780         for(var i = startRow; i <= endRow; i++){
36781             this.deselectRow(i, preventViewNotify);
36782         }
36783     },
36784
36785     /**
36786      * Selects a row.
36787      * @param {Number} row The index of the row to select
36788      * @param {Boolean} keepExisting (optional) True to keep existing selections
36789      */
36790     selectRow : function(index, keepExisting, preventViewNotify){
36791         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
36792         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
36793             if(!keepExisting || this.singleSelect){
36794                 this.clearSelections();
36795             }
36796             var r = this.grid.dataSource.getAt(index);
36797             this.selections.add(r);
36798             this.last = this.lastActive = index;
36799             if(!preventViewNotify){
36800                 this.grid.getView().onRowSelect(index);
36801             }
36802             this.fireEvent("rowselect", this, index, r);
36803             this.fireEvent("selectionchange", this);
36804         }
36805     },
36806
36807     /**
36808      * Deselects a row.
36809      * @param {Number} row The index of the row to deselect
36810      */
36811     deselectRow : function(index, preventViewNotify){
36812         if(this.locked) return;
36813         if(this.last == index){
36814             this.last = false;
36815         }
36816         if(this.lastActive == index){
36817             this.lastActive = false;
36818         }
36819         var r = this.grid.dataSource.getAt(index);
36820         this.selections.remove(r);
36821         if(!preventViewNotify){
36822             this.grid.getView().onRowDeselect(index);
36823         }
36824         this.fireEvent("rowdeselect", this, index);
36825         this.fireEvent("selectionchange", this);
36826     },
36827
36828     // private
36829     restoreLast : function(){
36830         if(this._last){
36831             this.last = this._last;
36832         }
36833     },
36834
36835     // private
36836     acceptsNav : function(row, col, cm){
36837         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36838     },
36839
36840     // private
36841     onEditorKey : function(field, e){
36842         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
36843         if(k == e.TAB){
36844             e.stopEvent();
36845             ed.completeEdit();
36846             if(e.shiftKey){
36847                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36848             }else{
36849                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36850             }
36851         }else if(k == e.ENTER && !e.ctrlKey){
36852             e.stopEvent();
36853             ed.completeEdit();
36854             if(e.shiftKey){
36855                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
36856             }else{
36857                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
36858             }
36859         }else if(k == e.ESC){
36860             ed.cancelEdit();
36861         }
36862         if(newCell){
36863             g.startEditing(newCell[0], newCell[1]);
36864         }
36865     }
36866 });/*
36867  * Based on:
36868  * Ext JS Library 1.1.1
36869  * Copyright(c) 2006-2007, Ext JS, LLC.
36870  *
36871  * Originally Released Under LGPL - original licence link has changed is not relivant.
36872  *
36873  * Fork - LGPL
36874  * <script type="text/javascript">
36875  */
36876 /**
36877  * @class Roo.grid.CellSelectionModel
36878  * @extends Roo.grid.AbstractSelectionModel
36879  * This class provides the basic implementation for cell selection in a grid.
36880  * @constructor
36881  * @param {Object} config The object containing the configuration of this model.
36882  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
36883  */
36884 Roo.grid.CellSelectionModel = function(config){
36885     Roo.apply(this, config);
36886
36887     this.selection = null;
36888
36889     this.addEvents({
36890         /**
36891              * @event beforerowselect
36892              * Fires before a cell is selected.
36893              * @param {SelectionModel} this
36894              * @param {Number} rowIndex The selected row index
36895              * @param {Number} colIndex The selected cell index
36896              */
36897             "beforecellselect" : true,
36898         /**
36899              * @event cellselect
36900              * Fires when a cell is selected.
36901              * @param {SelectionModel} this
36902              * @param {Number} rowIndex The selected row index
36903              * @param {Number} colIndex The selected cell index
36904              */
36905             "cellselect" : true,
36906         /**
36907              * @event selectionchange
36908              * Fires when the active selection changes.
36909              * @param {SelectionModel} this
36910              * @param {Object} selection null for no selection or an object (o) with two properties
36911                 <ul>
36912                 <li>o.record: the record object for the row the selection is in</li>
36913                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
36914                 </ul>
36915              */
36916             "selectionchange" : true,
36917         /**
36918              * @event tabend
36919              * Fires when the tab (or enter) was pressed on the last editable cell
36920              * You can use this to trigger add new row.
36921              * @param {SelectionModel} this
36922              */
36923             "tabend" : true,
36924          /**
36925              * @event beforeeditnext
36926              * Fires before the next editable sell is made active
36927              * You can use this to skip to another cell or fire the tabend
36928              *    if you set cell to false
36929              * @param {Object} eventdata object : { cell : [ row, col ] } 
36930              */
36931             "beforeeditnext" : true
36932     });
36933     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
36934 };
36935
36936 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
36937     
36938     enter_is_tab: false,
36939
36940     /** @ignore */
36941     initEvents : function(){
36942         this.grid.on("mousedown", this.handleMouseDown, this);
36943         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
36944         var view = this.grid.view;
36945         view.on("refresh", this.onViewChange, this);
36946         view.on("rowupdated", this.onRowUpdated, this);
36947         view.on("beforerowremoved", this.clearSelections, this);
36948         view.on("beforerowsinserted", this.clearSelections, this);
36949         if(this.grid.isEditor){
36950             this.grid.on("beforeedit", this.beforeEdit,  this);
36951         }
36952     },
36953
36954         //private
36955     beforeEdit : function(e){
36956         this.select(e.row, e.column, false, true, e.record);
36957     },
36958
36959         //private
36960     onRowUpdated : function(v, index, r){
36961         if(this.selection && this.selection.record == r){
36962             v.onCellSelect(index, this.selection.cell[1]);
36963         }
36964     },
36965
36966         //private
36967     onViewChange : function(){
36968         this.clearSelections(true);
36969     },
36970
36971         /**
36972          * Returns the currently selected cell,.
36973          * @return {Array} The selected cell (row, column) or null if none selected.
36974          */
36975     getSelectedCell : function(){
36976         return this.selection ? this.selection.cell : null;
36977     },
36978
36979     /**
36980      * Clears all selections.
36981      * @param {Boolean} true to prevent the gridview from being notified about the change.
36982      */
36983     clearSelections : function(preventNotify){
36984         var s = this.selection;
36985         if(s){
36986             if(preventNotify !== true){
36987                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
36988             }
36989             this.selection = null;
36990             this.fireEvent("selectionchange", this, null);
36991         }
36992     },
36993
36994     /**
36995      * Returns true if there is a selection.
36996      * @return {Boolean}
36997      */
36998     hasSelection : function(){
36999         return this.selection ? true : false;
37000     },
37001
37002     /** @ignore */
37003     handleMouseDown : function(e, t){
37004         var v = this.grid.getView();
37005         if(this.isLocked()){
37006             return;
37007         };
37008         var row = v.findRowIndex(t);
37009         var cell = v.findCellIndex(t);
37010         if(row !== false && cell !== false){
37011             this.select(row, cell);
37012         }
37013     },
37014
37015     /**
37016      * Selects a cell.
37017      * @param {Number} rowIndex
37018      * @param {Number} collIndex
37019      */
37020     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
37021         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
37022             this.clearSelections();
37023             r = r || this.grid.dataSource.getAt(rowIndex);
37024             this.selection = {
37025                 record : r,
37026                 cell : [rowIndex, colIndex]
37027             };
37028             if(!preventViewNotify){
37029                 var v = this.grid.getView();
37030                 v.onCellSelect(rowIndex, colIndex);
37031                 if(preventFocus !== true){
37032                     v.focusCell(rowIndex, colIndex);
37033                 }
37034             }
37035             this.fireEvent("cellselect", this, rowIndex, colIndex);
37036             this.fireEvent("selectionchange", this, this.selection);
37037         }
37038     },
37039
37040         //private
37041     isSelectable : function(rowIndex, colIndex, cm){
37042         return !cm.isHidden(colIndex);
37043     },
37044
37045     /** @ignore */
37046     handleKeyDown : function(e){
37047         //Roo.log('Cell Sel Model handleKeyDown');
37048         if(!e.isNavKeyPress()){
37049             return;
37050         }
37051         var g = this.grid, s = this.selection;
37052         if(!s){
37053             e.stopEvent();
37054             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
37055             if(cell){
37056                 this.select(cell[0], cell[1]);
37057             }
37058             return;
37059         }
37060         var sm = this;
37061         var walk = function(row, col, step){
37062             return g.walkCells(row, col, step, sm.isSelectable,  sm);
37063         };
37064         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
37065         var newCell;
37066
37067       
37068
37069         switch(k){
37070             case e.TAB:
37071                 // handled by onEditorKey
37072                 if (g.isEditor && g.editing) {
37073                     return;
37074                 }
37075                 if(e.shiftKey) {
37076                     newCell = walk(r, c-1, -1);
37077                 } else {
37078                     newCell = walk(r, c+1, 1);
37079                 }
37080                 break;
37081             
37082             case e.DOWN:
37083                newCell = walk(r+1, c, 1);
37084                 break;
37085             
37086             case e.UP:
37087                 newCell = walk(r-1, c, -1);
37088                 break;
37089             
37090             case e.RIGHT:
37091                 newCell = walk(r, c+1, 1);
37092                 break;
37093             
37094             case e.LEFT:
37095                 newCell = walk(r, c-1, -1);
37096                 break;
37097             
37098             case e.ENTER:
37099                 
37100                 if(g.isEditor && !g.editing){
37101                    g.startEditing(r, c);
37102                    e.stopEvent();
37103                    return;
37104                 }
37105                 
37106                 
37107              break;
37108         };
37109         if(newCell){
37110             this.select(newCell[0], newCell[1]);
37111             e.stopEvent();
37112             
37113         }
37114     },
37115
37116     acceptsNav : function(row, col, cm){
37117         return !cm.isHidden(col) && cm.isCellEditable(col, row);
37118     },
37119     /**
37120      * Selects a cell.
37121      * @param {Number} field (not used) - as it's normally used as a listener
37122      * @param {Number} e - event - fake it by using
37123      *
37124      * var e = Roo.EventObjectImpl.prototype;
37125      * e.keyCode = e.TAB
37126      *
37127      * 
37128      */
37129     onEditorKey : function(field, e){
37130         
37131         var k = e.getKey(),
37132             newCell,
37133             g = this.grid,
37134             ed = g.activeEditor,
37135             forward = false;
37136         ///Roo.log('onEditorKey' + k);
37137         
37138         
37139         if (this.enter_is_tab && k == e.ENTER) {
37140             k = e.TAB;
37141         }
37142         
37143         if(k == e.TAB){
37144             if(e.shiftKey){
37145                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
37146             }else{
37147                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
37148                 forward = true;
37149             }
37150             
37151             e.stopEvent();
37152             
37153         } else if(k == e.ENTER &&  !e.ctrlKey){
37154             ed.completeEdit();
37155             e.stopEvent();
37156             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
37157         
37158                 } else if(k == e.ESC){
37159             ed.cancelEdit();
37160         }
37161                 
37162         if (newCell) {
37163             var ecall = { cell : newCell, forward : forward };
37164             this.fireEvent('beforeeditnext', ecall );
37165             newCell = ecall.cell;
37166                         forward = ecall.forward;
37167         }
37168                 
37169         if(newCell){
37170             //Roo.log('next cell after edit');
37171             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
37172         } else if (forward) {
37173             // tabbed past last
37174             this.fireEvent.defer(100, this, ['tabend',this]);
37175         }
37176     }
37177 });/*
37178  * Based on:
37179  * Ext JS Library 1.1.1
37180  * Copyright(c) 2006-2007, Ext JS, LLC.
37181  *
37182  * Originally Released Under LGPL - original licence link has changed is not relivant.
37183  *
37184  * Fork - LGPL
37185  * <script type="text/javascript">
37186  */
37187  
37188 /**
37189  * @class Roo.grid.EditorGrid
37190  * @extends Roo.grid.Grid
37191  * Class for creating and editable grid.
37192  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
37193  * The container MUST have some type of size defined for the grid to fill. The container will be 
37194  * automatically set to position relative if it isn't already.
37195  * @param {Object} dataSource The data model to bind to
37196  * @param {Object} colModel The column model with info about this grid's columns
37197  */
37198 Roo.grid.EditorGrid = function(container, config){
37199     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
37200     this.getGridEl().addClass("xedit-grid");
37201
37202     if(!this.selModel){
37203         this.selModel = new Roo.grid.CellSelectionModel();
37204     }
37205
37206     this.activeEditor = null;
37207
37208         this.addEvents({
37209             /**
37210              * @event beforeedit
37211              * Fires before cell editing is triggered. The edit event object has the following properties <br />
37212              * <ul style="padding:5px;padding-left:16px;">
37213              * <li>grid - This grid</li>
37214              * <li>record - The record being edited</li>
37215              * <li>field - The field name being edited</li>
37216              * <li>value - The value for the field being edited.</li>
37217              * <li>row - The grid row index</li>
37218              * <li>column - The grid column index</li>
37219              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
37220              * </ul>
37221              * @param {Object} e An edit event (see above for description)
37222              */
37223             "beforeedit" : true,
37224             /**
37225              * @event afteredit
37226              * Fires after a cell is edited. <br />
37227              * <ul style="padding:5px;padding-left:16px;">
37228              * <li>grid - This grid</li>
37229              * <li>record - The record being edited</li>
37230              * <li>field - The field name being edited</li>
37231              * <li>value - The value being set</li>
37232              * <li>originalValue - The original value for the field, before the edit.</li>
37233              * <li>row - The grid row index</li>
37234              * <li>column - The grid column index</li>
37235              * </ul>
37236              * @param {Object} e An edit event (see above for description)
37237              */
37238             "afteredit" : true,
37239             /**
37240              * @event validateedit
37241              * Fires after a cell is edited, but before the value is set in the record. 
37242          * You can use this to modify the value being set in the field, Return false
37243              * to cancel the change. The edit event object has the following properties <br />
37244              * <ul style="padding:5px;padding-left:16px;">
37245          * <li>editor - This editor</li>
37246              * <li>grid - This grid</li>
37247              * <li>record - The record being edited</li>
37248              * <li>field - The field name being edited</li>
37249              * <li>value - The value being set</li>
37250              * <li>originalValue - The original value for the field, before the edit.</li>
37251              * <li>row - The grid row index</li>
37252              * <li>column - The grid column index</li>
37253              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
37254              * </ul>
37255              * @param {Object} e An edit event (see above for description)
37256              */
37257             "validateedit" : true
37258         });
37259     this.on("bodyscroll", this.stopEditing,  this);
37260     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
37261 };
37262
37263 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
37264     /**
37265      * @cfg {Number} clicksToEdit
37266      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
37267      */
37268     clicksToEdit: 2,
37269
37270     // private
37271     isEditor : true,
37272     // private
37273     trackMouseOver: false, // causes very odd FF errors
37274
37275     onCellDblClick : function(g, row, col){
37276         this.startEditing(row, col);
37277     },
37278
37279     onEditComplete : function(ed, value, startValue){
37280         this.editing = false;
37281         this.activeEditor = null;
37282         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
37283         var r = ed.record;
37284         var field = this.colModel.getDataIndex(ed.col);
37285         var e = {
37286             grid: this,
37287             record: r,
37288             field: field,
37289             originalValue: startValue,
37290             value: value,
37291             row: ed.row,
37292             column: ed.col,
37293             cancel:false,
37294             editor: ed
37295         };
37296         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
37297         cell.show();
37298           
37299         if(String(value) !== String(startValue)){
37300             
37301             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
37302                 r.set(field, e.value);
37303                 // if we are dealing with a combo box..
37304                 // then we also set the 'name' colum to be the displayField
37305                 if (ed.field.displayField && ed.field.name) {
37306                     r.set(ed.field.name, ed.field.el.dom.value);
37307                 }
37308                 
37309                 delete e.cancel; //?? why!!!
37310                 this.fireEvent("afteredit", e);
37311             }
37312         } else {
37313             this.fireEvent("afteredit", e); // always fire it!
37314         }
37315         this.view.focusCell(ed.row, ed.col);
37316     },
37317
37318     /**
37319      * Starts editing the specified for the specified row/column
37320      * @param {Number} rowIndex
37321      * @param {Number} colIndex
37322      */
37323     startEditing : function(row, col){
37324         this.stopEditing();
37325         if(this.colModel.isCellEditable(col, row)){
37326             this.view.ensureVisible(row, col, true);
37327           
37328             var r = this.dataSource.getAt(row);
37329             var field = this.colModel.getDataIndex(col);
37330             var cell = Roo.get(this.view.getCell(row,col));
37331             var e = {
37332                 grid: this,
37333                 record: r,
37334                 field: field,
37335                 value: r.data[field],
37336                 row: row,
37337                 column: col,
37338                 cancel:false 
37339             };
37340             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
37341                 this.editing = true;
37342                 var ed = this.colModel.getCellEditor(col, row);
37343                 
37344                 if (!ed) {
37345                     return;
37346                 }
37347                 if(!ed.rendered){
37348                     ed.render(ed.parentEl || document.body);
37349                 }
37350                 ed.field.reset();
37351                
37352                 cell.hide();
37353                 
37354                 (function(){ // complex but required for focus issues in safari, ie and opera
37355                     ed.row = row;
37356                     ed.col = col;
37357                     ed.record = r;
37358                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
37359                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
37360                     this.activeEditor = ed;
37361                     var v = r.data[field];
37362                     ed.startEdit(this.view.getCell(row, col), v);
37363                     // combo's with 'displayField and name set
37364                     if (ed.field.displayField && ed.field.name) {
37365                         ed.field.el.dom.value = r.data[ed.field.name];
37366                     }
37367                     
37368                     
37369                 }).defer(50, this);
37370             }
37371         }
37372     },
37373         
37374     /**
37375      * Stops any active editing
37376      */
37377     stopEditing : function(){
37378         if(this.activeEditor){
37379             this.activeEditor.completeEdit();
37380         }
37381         this.activeEditor = null;
37382     }
37383 });/*
37384  * Based on:
37385  * Ext JS Library 1.1.1
37386  * Copyright(c) 2006-2007, Ext JS, LLC.
37387  *
37388  * Originally Released Under LGPL - original licence link has changed is not relivant.
37389  *
37390  * Fork - LGPL
37391  * <script type="text/javascript">
37392  */
37393
37394 // private - not really -- you end up using it !
37395 // This is a support class used internally by the Grid components
37396
37397 /**
37398  * @class Roo.grid.GridEditor
37399  * @extends Roo.Editor
37400  * Class for creating and editable grid elements.
37401  * @param {Object} config any settings (must include field)
37402  */
37403 Roo.grid.GridEditor = function(field, config){
37404     if (!config && field.field) {
37405         config = field;
37406         field = Roo.factory(config.field, Roo.form);
37407     }
37408     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
37409     field.monitorTab = false;
37410 };
37411
37412 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
37413     
37414     /**
37415      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
37416      */
37417     
37418     alignment: "tl-tl",
37419     autoSize: "width",
37420     hideEl : false,
37421     cls: "x-small-editor x-grid-editor",
37422     shim:false,
37423     shadow:"frame"
37424 });/*
37425  * Based on:
37426  * Ext JS Library 1.1.1
37427  * Copyright(c) 2006-2007, Ext JS, LLC.
37428  *
37429  * Originally Released Under LGPL - original licence link has changed is not relivant.
37430  *
37431  * Fork - LGPL
37432  * <script type="text/javascript">
37433  */
37434   
37435
37436   
37437 Roo.grid.PropertyRecord = Roo.data.Record.create([
37438     {name:'name',type:'string'},  'value'
37439 ]);
37440
37441
37442 Roo.grid.PropertyStore = function(grid, source){
37443     this.grid = grid;
37444     this.store = new Roo.data.Store({
37445         recordType : Roo.grid.PropertyRecord
37446     });
37447     this.store.on('update', this.onUpdate,  this);
37448     if(source){
37449         this.setSource(source);
37450     }
37451     Roo.grid.PropertyStore.superclass.constructor.call(this);
37452 };
37453
37454
37455
37456 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
37457     setSource : function(o){
37458         this.source = o;
37459         this.store.removeAll();
37460         var data = [];
37461         for(var k in o){
37462             if(this.isEditableValue(o[k])){
37463                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
37464             }
37465         }
37466         this.store.loadRecords({records: data}, {}, true);
37467     },
37468
37469     onUpdate : function(ds, record, type){
37470         if(type == Roo.data.Record.EDIT){
37471             var v = record.data['value'];
37472             var oldValue = record.modified['value'];
37473             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
37474                 this.source[record.id] = v;
37475                 record.commit();
37476                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
37477             }else{
37478                 record.reject();
37479             }
37480         }
37481     },
37482
37483     getProperty : function(row){
37484        return this.store.getAt(row);
37485     },
37486
37487     isEditableValue: function(val){
37488         if(val && val instanceof Date){
37489             return true;
37490         }else if(typeof val == 'object' || typeof val == 'function'){
37491             return false;
37492         }
37493         return true;
37494     },
37495
37496     setValue : function(prop, value){
37497         this.source[prop] = value;
37498         this.store.getById(prop).set('value', value);
37499     },
37500
37501     getSource : function(){
37502         return this.source;
37503     }
37504 });
37505
37506 Roo.grid.PropertyColumnModel = function(grid, store){
37507     this.grid = grid;
37508     var g = Roo.grid;
37509     g.PropertyColumnModel.superclass.constructor.call(this, [
37510         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
37511         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
37512     ]);
37513     this.store = store;
37514     this.bselect = Roo.DomHelper.append(document.body, {
37515         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
37516             {tag: 'option', value: 'true', html: 'true'},
37517             {tag: 'option', value: 'false', html: 'false'}
37518         ]
37519     });
37520     Roo.id(this.bselect);
37521     var f = Roo.form;
37522     this.editors = {
37523         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
37524         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
37525         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
37526         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
37527         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
37528     };
37529     this.renderCellDelegate = this.renderCell.createDelegate(this);
37530     this.renderPropDelegate = this.renderProp.createDelegate(this);
37531 };
37532
37533 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
37534     
37535     
37536     nameText : 'Name',
37537     valueText : 'Value',
37538     
37539     dateFormat : 'm/j/Y',
37540     
37541     
37542     renderDate : function(dateVal){
37543         return dateVal.dateFormat(this.dateFormat);
37544     },
37545
37546     renderBool : function(bVal){
37547         return bVal ? 'true' : 'false';
37548     },
37549
37550     isCellEditable : function(colIndex, rowIndex){
37551         return colIndex == 1;
37552     },
37553
37554     getRenderer : function(col){
37555         return col == 1 ?
37556             this.renderCellDelegate : this.renderPropDelegate;
37557     },
37558
37559     renderProp : function(v){
37560         return this.getPropertyName(v);
37561     },
37562
37563     renderCell : function(val){
37564         var rv = val;
37565         if(val instanceof Date){
37566             rv = this.renderDate(val);
37567         }else if(typeof val == 'boolean'){
37568             rv = this.renderBool(val);
37569         }
37570         return Roo.util.Format.htmlEncode(rv);
37571     },
37572
37573     getPropertyName : function(name){
37574         var pn = this.grid.propertyNames;
37575         return pn && pn[name] ? pn[name] : name;
37576     },
37577
37578     getCellEditor : function(colIndex, rowIndex){
37579         var p = this.store.getProperty(rowIndex);
37580         var n = p.data['name'], val = p.data['value'];
37581         
37582         if(typeof(this.grid.customEditors[n]) == 'string'){
37583             return this.editors[this.grid.customEditors[n]];
37584         }
37585         if(typeof(this.grid.customEditors[n]) != 'undefined'){
37586             return this.grid.customEditors[n];
37587         }
37588         if(val instanceof Date){
37589             return this.editors['date'];
37590         }else if(typeof val == 'number'){
37591             return this.editors['number'];
37592         }else if(typeof val == 'boolean'){
37593             return this.editors['boolean'];
37594         }else{
37595             return this.editors['string'];
37596         }
37597     }
37598 });
37599
37600 /**
37601  * @class Roo.grid.PropertyGrid
37602  * @extends Roo.grid.EditorGrid
37603  * This class represents the  interface of a component based property grid control.
37604  * <br><br>Usage:<pre><code>
37605  var grid = new Roo.grid.PropertyGrid("my-container-id", {
37606       
37607  });
37608  // set any options
37609  grid.render();
37610  * </code></pre>
37611   
37612  * @constructor
37613  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37614  * The container MUST have some type of size defined for the grid to fill. The container will be
37615  * automatically set to position relative if it isn't already.
37616  * @param {Object} config A config object that sets properties on this grid.
37617  */
37618 Roo.grid.PropertyGrid = function(container, config){
37619     config = config || {};
37620     var store = new Roo.grid.PropertyStore(this);
37621     this.store = store;
37622     var cm = new Roo.grid.PropertyColumnModel(this, store);
37623     store.store.sort('name', 'ASC');
37624     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
37625         ds: store.store,
37626         cm: cm,
37627         enableColLock:false,
37628         enableColumnMove:false,
37629         stripeRows:false,
37630         trackMouseOver: false,
37631         clicksToEdit:1
37632     }, config));
37633     this.getGridEl().addClass('x-props-grid');
37634     this.lastEditRow = null;
37635     this.on('columnresize', this.onColumnResize, this);
37636     this.addEvents({
37637          /**
37638              * @event beforepropertychange
37639              * Fires before a property changes (return false to stop?)
37640              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37641              * @param {String} id Record Id
37642              * @param {String} newval New Value
37643          * @param {String} oldval Old Value
37644              */
37645         "beforepropertychange": true,
37646         /**
37647              * @event propertychange
37648              * Fires after a property changes
37649              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37650              * @param {String} id Record Id
37651              * @param {String} newval New Value
37652          * @param {String} oldval Old Value
37653              */
37654         "propertychange": true
37655     });
37656     this.customEditors = this.customEditors || {};
37657 };
37658 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
37659     
37660      /**
37661      * @cfg {Object} customEditors map of colnames=> custom editors.
37662      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
37663      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
37664      * false disables editing of the field.
37665          */
37666     
37667       /**
37668      * @cfg {Object} propertyNames map of property Names to their displayed value
37669          */
37670     
37671     render : function(){
37672         Roo.grid.PropertyGrid.superclass.render.call(this);
37673         this.autoSize.defer(100, this);
37674     },
37675
37676     autoSize : function(){
37677         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
37678         if(this.view){
37679             this.view.fitColumns();
37680         }
37681     },
37682
37683     onColumnResize : function(){
37684         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
37685         this.autoSize();
37686     },
37687     /**
37688      * Sets the data for the Grid
37689      * accepts a Key => Value object of all the elements avaiable.
37690      * @param {Object} data  to appear in grid.
37691      */
37692     setSource : function(source){
37693         this.store.setSource(source);
37694         //this.autoSize();
37695     },
37696     /**
37697      * Gets all the data from the grid.
37698      * @return {Object} data  data stored in grid
37699      */
37700     getSource : function(){
37701         return this.store.getSource();
37702     }
37703 });/*
37704  * Based on:
37705  * Ext JS Library 1.1.1
37706  * Copyright(c) 2006-2007, Ext JS, LLC.
37707  *
37708  * Originally Released Under LGPL - original licence link has changed is not relivant.
37709  *
37710  * Fork - LGPL
37711  * <script type="text/javascript">
37712  */
37713  
37714 /**
37715  * @class Roo.LoadMask
37716  * A simple utility class for generically masking elements while loading data.  If the element being masked has
37717  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
37718  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
37719  * element's UpdateManager load indicator and will be destroyed after the initial load.
37720  * @constructor
37721  * Create a new LoadMask
37722  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
37723  * @param {Object} config The config object
37724  */
37725 Roo.LoadMask = function(el, config){
37726     this.el = Roo.get(el);
37727     Roo.apply(this, config);
37728     if(this.store){
37729         this.store.on('beforeload', this.onBeforeLoad, this);
37730         this.store.on('load', this.onLoad, this);
37731         this.store.on('loadexception', this.onLoadException, this);
37732         this.removeMask = false;
37733     }else{
37734         var um = this.el.getUpdateManager();
37735         um.showLoadIndicator = false; // disable the default indicator
37736         um.on('beforeupdate', this.onBeforeLoad, this);
37737         um.on('update', this.onLoad, this);
37738         um.on('failure', this.onLoad, this);
37739         this.removeMask = true;
37740     }
37741 };
37742
37743 Roo.LoadMask.prototype = {
37744     /**
37745      * @cfg {Boolean} removeMask
37746      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
37747      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
37748      */
37749     /**
37750      * @cfg {String} msg
37751      * The text to display in a centered loading message box (defaults to 'Loading...')
37752      */
37753     msg : 'Loading...',
37754     /**
37755      * @cfg {String} msgCls
37756      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
37757      */
37758     msgCls : 'x-mask-loading',
37759
37760     /**
37761      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
37762      * @type Boolean
37763      */
37764     disabled: false,
37765
37766     /**
37767      * Disables the mask to prevent it from being displayed
37768      */
37769     disable : function(){
37770        this.disabled = true;
37771     },
37772
37773     /**
37774      * Enables the mask so that it can be displayed
37775      */
37776     enable : function(){
37777         this.disabled = false;
37778     },
37779     
37780     onLoadException : function()
37781     {
37782         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
37783             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
37784         }
37785         this.el.unmask(this.removeMask);
37786     },
37787     // private
37788     onLoad : function()
37789     {
37790         this.el.unmask(this.removeMask);
37791     },
37792
37793     // private
37794     onBeforeLoad : function(){
37795         if(!this.disabled){
37796             this.el.mask(this.msg, this.msgCls);
37797         }
37798     },
37799
37800     // private
37801     destroy : function(){
37802         if(this.store){
37803             this.store.un('beforeload', this.onBeforeLoad, this);
37804             this.store.un('load', this.onLoad, this);
37805             this.store.un('loadexception', this.onLoadException, this);
37806         }else{
37807             var um = this.el.getUpdateManager();
37808             um.un('beforeupdate', this.onBeforeLoad, this);
37809             um.un('update', this.onLoad, this);
37810             um.un('failure', this.onLoad, this);
37811         }
37812     }
37813 };/*
37814  * Based on:
37815  * Ext JS Library 1.1.1
37816  * Copyright(c) 2006-2007, Ext JS, LLC.
37817  *
37818  * Originally Released Under LGPL - original licence link has changed is not relivant.
37819  *
37820  * Fork - LGPL
37821  * <script type="text/javascript">
37822  */
37823 Roo.XTemplate = function(){
37824     Roo.XTemplate.superclass.constructor.apply(this, arguments);
37825     var s = this.html;
37826
37827     s = ['<tpl>', s, '</tpl>'].join('');
37828
37829     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
37830
37831     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
37832     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
37833     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
37834     var m, id = 0;
37835     var tpls = [];
37836
37837     while(m = s.match(re)){
37838        var m2 = m[0].match(nameRe);
37839        var m3 = m[0].match(ifRe);
37840        var m4 = m[0].match(execRe);
37841        var exp = null, fn = null, exec = null;
37842        var name = m2 && m2[1] ? m2[1] : '';
37843        if(m3){
37844            exp = m3 && m3[1] ? m3[1] : null;
37845            if(exp){
37846                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
37847            }
37848        }
37849        if(m4){
37850            exp = m4 && m4[1] ? m4[1] : null;
37851            if(exp){
37852                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
37853            }
37854        }
37855        if(name){
37856            switch(name){
37857                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
37858                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
37859                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
37860            }
37861        }
37862        tpls.push({
37863             id: id,
37864             target: name,
37865             exec: exec,
37866             test: fn,
37867             body: m[1]||''
37868         });
37869        s = s.replace(m[0], '{xtpl'+ id + '}');
37870        ++id;
37871     }
37872     for(var i = tpls.length-1; i >= 0; --i){
37873         this.compileTpl(tpls[i]);
37874     }
37875     this.master = tpls[tpls.length-1];
37876     this.tpls = tpls;
37877 };
37878 Roo.extend(Roo.XTemplate, Roo.Template, {
37879
37880     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
37881
37882     applySubTemplate : function(id, values, parent){
37883         var t = this.tpls[id];
37884         if(t.test && !t.test.call(this, values, parent)){
37885             return '';
37886         }
37887         if(t.exec && t.exec.call(this, values, parent)){
37888             return '';
37889         }
37890         var vs = t.target ? t.target.call(this, values, parent) : values;
37891         parent = t.target ? values : parent;
37892         if(t.target && vs instanceof Array){
37893             var buf = [];
37894             for(var i = 0, len = vs.length; i < len; i++){
37895                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
37896             }
37897             return buf.join('');
37898         }
37899         return t.compiled.call(this, vs, parent);
37900     },
37901
37902     compileTpl : function(tpl){
37903         var fm = Roo.util.Format;
37904         var useF = this.disableFormats !== true;
37905         var sep = Roo.isGecko ? "+" : ",";
37906         var fn = function(m, name, format, args){
37907             if(name.substr(0, 4) == 'xtpl'){
37908                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
37909             }
37910             var v;
37911             if(name.indexOf('.') != -1){
37912                 v = name;
37913             }else{
37914                 v = "values['" + name + "']";
37915             }
37916             if(format && useF){
37917                 args = args ? ',' + args : "";
37918                 if(format.substr(0, 5) != "this."){
37919                     format = "fm." + format + '(';
37920                 }else{
37921                     format = 'this.call("'+ format.substr(5) + '", ';
37922                     args = ", values";
37923                 }
37924             }else{
37925                 args= ''; format = "("+v+" === undefined ? '' : ";
37926             }
37927             return "'"+ sep + format + v + args + ")"+sep+"'";
37928         };
37929         var body;
37930         // branched to use + in gecko and [].join() in others
37931         if(Roo.isGecko){
37932             body = "tpl.compiled = function(values, parent){ return '" +
37933                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
37934                     "';};";
37935         }else{
37936             body = ["tpl.compiled = function(values, parent){ return ['"];
37937             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
37938             body.push("'].join('');};");
37939             body = body.join('');
37940         }
37941         /** eval:var:zzzzzzz */
37942         eval(body);
37943         return this;
37944     },
37945
37946     applyTemplate : function(values){
37947         return this.master.compiled.call(this, values, {});
37948         var s = this.subs;
37949     },
37950
37951     apply : function(){
37952         return this.applyTemplate.apply(this, arguments);
37953     },
37954
37955     compile : function(){return this;}
37956 });
37957
37958 Roo.XTemplate.from = function(el){
37959     el = Roo.getDom(el);
37960     return new Roo.XTemplate(el.value || el.innerHTML);
37961 };/*
37962  * Original code for Roojs - LGPL
37963  * <script type="text/javascript">
37964  */
37965  
37966 /**
37967  * @class Roo.XComponent
37968  * A delayed Element creator...
37969  * Or a way to group chunks of interface together.
37970  * 
37971  * Mypart.xyx = new Roo.XComponent({
37972
37973     parent : 'Mypart.xyz', // empty == document.element.!!
37974     order : '001',
37975     name : 'xxxx'
37976     region : 'xxxx'
37977     disabled : function() {} 
37978      
37979     tree : function() { // return an tree of xtype declared components
37980         var MODULE = this;
37981         return 
37982         {
37983             xtype : 'NestedLayoutPanel',
37984             // technicall
37985         }
37986      ]
37987  *})
37988  *
37989  *
37990  * It can be used to build a big heiracy, with parent etc.
37991  * or you can just use this to render a single compoent to a dom element
37992  * MYPART.render(Roo.Element | String(id) | dom_element )
37993  * 
37994  * @extends Roo.util.Observable
37995  * @constructor
37996  * @param cfg {Object} configuration of component
37997  * 
37998  */
37999 Roo.XComponent = function(cfg) {
38000     Roo.apply(this, cfg);
38001     this.addEvents({ 
38002         /**
38003              * @event built
38004              * Fires when this the componnt is built
38005              * @param {Roo.XComponent} c the component
38006              */
38007         'built' : true
38008         
38009     });
38010     this.region = this.region || 'center'; // default..
38011     Roo.XComponent.register(this);
38012     this.modules = false;
38013     this.el = false; // where the layout goes..
38014     
38015     
38016 }
38017 Roo.extend(Roo.XComponent, Roo.util.Observable, {
38018     /**
38019      * @property el
38020      * The created element (with Roo.factory())
38021      * @type {Roo.Layout}
38022      */
38023     el  : false,
38024     
38025     /**
38026      * @property el
38027      * for BC  - use el in new code
38028      * @type {Roo.Layout}
38029      */
38030     panel : false,
38031     
38032     /**
38033      * @property layout
38034      * for BC  - use el in new code
38035      * @type {Roo.Layout}
38036      */
38037     layout : false,
38038     
38039      /**
38040      * @cfg {Function|boolean} disabled
38041      * If this module is disabled by some rule, return true from the funtion
38042      */
38043     disabled : false,
38044     
38045     /**
38046      * @cfg {String} parent 
38047      * Name of parent element which it get xtype added to..
38048      */
38049     parent: false,
38050     
38051     /**
38052      * @cfg {String} order
38053      * Used to set the order in which elements are created (usefull for multiple tabs)
38054      */
38055     
38056     order : false,
38057     /**
38058      * @cfg {String} name
38059      * String to display while loading.
38060      */
38061     name : false,
38062     /**
38063      * @cfg {String} region
38064      * Region to render component to (defaults to center)
38065      */
38066     region : 'center',
38067     
38068     /**
38069      * @cfg {Array} items
38070      * A single item array - the first element is the root of the tree..
38071      * It's done this way to stay compatible with the Xtype system...
38072      */
38073     items : false,
38074     
38075     /**
38076      * @property _tree
38077      * The method that retuns the tree of parts that make up this compoennt 
38078      * @type {function}
38079      */
38080     _tree  : false,
38081     
38082      /**
38083      * render
38084      * render element to dom or tree
38085      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
38086      */
38087     
38088     render : function(el)
38089     {
38090         
38091         el = el || false;
38092         var hp = this.parent ? 1 : 0;
38093         
38094         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
38095             // if parent is a '#.....' string, then let's use that..
38096             var ename = this.parent.substr(1)
38097             this.parent = false;
38098             el = Roo.get(ename);
38099             if (!el) {
38100                 Roo.log("Warning - element can not be found :#" + ename );
38101                 return;
38102             }
38103         }
38104         
38105         
38106         if (!this.parent) {
38107             
38108             el = el ? Roo.get(el) : false;      
38109             
38110             // it's a top level one..
38111             this.parent =  {
38112                 el : new Roo.BorderLayout(el || document.body, {
38113                 
38114                      center: {
38115                          titlebar: false,
38116                          autoScroll:false,
38117                          closeOnTab: true,
38118                          tabPosition: 'top',
38119                           //resizeTabs: true,
38120                          alwaysShowTabs: el && hp? false :  true,
38121                          hideTabs: el || !hp ? true :  false,
38122                          minTabWidth: 140
38123                      }
38124                  })
38125             }
38126         }
38127         
38128                 
38129                 // The 'tree' method is  '_tree now' 
38130             
38131         var tree = this._tree ? this._tree() : this.tree();
38132         tree.region = tree.region || this.region;
38133         this.el = this.parent.el.addxtype(tree);
38134         this.fireEvent('built', this);
38135         
38136         this.panel = this.el;
38137         this.layout = this.panel.layout;
38138                 this.parentLayout = this.parent.layout  || false;  
38139          
38140     }
38141     
38142 });
38143
38144 Roo.apply(Roo.XComponent, {
38145     
38146     /**
38147      * @property  buildCompleted
38148      * True when the builder has completed building the interface.
38149      * @type Boolean
38150      */
38151     buildCompleted : false,
38152      
38153     /**
38154      * @property  topModule
38155      * the upper most module - uses document.element as it's constructor.
38156      * @type Object
38157      */
38158      
38159     topModule  : false,
38160       
38161     /**
38162      * @property  modules
38163      * array of modules to be created by registration system.
38164      * @type {Array} of Roo.XComponent
38165      */
38166     
38167     modules : [],
38168     /**
38169      * @property  elmodules
38170      * array of modules to be created by which use #ID 
38171      * @type {Array} of Roo.XComponent
38172      */
38173      
38174     elmodules : [],
38175
38176     
38177     /**
38178      * Register components to be built later.
38179      *
38180      * This solves the following issues
38181      * - Building is not done on page load, but after an authentication process has occured.
38182      * - Interface elements are registered on page load
38183      * - Parent Interface elements may not be loaded before child, so this handles that..
38184      * 
38185      *
38186      * example:
38187      * 
38188      * MyApp.register({
38189           order : '000001',
38190           module : 'Pman.Tab.projectMgr',
38191           region : 'center',
38192           parent : 'Pman.layout',
38193           disabled : false,  // or use a function..
38194         })
38195      
38196      * * @param {Object} details about module
38197      */
38198     register : function(obj) {
38199                 
38200                 Roo.XComponent.event.fireEvent('register', obj);
38201                 switch(typeof(obj.disabled) ) {
38202                         
38203                         case 'undefined':
38204                                 break;
38205                         
38206                         case 'function':
38207                                 if ( obj.disabled() ) {
38208                                         return;
38209                                 }
38210                                 break;
38211                         default:
38212                                 if (obj.disabled) {
38213                                         return;
38214                                 }
38215                                 break;
38216                 }
38217                 
38218         this.modules.push(obj);
38219          
38220     },
38221     /**
38222      * convert a string to an object..
38223      * eg. 'AAA.BBB' -> finds AAA.BBB
38224
38225      */
38226     
38227     toObject : function(str)
38228     {
38229         if (!str || typeof(str) == 'object') {
38230             return str;
38231         }
38232         if (str.substring(0,1) == '#') {
38233             return str;
38234         }
38235
38236         var ar = str.split('.');
38237         var rt, o;
38238         rt = ar.shift();
38239             /** eval:var:o */
38240         try {
38241             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
38242         } catch (e) {
38243             throw "Module not found : " + str;
38244         }
38245         
38246         if (o === false) {
38247             throw "Module not found : " + str;
38248         }
38249         Roo.each(ar, function(e) {
38250             if (typeof(o[e]) == 'undefined') {
38251                 throw "Module not found : " + str;
38252             }
38253             o = o[e];
38254         });
38255         
38256         return o;
38257         
38258     },
38259     
38260     
38261     /**
38262      * move modules into their correct place in the tree..
38263      * 
38264      */
38265     preBuild : function ()
38266     {
38267         var _t = this;
38268         Roo.each(this.modules , function (obj)
38269         {
38270             var opar = obj.parent;
38271             try { 
38272                 obj.parent = this.toObject(opar);
38273             } catch(e) {
38274                 Roo.log("parent:toObject failed: " + e.toString());
38275                 return;
38276             }
38277             
38278             if (!obj.parent) {
38279                                 Roo.debug && Roo.log("GOT top level module");
38280                                 Roo.debug && Roo.log(obj);
38281                                 obj.modules = new Roo.util.MixedCollection(false, 
38282                     function(o) { return o.order + '' }
38283                 );
38284                 this.topModule = obj;
38285                 return;
38286             }
38287                         // parent is a string (usually a dom element name..)
38288             if (typeof(obj.parent) == 'string') {
38289                 this.elmodules.push(obj);
38290                 return;
38291             }
38292             if (obj.parent.constructor != Roo.XComponent) {
38293                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
38294             }
38295             if (!obj.parent.modules) {
38296                 obj.parent.modules = new Roo.util.MixedCollection(false, 
38297                     function(o) { return o.order + '' }
38298                 );
38299             }
38300             
38301             obj.parent.modules.add(obj);
38302         }, this);
38303     },
38304     
38305      /**
38306      * make a list of modules to build.
38307      * @return {Array} list of modules. 
38308      */ 
38309     
38310     buildOrder : function()
38311     {
38312         var _this = this;
38313         var cmp = function(a,b) {   
38314             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
38315         };
38316         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
38317             throw "No top level modules to build";
38318         }
38319         
38320         // make a flat list in order of modules to build.
38321         var mods = this.topModule ? [ this.topModule ] : [];
38322                 
38323                 // elmodules (is a list of DOM based modules )
38324         Roo.each(this.elmodules,function(e) { mods.push(e) });
38325
38326         
38327         // add modules to their parents..
38328         var addMod = function(m) {
38329                         Roo.debug && Roo.log("build Order: add: " + m.name);
38330             
38331             mods.push(m);
38332             if (m.modules) {
38333                                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
38334                 m.modules.keySort('ASC',  cmp );
38335                                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
38336
38337                 m.modules.each(addMod);
38338             } else {
38339                                 Roo.debug && Roo.log("build Order: no child modules");
38340                         }
38341             // not sure if this is used any more..
38342             if (m.finalize) {
38343                 m.finalize.name = m.name + " (clean up) ";
38344                 mods.push(m.finalize);
38345             }
38346             
38347         }
38348         if (this.topModule) { 
38349             this.topModule.modules.keySort('ASC',  cmp );
38350             this.topModule.modules.each(addMod);
38351         }
38352         return mods;
38353     },
38354     
38355      /**
38356      * Build the registered modules.
38357      * @param {Object} parent element.
38358      * @param {Function} optional method to call after module has been added.
38359      * 
38360      */ 
38361    
38362     build : function() 
38363     {
38364         
38365         this.preBuild();
38366         var mods = this.buildOrder();
38367       
38368         //this.allmods = mods;
38369         //Roo.debug && Roo.log(mods);
38370         //return;
38371         if (!mods.length) { // should not happen
38372             throw "NO modules!!!";
38373         }
38374         
38375         
38376         var msg = "Building Interface...";
38377         // flash it up as modal - so we store the mask!?
38378         Roo.MessageBox.show({ title: 'loading' });
38379         Roo.MessageBox.show({
38380            title: "Please wait...",
38381            msg: msg,
38382            width:450,
38383            progress:true,
38384            closable:false,
38385            modal: false
38386           
38387         });
38388         var total = mods.length;
38389         
38390         var _this = this;
38391         var progressRun = function() {
38392             if (!mods.length) {
38393                 Roo.debug && Roo.log('hide?');
38394                 Roo.MessageBox.hide();
38395                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
38396                 
38397                 // THE END...
38398                 return false;   
38399             }
38400             
38401             var m = mods.shift();
38402             
38403             
38404             Roo.debug && Roo.log(m);
38405             // not sure if this is supported any more.. - modules that are are just function
38406             if (typeof(m) == 'function') { 
38407                 m.call(this);
38408                 return progressRun.defer(10, _this);
38409             } 
38410             
38411             
38412             msg = "Building Interface " + (total  - mods.length) + 
38413                     " of " + total + 
38414                     (m.name ? (' - ' + m.name) : '');
38415                         Roo.debug && Roo.log(msg);
38416             Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
38417             
38418          
38419             // is the module disabled?
38420             var disabled = (typeof(m.disabled) == 'function') ?
38421                 m.disabled.call(m.module.disabled) : m.disabled;    
38422             
38423             
38424             if (disabled) {
38425                 return progressRun(); // we do not update the display!
38426             }
38427             
38428             // now build 
38429             
38430                         
38431                         
38432             m.render();
38433             // it's 10 on top level, and 1 on others??? why...
38434             return progressRun.defer(10, _this);
38435              
38436         }
38437         progressRun.defer(1, _this);
38438      
38439         
38440         
38441     },
38442         
38443         
38444         /**
38445          * Event Object.
38446          *
38447          *
38448          */
38449         event: false, 
38450     /**
38451          * wrapper for event.on - aliased later..  
38452          * Typically use to register a event handler for register:
38453          *
38454          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
38455          *
38456          */
38457     on : false
38458    
38459     
38460     
38461 });
38462
38463 Roo.XComponent.event = new Roo.util.Observable({
38464                 events : { 
38465                         /**
38466                          * @event register
38467                          * Fires when an Component is registered,
38468                          * set the disable property on the Component to stop registration.
38469                          * @param {Roo.XComponent} c the component being registerd.
38470                          * 
38471                          */
38472                         'register' : true,
38473                         /**
38474                          * @event buildcomplete
38475                          * Fires on the top level element when all elements have been built
38476                          * @param {Roo.XComponent} the top level component.
38477                          */
38478                         'buildcomplete' : true
38479                         
38480                 }
38481 });
38482
38483 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
38484  //<script type="text/javascript">
38485
38486
38487 /**
38488  * @class Roo.Login
38489  * @extends Roo.LayoutDialog
38490  * A generic Login Dialog..... - only one needed in theory!?!?
38491  *
38492  * Fires XComponent builder on success...
38493  * 
38494  * Sends 
38495  *    username,password, lang = for login actions.
38496  *    check = 1 for periodic checking that sesion is valid.
38497  *    passwordRequest = email request password
38498  *    logout = 1 = to logout
38499  * 
38500  * Affects: (this id="????" elements)
38501  *   loading  (removed) (used to indicate application is loading)
38502  *   loading-mask (hides) (used to hide application when it's building loading)
38503  *   
38504  * 
38505  * Usage: 
38506  *    
38507  * 
38508  * Myapp.login = Roo.Login({
38509      url: xxxx,
38510    
38511      realm : 'Myapp', 
38512      
38513      
38514      method : 'POST',
38515      
38516      
38517      * 
38518  })
38519  * 
38520  * 
38521  * 
38522  **/
38523  
38524 Roo.Login = function(cfg)
38525 {
38526     this.addEvents({
38527         'refreshed' : true
38528     });
38529     
38530     Roo.apply(this,cfg);
38531     
38532     Roo.onReady(function() {
38533         this.onLoad();
38534     }, this);
38535     // call parent..
38536     
38537    
38538     Roo.Login.superclass.constructor.call(this, this);
38539     //this.addxtype(this.items[0]);
38540     
38541     
38542 }
38543
38544
38545 Roo.extend(Roo.Login, Roo.LayoutDialog, {
38546     
38547     /**
38548      * @cfg {String} method
38549      * Method used to query for login details.
38550      */
38551     
38552     method : 'POST',
38553     /**
38554      * @cfg {String} url
38555      * URL to query login data. - eg. baseURL + '/Login.php'
38556      */
38557     url : '',
38558     
38559     /**
38560      * @property user
38561      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
38562      * @type {Object} 
38563      */
38564     user : false,
38565     /**
38566      * @property checkFails
38567      * Number of times we have attempted to get authentication check, and failed.
38568      * @type {Number} 
38569      */
38570     checkFails : 0,
38571       /**
38572      * @property intervalID
38573      * The window interval that does the constant login checking.
38574      * @type {Number} 
38575      */
38576     intervalID : 0,
38577     
38578     
38579     onLoad : function() // called on page load...
38580     {
38581         // load 
38582          
38583         if (Roo.get('loading')) { // clear any loading indicator..
38584             Roo.get('loading').remove();
38585         }
38586         
38587         //this.switchLang('en'); // set the language to english..
38588        
38589         this.check({
38590             success:  function(response, opts)  {  // check successfull...
38591             
38592                 var res = this.processResponse(response);
38593                 this.checkFails =0;
38594                 if (!res.success) { // error!
38595                     this.checkFails = 5;
38596                     //console.log('call failure');
38597                     return this.failure(response,opts);
38598                 }
38599                 
38600                 if (!res.data.id) { // id=0 == login failure.
38601                     return this.show();
38602                 }
38603                 
38604                               
38605                         //console.log(success);
38606                 this.fillAuth(res.data);   
38607                 this.checkFails =0;
38608                 Roo.XComponent.build();
38609             },
38610             failure : this.show
38611         });
38612         
38613     }, 
38614     
38615     
38616     check: function(cfg) // called every so often to refresh cookie etc..
38617     {
38618         if (cfg.again) { // could be undefined..
38619             this.checkFails++;
38620         } else {
38621             this.checkFails = 0;
38622         }
38623         var _this = this;
38624         if (this.sending) {
38625             if ( this.checkFails > 4) {
38626                 Roo.MessageBox.alert("Error",  
38627                     "Error getting authentication status. - try reloading, or wait a while", function() {
38628                         _this.sending = false;
38629                     }); 
38630                 return;
38631             }
38632             cfg.again = true;
38633             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
38634             return;
38635         }
38636         this.sending = true;
38637         
38638         Roo.Ajax.request({  
38639             url: this.url,
38640             params: {
38641                 getAuthUser: true
38642             },  
38643             method: this.method,
38644             success:  cfg.success || this.success,
38645             failure : cfg.failure || this.failure,
38646             scope : this,
38647             callCfg : cfg
38648               
38649         });  
38650     }, 
38651     
38652     
38653     logout: function()
38654     {
38655         window.onbeforeunload = function() { }; // false does not work for IE..
38656         this.user = false;
38657         var _this = this;
38658         
38659         Roo.Ajax.request({  
38660             url: this.url,
38661             params: {
38662                 logout: 1
38663             },  
38664             method: 'GET',
38665             failure : function() {
38666                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
38667                     document.location = document.location.toString() + '?ts=' + Math.random();
38668                 });
38669                 
38670             },
38671             success : function() {
38672                 _this.user = false;
38673                 this.checkFails =0;
38674                 // fixme..
38675                 document.location = document.location.toString() + '?ts=' + Math.random();
38676             }
38677               
38678               
38679         }); 
38680     },
38681     
38682     processResponse : function (response)
38683     {
38684         var res = '';
38685         try {
38686             res = Roo.decode(response.responseText);
38687             // oops...
38688             if (typeof(res) != 'object') {
38689                 res = { success : false, errorMsg : res, errors : true };
38690             }
38691             if (typeof(res.success) == 'undefined') {
38692                 res.success = false;
38693             }
38694             
38695         } catch(e) {
38696             res = { success : false,  errorMsg : response.responseText, errors : true };
38697         }
38698         return res;
38699     },
38700     
38701     success : function(response, opts)  // check successfull...
38702     {  
38703         this.sending = false;
38704         var res = this.processResponse(response);
38705         if (!res.success) {
38706             return this.failure(response, opts);
38707         }
38708         if (!res.data || !res.data.id) {
38709             return this.failure(response,opts);
38710         }
38711         //console.log(res);
38712         this.fillAuth(res.data);
38713         
38714         this.checkFails =0;
38715         
38716     },
38717     
38718     
38719     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
38720     {
38721         this.authUser = -1;
38722         this.sending = false;
38723         var res = this.processResponse(response);
38724         //console.log(res);
38725         if ( this.checkFails > 2) {
38726         
38727             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
38728                 "Error getting authentication status. - try reloading"); 
38729             return;
38730         }
38731         opts.callCfg.again = true;
38732         this.check.defer(1000, this, [ opts.callCfg ]);
38733         return;  
38734     },
38735     
38736     
38737     
38738     fillAuth: function(au) {
38739         this.startAuthCheck();
38740         this.authUserId = au.id;
38741         this.authUser = au;
38742         this.lastChecked = new Date();
38743         this.fireEvent('refreshed', au);
38744         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
38745         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
38746         au.lang = au.lang || 'en';
38747         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
38748         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
38749         this.switchLang(au.lang );
38750         
38751      
38752         // open system... - -on setyp..
38753         if (this.authUserId  < 0) {
38754             Roo.MessageBox.alert("Warning", 
38755                 "This is an open system - please set up a admin user with a password.");  
38756         }
38757          
38758         //Pman.onload(); // which should do nothing if it's a re-auth result...
38759         
38760              
38761     },
38762     
38763     startAuthCheck : function() // starter for timeout checking..
38764     {
38765         if (this.intervalID) { // timer already in place...
38766             return false;
38767         }
38768         var _this = this;
38769         this.intervalID =  window.setInterval(function() {
38770               _this.check(false);
38771             }, 120000); // every 120 secs = 2mins..
38772         
38773         
38774     },
38775          
38776     
38777     switchLang : function (lang) 
38778     {
38779         _T = typeof(_T) == 'undefined' ? false : _T;
38780           if (!_T || !lang.length) {
38781             return;
38782         }
38783         
38784         if (!_T && lang != 'en') {
38785             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
38786             return;
38787         }
38788         
38789         if (typeof(_T.en) == 'undefined') {
38790             _T.en = {};
38791             Roo.apply(_T.en, _T);
38792         }
38793         
38794         if (typeof(_T[lang]) == 'undefined') {
38795             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
38796             return;
38797         }
38798         
38799         
38800         Roo.apply(_T, _T[lang]);
38801         // just need to set the text values for everything...
38802         var _this = this;
38803         /* this will not work ...
38804         if (this.form) { 
38805             
38806                
38807             function formLabel(name, val) {
38808                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
38809             }
38810             
38811             formLabel('password', "Password"+':');
38812             formLabel('username', "Email Address"+':');
38813             formLabel('lang', "Language"+':');
38814             this.dialog.setTitle("Login");
38815             this.dialog.buttons[0].setText("Forgot Password");
38816             this.dialog.buttons[1].setText("Login");
38817         }
38818         */
38819         
38820         
38821     },
38822     
38823     
38824     title: "Login",
38825     modal: true,
38826     width:  350,
38827     //height: 230,
38828     height: 180,
38829     shadow: true,
38830     minWidth:200,
38831     minHeight:180,
38832     //proxyDrag: true,
38833     closable: false,
38834     draggable: false,
38835     collapsible: false,
38836     resizable: false,
38837     center: {  // needed??
38838         autoScroll:false,
38839         titlebar: false,
38840        // tabPosition: 'top',
38841         hideTabs: true,
38842         closeOnTab: true,
38843         alwaysShowTabs: false
38844     } ,
38845     listeners : {
38846         
38847         show  : function(dlg)
38848         {
38849             //console.log(this);
38850             this.form = this.layout.getRegion('center').activePanel.form;
38851             this.form.dialog = dlg;
38852             this.buttons[0].form = this.form;
38853             this.buttons[0].dialog = dlg;
38854             this.buttons[1].form = this.form;
38855             this.buttons[1].dialog = dlg;
38856            
38857            //this.resizeToLogo.defer(1000,this);
38858             // this is all related to resizing for logos..
38859             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
38860            //// if (!sz) {
38861              //   this.resizeToLogo.defer(1000,this);
38862              //   return;
38863            // }
38864             //var w = Ext.lib.Dom.getViewWidth() - 100;
38865             //var h = Ext.lib.Dom.getViewHeight() - 100;
38866             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
38867             //this.center();
38868             if (this.disabled) {
38869                 this.hide();
38870                 return;
38871             }
38872             
38873             if (this.user.id < 0) { // used for inital setup situations.
38874                 return;
38875             }
38876             
38877             if (this.intervalID) {
38878                 // remove the timer
38879                 window.clearInterval(this.intervalID);
38880                 this.intervalID = false;
38881             }
38882             
38883             
38884             if (Roo.get('loading')) {
38885                 Roo.get('loading').remove();
38886             }
38887             if (Roo.get('loading-mask')) {
38888                 Roo.get('loading-mask').hide();
38889             }
38890             
38891             //incomming._node = tnode;
38892             this.form.reset();
38893             //this.dialog.modal = !modal;
38894             //this.dialog.show();
38895             this.el.unmask(); 
38896             
38897             
38898             this.form.setValues({
38899                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
38900                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
38901             });
38902             
38903             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
38904             if (this.form.findField('username').getValue().length > 0 ){
38905                 this.form.findField('password').focus();
38906             } else {
38907                this.form.findField('username').focus();
38908             }
38909     
38910         }
38911     },
38912     items : [
38913          {
38914        
38915             xtype : 'ContentPanel',
38916             xns : Roo,
38917             region: 'center',
38918             fitToFrame : true,
38919             
38920             items : [
38921     
38922                 {
38923                
38924                     xtype : 'Form',
38925                     xns : Roo.form,
38926                     labelWidth: 100,
38927                     style : 'margin: 10px;',
38928                     
38929                     listeners : {
38930                         actionfailed : function(f, act) {
38931                             // form can return { errors: .... }
38932                                 
38933                             //act.result.errors // invalid form element list...
38934                             //act.result.errorMsg// invalid form element list...
38935                             
38936                             this.dialog.el.unmask();
38937                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
38938                                         "Login failed - communication error - try again.");
38939                                       
38940                         },
38941                         actioncomplete: function(re, act) {
38942                              
38943                             Roo.state.Manager.set(
38944                                 this.dialog.realm + '.username',  
38945                                     this.findField('username').getValue()
38946                             );
38947                             Roo.state.Manager.set(
38948                                 this.dialog.realm + '.lang',  
38949                                 this.findField('lang').getValue() 
38950                             );
38951                             
38952                             this.dialog.fillAuth(act.result.data);
38953                               
38954                             this.dialog.hide();
38955                             
38956                             if (Roo.get('loading-mask')) {
38957                                 Roo.get('loading-mask').show();
38958                             }
38959                             Roo.XComponent.build();
38960                             
38961                              
38962                             
38963                         }
38964                     },
38965                     items : [
38966                         {
38967                             xtype : 'TextField',
38968                             xns : Roo.form,
38969                             fieldLabel: "Email Address",
38970                             name: 'username',
38971                             width:200,
38972                             autoCreate : {tag: "input", type: "text", size: "20"}
38973                         },
38974                         {
38975                             xtype : 'TextField',
38976                             xns : Roo.form,
38977                             fieldLabel: "Password",
38978                             inputType: 'password',
38979                             name: 'password',
38980                             width:200,
38981                             autoCreate : {tag: "input", type: "text", size: "20"},
38982                             listeners : {
38983                                 specialkey : function(e,ev) {
38984                                     if (ev.keyCode == 13) {
38985                                         this.form.dialog.el.mask("Logging in");
38986                                         this.form.doAction('submit', {
38987                                             url: this.form.dialog.url,
38988                                             method: this.form.dialog.method
38989                                         });
38990                                     }
38991                                 }
38992                             }  
38993                         },
38994                         {
38995                             xtype : 'ComboBox',
38996                             xns : Roo.form,
38997                             fieldLabel: "Language",
38998                             name : 'langdisp',
38999                             store: {
39000                                 xtype : 'SimpleStore',
39001                                 fields: ['lang', 'ldisp'],
39002                                 data : [
39003                                     [ 'en', 'English' ],
39004                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
39005                                     [ 'zh_CN', '\u7C21\u4E2D' ]
39006                                 ]
39007                             },
39008                             
39009                             valueField : 'lang',
39010                             hiddenName:  'lang',
39011                             width: 200,
39012                             displayField:'ldisp',
39013                             typeAhead: false,
39014                             editable: false,
39015                             mode: 'local',
39016                             triggerAction: 'all',
39017                             emptyText:'Select a Language...',
39018                             selectOnFocus:true,
39019                             listeners : {
39020                                 select :  function(cb, rec, ix) {
39021                                     this.form.switchLang(rec.data.lang);
39022                                 }
39023                             }
39024                         
39025                         }
39026                     ]
39027                 }
39028                   
39029                 
39030             ]
39031         }
39032     ],
39033     buttons : [
39034         {
39035             xtype : 'Button',
39036             xns : 'Roo',
39037             text : "Forgot Password",
39038             listeners : {
39039                 click : function() {
39040                     //console.log(this);
39041                     var n = this.form.findField('username').getValue();
39042                     if (!n.length) {
39043                         Roo.MessageBox.alert("Error", "Fill in your email address");
39044                         return;
39045                     }
39046                     Roo.Ajax.request({
39047                         url: this.dialog.url,
39048                         params: {
39049                             passwordRequest: n
39050                         },
39051                         method: this.dialog.method,
39052                         success:  function(response, opts)  {  // check successfull...
39053                         
39054                             var res = this.dialog.processResponse(response);
39055                             if (!res.success) { // error!
39056                                Roo.MessageBox.alert("Error" ,
39057                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
39058                                return;
39059                             }
39060                             Roo.MessageBox.alert("Notice" ,
39061                                 "Please check you email for the Password Reset message");
39062                         },
39063                         failure : function() {
39064                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
39065                         }
39066                         
39067                     });
39068                 }
39069             }
39070         },
39071         {
39072             xtype : 'Button',
39073             xns : 'Roo',
39074             text : "Login",
39075             listeners : {
39076                 
39077                 click : function () {
39078                         
39079                     this.dialog.el.mask("Logging in");
39080                     this.form.doAction('submit', {
39081                             url: this.dialog.url,
39082                             method: this.dialog.method
39083                     });
39084                 }
39085             }
39086         }
39087     ]
39088   
39089   
39090 })
39091  
39092
39093
39094