roojs-ui-debug.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  * Defines the interface and base operation of items that that can be
30  * dragged or can be drop targets.  It was designed to be extended, overriding
31  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
32  * Up to three html elements can be associated with a DragDrop instance:
33  * <ul>
34  * <li>linked element: the element that is passed into the constructor.
35  * This is the element which defines the boundaries for interaction with
36  * other DragDrop objects.</li>
37  * <li>handle element(s): The drag operation only occurs if the element that
38  * was clicked matches a handle element.  By default this is the linked
39  * element, but there are times that you will want only a portion of the
40  * linked element to initiate the drag operation, and the setHandleElId()
41  * method provides a way to define this.</li>
42  * <li>drag element: this represents the element that would be moved along
43  * with the cursor during a drag operation.  By default, this is the linked
44  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
45  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
46  * </li>
47  * </ul>
48  * This class should not be instantiated until the onload event to ensure that
49  * the associated elements are available.
50  * The following would define a DragDrop obj that would interact with any
51  * other DragDrop obj in the "group1" group:
52  * <pre>
53  *  dd = new Roo.dd.DragDrop("div1", "group1");
54  * </pre>
55  * Since none of the event handlers have been implemented, nothing would
56  * actually happen if you were to run the code above.  Normally you would
57  * override this class or one of the default implementations, but you can
58  * also override the methods you want on an instance of the class...
59  * <pre>
60  *  dd.onDragDrop = function(e, id) {
61  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
62  *  }
63  * </pre>
64  * @constructor
65  * @param {String} id of the element that is linked to this instance
66  * @param {String} sGroup the group of related DragDrop objects
67  * @param {object} config an object containing configurable attributes
68  *                Valid properties for DragDrop:
69  *                    padding, isTarget, maintainOffset, primaryButtonOnly
70  */
71 Roo.dd.DragDrop = function(id, sGroup, config) {
72     if (id) {
73         this.init(id, sGroup, config);
74     }
75     
76 };
77
78 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
79
80     /**
81      * The id of the element associated with this object.  This is what we
82      * refer to as the "linked element" because the size and position of
83      * this element is used to determine when the drag and drop objects have
84      * interacted.
85      * @property id
86      * @type String
87      */
88     id: null,
89
90     /**
91      * Configuration attributes passed into the constructor
92      * @property config
93      * @type object
94      */
95     config: null,
96
97     /**
98      * The id of the element that will be dragged.  By default this is same
99      * as the linked element , but could be changed to another element. Ex:
100      * Roo.dd.DDProxy
101      * @property dragElId
102      * @type String
103      * @private
104      */
105     dragElId: null,
106
107     /**
108      * the id of the element that initiates the drag operation.  By default
109      * this is the linked element, but could be changed to be a child of this
110      * element.  This lets us do things like only starting the drag when the
111      * header element within the linked html element is clicked.
112      * @property handleElId
113      * @type String
114      * @private
115      */
116     handleElId: null,
117
118     /**
119      * An associative array of HTML tags that will be ignored if clicked.
120      * @property invalidHandleTypes
121      * @type {string: string}
122      */
123     invalidHandleTypes: null,
124
125     /**
126      * An associative array of ids for elements that will be ignored if clicked
127      * @property invalidHandleIds
128      * @type {string: string}
129      */
130     invalidHandleIds: null,
131
132     /**
133      * An indexted array of css class names for elements that will be ignored
134      * if clicked.
135      * @property invalidHandleClasses
136      * @type string[]
137      */
138     invalidHandleClasses: null,
139
140     /**
141      * The linked element's absolute X position at the time the drag was
142      * started
143      * @property startPageX
144      * @type int
145      * @private
146      */
147     startPageX: 0,
148
149     /**
150      * The linked element's absolute X position at the time the drag was
151      * started
152      * @property startPageY
153      * @type int
154      * @private
155      */
156     startPageY: 0,
157
158     /**
159      * The group defines a logical collection of DragDrop objects that are
160      * related.  Instances only get events when interacting with other
161      * DragDrop object in the same group.  This lets us define multiple
162      * groups using a single DragDrop subclass if we want.
163      * @property groups
164      * @type {string: string}
165      */
166     groups: null,
167
168     /**
169      * Individual drag/drop instances can be locked.  This will prevent
170      * onmousedown start drag.
171      * @property locked
172      * @type boolean
173      * @private
174      */
175     locked: false,
176
177     /**
178      * Lock this instance
179      * @method lock
180      */
181     lock: function() { this.locked = true; },
182
183     /**
184      * Unlock this instace
185      * @method unlock
186      */
187     unlock: function() { this.locked = false; },
188
189     /**
190      * By default, all insances can be a drop target.  This can be disabled by
191      * setting isTarget to false.
192      * @method isTarget
193      * @type boolean
194      */
195     isTarget: true,
196
197     /**
198      * The padding configured for this drag and drop object for calculating
199      * the drop zone intersection with this object.
200      * @method padding
201      * @type int[]
202      */
203     padding: null,
204
205     /**
206      * Cached reference to the linked element
207      * @property _domRef
208      * @private
209      */
210     _domRef: null,
211
212     /**
213      * Internal typeof flag
214      * @property __ygDragDrop
215      * @private
216      */
217     __ygDragDrop: true,
218
219     /**
220      * Set to true when horizontal contraints are applied
221      * @property constrainX
222      * @type boolean
223      * @private
224      */
225     constrainX: false,
226
227     /**
228      * Set to true when vertical contraints are applied
229      * @property constrainY
230      * @type boolean
231      * @private
232      */
233     constrainY: false,
234
235     /**
236      * The left constraint
237      * @property minX
238      * @type int
239      * @private
240      */
241     minX: 0,
242
243     /**
244      * The right constraint
245      * @property maxX
246      * @type int
247      * @private
248      */
249     maxX: 0,
250
251     /**
252      * The up constraint
253      * @property minY
254      * @type int
255      * @type int
256      * @private
257      */
258     minY: 0,
259
260     /**
261      * The down constraint
262      * @property maxY
263      * @type int
264      * @private
265      */
266     maxY: 0,
267
268     /**
269      * Maintain offsets when we resetconstraints.  Set to true when you want
270      * the position of the element relative to its parent to stay the same
271      * when the page changes
272      *
273      * @property maintainOffset
274      * @type boolean
275      */
276     maintainOffset: false,
277
278     /**
279      * Array of pixel locations the element will snap to if we specified a
280      * horizontal graduation/interval.  This array is generated automatically
281      * when you define a tick interval.
282      * @property xTicks
283      * @type int[]
284      */
285     xTicks: null,
286
287     /**
288      * Array of pixel locations the element will snap to if we specified a
289      * vertical graduation/interval.  This array is generated automatically
290      * when you define a tick interval.
291      * @property yTicks
292      * @type int[]
293      */
294     yTicks: null,
295
296     /**
297      * By default the drag and drop instance will only respond to the primary
298      * button click (left button for a right-handed mouse).  Set to true to
299      * allow drag and drop to start with any mouse click that is propogated
300      * by the browser
301      * @property primaryButtonOnly
302      * @type boolean
303      */
304     primaryButtonOnly: true,
305
306     /**
307      * The availabe property is false until the linked dom element is accessible.
308      * @property available
309      * @type boolean
310      */
311     available: false,
312
313     /**
314      * By default, drags can only be initiated if the mousedown occurs in the
315      * region the linked element is.  This is done in part to work around a
316      * bug in some browsers that mis-report the mousedown if the previous
317      * mouseup happened outside of the window.  This property is set to true
318      * if outer handles are defined.
319      *
320      * @property hasOuterHandles
321      * @type boolean
322      * @default false
323      */
324     hasOuterHandles: false,
325
326     /**
327      * Code that executes immediately before the startDrag event
328      * @method b4StartDrag
329      * @private
330      */
331     b4StartDrag: function(x, y) { },
332
333     /**
334      * Abstract method called after a drag/drop object is clicked
335      * and the drag or mousedown time thresholds have beeen met.
336      * @method startDrag
337      * @param {int} X click location
338      * @param {int} Y click location
339      */
340     startDrag: function(x, y) { /* override this */ },
341
342     /**
343      * Code that executes immediately before the onDrag event
344      * @method b4Drag
345      * @private
346      */
347     b4Drag: function(e) { },
348
349     /**
350      * Abstract method called during the onMouseMove event while dragging an
351      * object.
352      * @method onDrag
353      * @param {Event} e the mousemove event
354      */
355     onDrag: function(e) { /* override this */ },
356
357     /**
358      * Abstract method called when this element fist begins hovering over
359      * another DragDrop obj
360      * @method onDragEnter
361      * @param {Event} e the mousemove event
362      * @param {String|DragDrop[]} id In POINT mode, the element
363      * id this is hovering over.  In INTERSECT mode, an array of one or more
364      * dragdrop items being hovered over.
365      */
366     onDragEnter: function(e, id) { /* override this */ },
367
368     /**
369      * Code that executes immediately before the onDragOver event
370      * @method b4DragOver
371      * @private
372      */
373     b4DragOver: function(e) { },
374
375     /**
376      * Abstract method called when this element is hovering over another
377      * DragDrop obj
378      * @method onDragOver
379      * @param {Event} e the mousemove event
380      * @param {String|DragDrop[]} id In POINT mode, the element
381      * id this is hovering over.  In INTERSECT mode, an array of dd items
382      * being hovered over.
383      */
384     onDragOver: function(e, id) { /* override this */ },
385
386     /**
387      * Code that executes immediately before the onDragOut event
388      * @method b4DragOut
389      * @private
390      */
391     b4DragOut: function(e) { },
392
393     /**
394      * Abstract method called when we are no longer hovering over an element
395      * @method onDragOut
396      * @param {Event} e the mousemove event
397      * @param {String|DragDrop[]} id In POINT mode, the element
398      * id this was hovering over.  In INTERSECT mode, an array of dd items
399      * that the mouse is no longer over.
400      */
401     onDragOut: function(e, id) { /* override this */ },
402
403     /**
404      * Code that executes immediately before the onDragDrop event
405      * @method b4DragDrop
406      * @private
407      */
408     b4DragDrop: function(e) { },
409
410     /**
411      * Abstract method called when this item is dropped on another DragDrop
412      * obj
413      * @method onDragDrop
414      * @param {Event} e the mouseup event
415      * @param {String|DragDrop[]} id In POINT mode, the element
416      * id this was dropped on.  In INTERSECT mode, an array of dd items this
417      * was dropped on.
418      */
419     onDragDrop: function(e, id) { /* override this */ },
420
421     /**
422      * Abstract method called when this item is dropped on an area with no
423      * drop target
424      * @method onInvalidDrop
425      * @param {Event} e the mouseup event
426      */
427     onInvalidDrop: function(e) { /* override this */ },
428
429     /**
430      * Code that executes immediately before the endDrag event
431      * @method b4EndDrag
432      * @private
433      */
434     b4EndDrag: function(e) { },
435
436     /**
437      * Fired when we are done dragging the object
438      * @method endDrag
439      * @param {Event} e the mouseup event
440      */
441     endDrag: function(e) { /* override this */ },
442
443     /**
444      * Code executed immediately before the onMouseDown event
445      * @method b4MouseDown
446      * @param {Event} e the mousedown event
447      * @private
448      */
449     b4MouseDown: function(e) {  },
450
451     /**
452      * Event handler that fires when a drag/drop obj gets a mousedown
453      * @method onMouseDown
454      * @param {Event} e the mousedown event
455      */
456     onMouseDown: function(e) { /* override this */ },
457
458     /**
459      * Event handler that fires when a drag/drop obj gets a mouseup
460      * @method onMouseUp
461      * @param {Event} e the mouseup event
462      */
463     onMouseUp: function(e) { /* override this */ },
464
465     /**
466      * Override the onAvailable method to do what is needed after the initial
467      * position was determined.
468      * @method onAvailable
469      */
470     onAvailable: function () {
471     },
472
473     /*
474      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
475      * @type Object
476      */
477     defaultPadding : {left:0, right:0, top:0, bottom:0},
478
479     /*
480      * Initializes the drag drop object's constraints to restrict movement to a certain element.
481  *
482  * Usage:
483  <pre><code>
484  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
485                 { dragElId: "existingProxyDiv" });
486  dd.startDrag = function(){
487      this.constrainTo("parent-id");
488  };
489  </code></pre>
490  * Or you can initalize it using the {@link Roo.Element} object:
491  <pre><code>
492  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
493      startDrag : function(){
494          this.constrainTo("parent-id");
495      }
496  });
497  </code></pre>
498      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
499      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
500      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
501      * an object containing the sides to pad. For example: {right:10, bottom:10}
502      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
503      */
504     constrainTo : function(constrainTo, pad, inContent){
505         if(typeof pad == "number"){
506             pad = {left: pad, right:pad, top:pad, bottom:pad};
507         }
508         pad = pad || this.defaultPadding;
509         var b = Roo.get(this.getEl()).getBox();
510         var ce = Roo.get(constrainTo);
511         var s = ce.getScroll();
512         var c, cd = ce.dom;
513         if(cd == document.body){
514             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
515         }else{
516             xy = ce.getXY();
517             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
518         }
519
520
521         var topSpace = b.y - c.y;
522         var leftSpace = b.x - c.x;
523
524         this.resetConstraints();
525         this.setXConstraint(leftSpace - (pad.left||0), // left
526                 c.width - leftSpace - b.width - (pad.right||0) //right
527         );
528         this.setYConstraint(topSpace - (pad.top||0), //top
529                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
530         );
531     },
532
533     /**
534      * Returns a reference to the linked element
535      * @method getEl
536      * @return {HTMLElement} the html element
537      */
538     getEl: function() {
539         if (!this._domRef) {
540             this._domRef = Roo.getDom(this.id);
541         }
542
543         return this._domRef;
544     },
545
546     /**
547      * Returns a reference to the actual element to drag.  By default this is
548      * the same as the html element, but it can be assigned to another
549      * element. An example of this can be found in Roo.dd.DDProxy
550      * @method getDragEl
551      * @return {HTMLElement} the html element
552      */
553     getDragEl: function() {
554         return Roo.getDom(this.dragElId);
555     },
556
557     /**
558      * Sets up the DragDrop object.  Must be called in the constructor of any
559      * Roo.dd.DragDrop subclass
560      * @method init
561      * @param id the id of the linked element
562      * @param {String} sGroup the group of related items
563      * @param {object} config configuration attributes
564      */
565     init: function(id, sGroup, config) {
566         this.initTarget(id, sGroup, config);
567         Event.on(this.id, "mousedown", this.handleMouseDown, this);
568         // Event.on(this.id, "selectstart", Event.preventDefault);
569     },
570
571     /**
572      * Initializes Targeting functionality only... the object does not
573      * get a mousedown handler.
574      * @method initTarget
575      * @param id the id of the linked element
576      * @param {String} sGroup the group of related items
577      * @param {object} config configuration attributes
578      */
579     initTarget: function(id, sGroup, config) {
580
581         // configuration attributes
582         this.config = config || {};
583
584         // create a local reference to the drag and drop manager
585         this.DDM = Roo.dd.DDM;
586         // initialize the groups array
587         this.groups = {};
588
589         // assume that we have an element reference instead of an id if the
590         // parameter is not a string
591         if (typeof id !== "string") {
592             id = Roo.id(id);
593         }
594
595         // set the id
596         this.id = id;
597
598         // add to an interaction group
599         this.addToGroup((sGroup) ? sGroup : "default");
600
601         // We don't want to register this as the handle with the manager
602         // so we just set the id rather than calling the setter.
603         this.handleElId = id;
604
605         // the linked element is the element that gets dragged by default
606         this.setDragElId(id);
607
608         // by default, clicked anchors will not start drag operations.
609         this.invalidHandleTypes = { A: "A" };
610         this.invalidHandleIds = {};
611         this.invalidHandleClasses = [];
612
613         this.applyConfig();
614
615         this.handleOnAvailable();
616     },
617
618     /**
619      * Applies the configuration parameters that were passed into the constructor.
620      * This is supposed to happen at each level through the inheritance chain.  So
621      * a DDProxy implentation will execute apply config on DDProxy, DD, and
622      * DragDrop in order to get all of the parameters that are available in
623      * each object.
624      * @method applyConfig
625      */
626     applyConfig: function() {
627
628         // configurable properties:
629         //    padding, isTarget, maintainOffset, primaryButtonOnly
630         this.padding           = this.config.padding || [0, 0, 0, 0];
631         this.isTarget          = (this.config.isTarget !== false);
632         this.maintainOffset    = (this.config.maintainOffset);
633         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
634
635     },
636
637     /**
638      * Executed when the linked element is available
639      * @method handleOnAvailable
640      * @private
641      */
642     handleOnAvailable: function() {
643         this.available = true;
644         this.resetConstraints();
645         this.onAvailable();
646     },
647
648      /**
649      * Configures the padding for the target zone in px.  Effectively expands
650      * (or reduces) the virtual object size for targeting calculations.
651      * Supports css-style shorthand; if only one parameter is passed, all sides
652      * will have that padding, and if only two are passed, the top and bottom
653      * will have the first param, the left and right the second.
654      * @method setPadding
655      * @param {int} iTop    Top pad
656      * @param {int} iRight  Right pad
657      * @param {int} iBot    Bot pad
658      * @param {int} iLeft   Left pad
659      */
660     setPadding: function(iTop, iRight, iBot, iLeft) {
661         // this.padding = [iLeft, iRight, iTop, iBot];
662         if (!iRight && 0 !== iRight) {
663             this.padding = [iTop, iTop, iTop, iTop];
664         } else if (!iBot && 0 !== iBot) {
665             this.padding = [iTop, iRight, iTop, iRight];
666         } else {
667             this.padding = [iTop, iRight, iBot, iLeft];
668         }
669     },
670
671     /**
672      * Stores the initial placement of the linked element.
673      * @method setInitialPosition
674      * @param {int} diffX   the X offset, default 0
675      * @param {int} diffY   the Y offset, default 0
676      */
677     setInitPosition: function(diffX, diffY) {
678         var el = this.getEl();
679
680         if (!this.DDM.verifyEl(el)) {
681             return;
682         }
683
684         var dx = diffX || 0;
685         var dy = diffY || 0;
686
687         var p = Dom.getXY( el );
688
689         this.initPageX = p[0] - dx;
690         this.initPageY = p[1] - dy;
691
692         this.lastPageX = p[0];
693         this.lastPageY = p[1];
694
695
696         this.setStartPosition(p);
697     },
698
699     /**
700      * Sets the start position of the element.  This is set when the obj
701      * is initialized, the reset when a drag is started.
702      * @method setStartPosition
703      * @param pos current position (from previous lookup)
704      * @private
705      */
706     setStartPosition: function(pos) {
707         var p = pos || Dom.getXY( this.getEl() );
708         this.deltaSetXY = null;
709
710         this.startPageX = p[0];
711         this.startPageY = p[1];
712     },
713
714     /**
715      * Add this instance to a group of related drag/drop objects.  All
716      * instances belong to at least one group, and can belong to as many
717      * groups as needed.
718      * @method addToGroup
719      * @param sGroup {string} the name of the group
720      */
721     addToGroup: function(sGroup) {
722         this.groups[sGroup] = true;
723         this.DDM.regDragDrop(this, sGroup);
724     },
725
726     /**
727      * Remove's this instance from the supplied interaction group
728      * @method removeFromGroup
729      * @param {string}  sGroup  The group to drop
730      */
731     removeFromGroup: function(sGroup) {
732         if (this.groups[sGroup]) {
733             delete this.groups[sGroup];
734         }
735
736         this.DDM.removeDDFromGroup(this, sGroup);
737     },
738
739     /**
740      * Allows you to specify that an element other than the linked element
741      * will be moved with the cursor during a drag
742      * @method setDragElId
743      * @param id {string} the id of the element that will be used to initiate the drag
744      */
745     setDragElId: function(id) {
746         this.dragElId = id;
747     },
748
749     /**
750      * Allows you to specify a child of the linked element that should be
751      * used to initiate the drag operation.  An example of this would be if
752      * you have a content div with text and links.  Clicking anywhere in the
753      * content area would normally start the drag operation.  Use this method
754      * to specify that an element inside of the content div is the element
755      * that starts the drag operation.
756      * @method setHandleElId
757      * @param id {string} the id of the element that will be used to
758      * initiate the drag.
759      */
760     setHandleElId: function(id) {
761         if (typeof id !== "string") {
762             id = Roo.id(id);
763         }
764         this.handleElId = id;
765         this.DDM.regHandle(this.id, id);
766     },
767
768     /**
769      * Allows you to set an element outside of the linked element as a drag
770      * handle
771      * @method setOuterHandleElId
772      * @param id the id of the element that will be used to initiate the drag
773      */
774     setOuterHandleElId: function(id) {
775         if (typeof id !== "string") {
776             id = Roo.id(id);
777         }
778         Event.on(id, "mousedown",
779                 this.handleMouseDown, this);
780         this.setHandleElId(id);
781
782         this.hasOuterHandles = true;
783     },
784
785     /**
786      * Remove all drag and drop hooks for this element
787      * @method unreg
788      */
789     unreg: function() {
790         Event.un(this.id, "mousedown",
791                 this.handleMouseDown);
792         this._domRef = null;
793         this.DDM._remove(this);
794     },
795
796     destroy : function(){
797         this.unreg();
798     },
799
800     /**
801      * Returns true if this instance is locked, or the drag drop mgr is locked
802      * (meaning that all drag/drop is disabled on the page.)
803      * @method isLocked
804      * @return {boolean} true if this obj or all drag/drop is locked, else
805      * false
806      */
807     isLocked: function() {
808         return (this.DDM.isLocked() || this.locked);
809     },
810
811     /**
812      * Fired when this object is clicked
813      * @method handleMouseDown
814      * @param {Event} e
815      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
816      * @private
817      */
818     handleMouseDown: function(e, oDD){
819         if (this.primaryButtonOnly && e.button != 0) {
820             return;
821         }
822
823         if (this.isLocked()) {
824             return;
825         }
826
827         this.DDM.refreshCache(this.groups);
828
829         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
830         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
831         } else {
832             if (this.clickValidator(e)) {
833
834                 // set the initial element position
835                 this.setStartPosition();
836
837
838                 this.b4MouseDown(e);
839                 this.onMouseDown(e);
840
841                 this.DDM.handleMouseDown(e, this);
842
843                 this.DDM.stopEvent(e);
844             } else {
845
846
847             }
848         }
849     },
850
851     clickValidator: function(e) {
852         var target = e.getTarget();
853         return ( this.isValidHandleChild(target) &&
854                     (this.id == this.handleElId ||
855                         this.DDM.handleWasClicked(target, this.id)) );
856     },
857
858     /**
859      * Allows you to specify a tag name that should not start a drag operation
860      * when clicked.  This is designed to facilitate embedding links within a
861      * drag handle that do something other than start the drag.
862      * @method addInvalidHandleType
863      * @param {string} tagName the type of element to exclude
864      */
865     addInvalidHandleType: function(tagName) {
866         var type = tagName.toUpperCase();
867         this.invalidHandleTypes[type] = type;
868     },
869
870     /**
871      * Lets you to specify an element id for a child of a drag handle
872      * that should not initiate a drag
873      * @method addInvalidHandleId
874      * @param {string} id the element id of the element you wish to ignore
875      */
876     addInvalidHandleId: function(id) {
877         if (typeof id !== "string") {
878             id = Roo.id(id);
879         }
880         this.invalidHandleIds[id] = id;
881     },
882
883     /**
884      * Lets you specify a css class of elements that will not initiate a drag
885      * @method addInvalidHandleClass
886      * @param {string} cssClass the class of the elements you wish to ignore
887      */
888     addInvalidHandleClass: function(cssClass) {
889         this.invalidHandleClasses.push(cssClass);
890     },
891
892     /**
893      * Unsets an excluded tag name set by addInvalidHandleType
894      * @method removeInvalidHandleType
895      * @param {string} tagName the type of element to unexclude
896      */
897     removeInvalidHandleType: function(tagName) {
898         var type = tagName.toUpperCase();
899         // this.invalidHandleTypes[type] = null;
900         delete this.invalidHandleTypes[type];
901     },
902
903     /**
904      * Unsets an invalid handle id
905      * @method removeInvalidHandleId
906      * @param {string} id the id of the element to re-enable
907      */
908     removeInvalidHandleId: function(id) {
909         if (typeof id !== "string") {
910             id = Roo.id(id);
911         }
912         delete this.invalidHandleIds[id];
913     },
914
915     /**
916      * Unsets an invalid css class
917      * @method removeInvalidHandleClass
918      * @param {string} cssClass the class of the element(s) you wish to
919      * re-enable
920      */
921     removeInvalidHandleClass: function(cssClass) {
922         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
923             if (this.invalidHandleClasses[i] == cssClass) {
924                 delete this.invalidHandleClasses[i];
925             }
926         }
927     },
928
929     /**
930      * Checks the tag exclusion list to see if this click should be ignored
931      * @method isValidHandleChild
932      * @param {HTMLElement} node the HTMLElement to evaluate
933      * @return {boolean} true if this is a valid tag type, false if not
934      */
935     isValidHandleChild: function(node) {
936
937         var valid = true;
938         // var n = (node.nodeName == "#text") ? node.parentNode : node;
939         var nodeName;
940         try {
941             nodeName = node.nodeName.toUpperCase();
942         } catch(e) {
943             nodeName = node.nodeName;
944         }
945         valid = valid && !this.invalidHandleTypes[nodeName];
946         valid = valid && !this.invalidHandleIds[node.id];
947
948         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
949             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
950         }
951
952
953         return valid;
954
955     },
956
957     /**
958      * Create the array of horizontal tick marks if an interval was specified
959      * in setXConstraint().
960      * @method setXTicks
961      * @private
962      */
963     setXTicks: function(iStartX, iTickSize) {
964         this.xTicks = [];
965         this.xTickSize = iTickSize;
966
967         var tickMap = {};
968
969         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
970             if (!tickMap[i]) {
971                 this.xTicks[this.xTicks.length] = i;
972                 tickMap[i] = true;
973             }
974         }
975
976         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
977             if (!tickMap[i]) {
978                 this.xTicks[this.xTicks.length] = i;
979                 tickMap[i] = true;
980             }
981         }
982
983         this.xTicks.sort(this.DDM.numericSort) ;
984     },
985
986     /**
987      * Create the array of vertical tick marks if an interval was specified in
988      * setYConstraint().
989      * @method setYTicks
990      * @private
991      */
992     setYTicks: function(iStartY, iTickSize) {
993         this.yTicks = [];
994         this.yTickSize = iTickSize;
995
996         var tickMap = {};
997
998         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
999             if (!tickMap[i]) {
1000                 this.yTicks[this.yTicks.length] = i;
1001                 tickMap[i] = true;
1002             }
1003         }
1004
1005         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1006             if (!tickMap[i]) {
1007                 this.yTicks[this.yTicks.length] = i;
1008                 tickMap[i] = true;
1009             }
1010         }
1011
1012         this.yTicks.sort(this.DDM.numericSort) ;
1013     },
1014
1015     /**
1016      * By default, the element can be dragged any place on the screen.  Use
1017      * this method to limit the horizontal travel of the element.  Pass in
1018      * 0,0 for the parameters if you want to lock the drag to the y axis.
1019      * @method setXConstraint
1020      * @param {int} iLeft the number of pixels the element can move to the left
1021      * @param {int} iRight the number of pixels the element can move to the
1022      * right
1023      * @param {int} iTickSize optional parameter for specifying that the
1024      * element
1025      * should move iTickSize pixels at a time.
1026      */
1027     setXConstraint: function(iLeft, iRight, iTickSize) {
1028         this.leftConstraint = iLeft;
1029         this.rightConstraint = iRight;
1030
1031         this.minX = this.initPageX - iLeft;
1032         this.maxX = this.initPageX + iRight;
1033         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1034
1035         this.constrainX = true;
1036     },
1037
1038     /**
1039      * Clears any constraints applied to this instance.  Also clears ticks
1040      * since they can't exist independent of a constraint at this time.
1041      * @method clearConstraints
1042      */
1043     clearConstraints: function() {
1044         this.constrainX = false;
1045         this.constrainY = false;
1046         this.clearTicks();
1047     },
1048
1049     /**
1050      * Clears any tick interval defined for this instance
1051      * @method clearTicks
1052      */
1053     clearTicks: function() {
1054         this.xTicks = null;
1055         this.yTicks = null;
1056         this.xTickSize = 0;
1057         this.yTickSize = 0;
1058     },
1059
1060     /**
1061      * By default, the element can be dragged any place on the screen.  Set
1062      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1063      * parameters if you want to lock the drag to the x axis.
1064      * @method setYConstraint
1065      * @param {int} iUp the number of pixels the element can move up
1066      * @param {int} iDown the number of pixels the element can move down
1067      * @param {int} iTickSize optional parameter for specifying that the
1068      * element should move iTickSize pixels at a time.
1069      */
1070     setYConstraint: function(iUp, iDown, iTickSize) {
1071         this.topConstraint = iUp;
1072         this.bottomConstraint = iDown;
1073
1074         this.minY = this.initPageY - iUp;
1075         this.maxY = this.initPageY + iDown;
1076         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1077
1078         this.constrainY = true;
1079
1080     },
1081
1082     /**
1083      * resetConstraints must be called if you manually reposition a dd element.
1084      * @method resetConstraints
1085      * @param {boolean} maintainOffset
1086      */
1087     resetConstraints: function() {
1088
1089
1090         // Maintain offsets if necessary
1091         if (this.initPageX || this.initPageX === 0) {
1092             // figure out how much this thing has moved
1093             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1094             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1095
1096             this.setInitPosition(dx, dy);
1097
1098         // This is the first time we have detected the element's position
1099         } else {
1100             this.setInitPosition();
1101         }
1102
1103         if (this.constrainX) {
1104             this.setXConstraint( this.leftConstraint,
1105                                  this.rightConstraint,
1106                                  this.xTickSize        );
1107         }
1108
1109         if (this.constrainY) {
1110             this.setYConstraint( this.topConstraint,
1111                                  this.bottomConstraint,
1112                                  this.yTickSize         );
1113         }
1114     },
1115
1116     /**
1117      * Normally the drag element is moved pixel by pixel, but we can specify
1118      * that it move a number of pixels at a time.  This method resolves the
1119      * location when we have it set up like this.
1120      * @method getTick
1121      * @param {int} val where we want to place the object
1122      * @param {int[]} tickArray sorted array of valid points
1123      * @return {int} the closest tick
1124      * @private
1125      */
1126     getTick: function(val, tickArray) {
1127
1128         if (!tickArray) {
1129             // If tick interval is not defined, it is effectively 1 pixel,
1130             // so we return the value passed to us.
1131             return val;
1132         } else if (tickArray[0] >= val) {
1133             // The value is lower than the first tick, so we return the first
1134             // tick.
1135             return tickArray[0];
1136         } else {
1137             for (var i=0, len=tickArray.length; i<len; ++i) {
1138                 var next = i + 1;
1139                 if (tickArray[next] && tickArray[next] >= val) {
1140                     var diff1 = val - tickArray[i];
1141                     var diff2 = tickArray[next] - val;
1142                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1143                 }
1144             }
1145
1146             // The value is larger than the last tick, so we return the last
1147             // tick.
1148             return tickArray[tickArray.length - 1];
1149         }
1150     },
1151
1152     /**
1153      * toString method
1154      * @method toString
1155      * @return {string} string representation of the dd obj
1156      */
1157     toString: function() {
1158         return ("DragDrop " + this.id);
1159     }
1160
1161 });
1162
1163 })();
1164 /*
1165  * Based on:
1166  * Ext JS Library 1.1.1
1167  * Copyright(c) 2006-2007, Ext JS, LLC.
1168  *
1169  * Originally Released Under LGPL - original licence link has changed is not relivant.
1170  *
1171  * Fork - LGPL
1172  * <script type="text/javascript">
1173  */
1174
1175
1176 /**
1177  * The drag and drop utility provides a framework for building drag and drop
1178  * applications.  In addition to enabling drag and drop for specific elements,
1179  * the drag and drop elements are tracked by the manager class, and the
1180  * interactions between the various elements are tracked during the drag and
1181  * the implementing code is notified about these important moments.
1182  */
1183
1184 // Only load the library once.  Rewriting the manager class would orphan
1185 // existing drag and drop instances.
1186 if (!Roo.dd.DragDropMgr) {
1187
1188 /**
1189  * @class Roo.dd.DragDropMgr
1190  * DragDropMgr is a singleton that tracks the element interaction for
1191  * all DragDrop items in the window.  Generally, you will not call
1192  * this class directly, but it does have helper methods that could
1193  * be useful in your DragDrop implementations.
1194  * @singleton
1195  */
1196 Roo.dd.DragDropMgr = function() {
1197
1198     var Event = Roo.EventManager;
1199
1200     return {
1201
1202         /**
1203          * Two dimensional Array of registered DragDrop objects.  The first
1204          * dimension is the DragDrop item group, the second the DragDrop
1205          * object.
1206          * @property ids
1207          * @type {string: string}
1208          * @private
1209          * @static
1210          */
1211         ids: {},
1212
1213         /**
1214          * Array of element ids defined as drag handles.  Used to determine
1215          * if the element that generated the mousedown event is actually the
1216          * handle and not the html element itself.
1217          * @property handleIds
1218          * @type {string: string}
1219          * @private
1220          * @static
1221          */
1222         handleIds: {},
1223
1224         /**
1225          * the DragDrop object that is currently being dragged
1226          * @property dragCurrent
1227          * @type DragDrop
1228          * @private
1229          * @static
1230          **/
1231         dragCurrent: null,
1232
1233         /**
1234          * the DragDrop object(s) that are being hovered over
1235          * @property dragOvers
1236          * @type Array
1237          * @private
1238          * @static
1239          */
1240         dragOvers: {},
1241
1242         /**
1243          * the X distance between the cursor and the object being dragged
1244          * @property deltaX
1245          * @type int
1246          * @private
1247          * @static
1248          */
1249         deltaX: 0,
1250
1251         /**
1252          * the Y distance between the cursor and the object being dragged
1253          * @property deltaY
1254          * @type int
1255          * @private
1256          * @static
1257          */
1258         deltaY: 0,
1259
1260         /**
1261          * Flag to determine if we should prevent the default behavior of the
1262          * events we define. By default this is true, but this can be set to
1263          * false if you need the default behavior (not recommended)
1264          * @property preventDefault
1265          * @type boolean
1266          * @static
1267          */
1268         preventDefault: true,
1269
1270         /**
1271          * Flag to determine if we should stop the propagation of the events
1272          * we generate. This is true by default but you may want to set it to
1273          * false if the html element contains other features that require the
1274          * mouse click.
1275          * @property stopPropagation
1276          * @type boolean
1277          * @static
1278          */
1279         stopPropagation: true,
1280
1281         /**
1282          * Internal flag that is set to true when drag and drop has been
1283          * intialized
1284          * @property initialized
1285          * @private
1286          * @static
1287          */
1288         initalized: false,
1289
1290         /**
1291          * All drag and drop can be disabled.
1292          * @property locked
1293          * @private
1294          * @static
1295          */
1296         locked: false,
1297
1298         /**
1299          * Called the first time an element is registered.
1300          * @method init
1301          * @private
1302          * @static
1303          */
1304         init: function() {
1305             this.initialized = true;
1306         },
1307
1308         /**
1309          * In point mode, drag and drop interaction is defined by the
1310          * location of the cursor during the drag/drop
1311          * @property POINT
1312          * @type int
1313          * @static
1314          */
1315         POINT: 0,
1316
1317         /**
1318          * In intersect mode, drag and drop interactio nis defined by the
1319          * overlap of two or more drag and drop objects.
1320          * @property INTERSECT
1321          * @type int
1322          * @static
1323          */
1324         INTERSECT: 1,
1325
1326         /**
1327          * The current drag and drop mode.  Default: POINT
1328          * @property mode
1329          * @type int
1330          * @static
1331          */
1332         mode: 0,
1333
1334         /**
1335          * Runs method on all drag and drop objects
1336          * @method _execOnAll
1337          * @private
1338          * @static
1339          */
1340         _execOnAll: function(sMethod, args) {
1341             for (var i in this.ids) {
1342                 for (var j in this.ids[i]) {
1343                     var oDD = this.ids[i][j];
1344                     if (! this.isTypeOfDD(oDD)) {
1345                         continue;
1346                     }
1347                     oDD[sMethod].apply(oDD, args);
1348                 }
1349             }
1350         },
1351
1352         /**
1353          * Drag and drop initialization.  Sets up the global event handlers
1354          * @method _onLoad
1355          * @private
1356          * @static
1357          */
1358         _onLoad: function() {
1359
1360             this.init();
1361
1362
1363             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1364             Event.on(document, "mousemove", this.handleMouseMove, this, true);
1365             Event.on(window,   "unload",    this._onUnload, this, true);
1366             Event.on(window,   "resize",    this._onResize, this, true);
1367             // Event.on(window,   "mouseout",    this._test);
1368
1369         },
1370
1371         /**
1372          * Reset constraints on all drag and drop objs
1373          * @method _onResize
1374          * @private
1375          * @static
1376          */
1377         _onResize: function(e) {
1378             this._execOnAll("resetConstraints", []);
1379         },
1380
1381         /**
1382          * Lock all drag and drop functionality
1383          * @method lock
1384          * @static
1385          */
1386         lock: function() { this.locked = true; },
1387
1388         /**
1389          * Unlock all drag and drop functionality
1390          * @method unlock
1391          * @static
1392          */
1393         unlock: function() { this.locked = false; },
1394
1395         /**
1396          * Is drag and drop locked?
1397          * @method isLocked
1398          * @return {boolean} True if drag and drop is locked, false otherwise.
1399          * @static
1400          */
1401         isLocked: function() { return this.locked; },
1402
1403         /**
1404          * Location cache that is set for all drag drop objects when a drag is
1405          * initiated, cleared when the drag is finished.
1406          * @property locationCache
1407          * @private
1408          * @static
1409          */
1410         locationCache: {},
1411
1412         /**
1413          * Set useCache to false if you want to force object the lookup of each
1414          * drag and drop linked element constantly during a drag.
1415          * @property useCache
1416          * @type boolean
1417          * @static
1418          */
1419         useCache: true,
1420
1421         /**
1422          * The number of pixels that the mouse needs to move after the
1423          * mousedown before the drag is initiated.  Default=3;
1424          * @property clickPixelThresh
1425          * @type int
1426          * @static
1427          */
1428         clickPixelThresh: 3,
1429
1430         /**
1431          * The number of milliseconds after the mousedown event to initiate the
1432          * drag if we don't get a mouseup event. Default=1000
1433          * @property clickTimeThresh
1434          * @type int
1435          * @static
1436          */
1437         clickTimeThresh: 350,
1438
1439         /**
1440          * Flag that indicates that either the drag pixel threshold or the
1441          * mousdown time threshold has been met
1442          * @property dragThreshMet
1443          * @type boolean
1444          * @private
1445          * @static
1446          */
1447         dragThreshMet: false,
1448
1449         /**
1450          * Timeout used for the click time threshold
1451          * @property clickTimeout
1452          * @type Object
1453          * @private
1454          * @static
1455          */
1456         clickTimeout: null,
1457
1458         /**
1459          * The X position of the mousedown event stored for later use when a
1460          * drag threshold is met.
1461          * @property startX
1462          * @type int
1463          * @private
1464          * @static
1465          */
1466         startX: 0,
1467
1468         /**
1469          * The Y position of the mousedown event stored for later use when a
1470          * drag threshold is met.
1471          * @property startY
1472          * @type int
1473          * @private
1474          * @static
1475          */
1476         startY: 0,
1477
1478         /**
1479          * Each DragDrop instance must be registered with the DragDropMgr.
1480          * This is executed in DragDrop.init()
1481          * @method regDragDrop
1482          * @param {DragDrop} oDD the DragDrop object to register
1483          * @param {String} sGroup the name of the group this element belongs to
1484          * @static
1485          */
1486         regDragDrop: function(oDD, sGroup) {
1487             if (!this.initialized) { this.init(); }
1488
1489             if (!this.ids[sGroup]) {
1490                 this.ids[sGroup] = {};
1491             }
1492             this.ids[sGroup][oDD.id] = oDD;
1493         },
1494
1495         /**
1496          * Removes the supplied dd instance from the supplied group. Executed
1497          * by DragDrop.removeFromGroup, so don't call this function directly.
1498          * @method removeDDFromGroup
1499          * @private
1500          * @static
1501          */
1502         removeDDFromGroup: function(oDD, sGroup) {
1503             if (!this.ids[sGroup]) {
1504                 this.ids[sGroup] = {};
1505             }
1506
1507             var obj = this.ids[sGroup];
1508             if (obj && obj[oDD.id]) {
1509                 delete obj[oDD.id];
1510             }
1511         },
1512
1513         /**
1514          * Unregisters a drag and drop item.  This is executed in
1515          * DragDrop.unreg, use that method instead of calling this directly.
1516          * @method _remove
1517          * @private
1518          * @static
1519          */
1520         _remove: function(oDD) {
1521             for (var g in oDD.groups) {
1522                 if (g && this.ids[g][oDD.id]) {
1523                     delete this.ids[g][oDD.id];
1524                 }
1525             }
1526             delete this.handleIds[oDD.id];
1527         },
1528
1529         /**
1530          * Each DragDrop handle element must be registered.  This is done
1531          * automatically when executing DragDrop.setHandleElId()
1532          * @method regHandle
1533          * @param {String} sDDId the DragDrop id this element is a handle for
1534          * @param {String} sHandleId the id of the element that is the drag
1535          * handle
1536          * @static
1537          */
1538         regHandle: function(sDDId, sHandleId) {
1539             if (!this.handleIds[sDDId]) {
1540                 this.handleIds[sDDId] = {};
1541             }
1542             this.handleIds[sDDId][sHandleId] = sHandleId;
1543         },
1544
1545         /**
1546          * Utility function to determine if a given element has been
1547          * registered as a drag drop item.
1548          * @method isDragDrop
1549          * @param {String} id the element id to check
1550          * @return {boolean} true if this element is a DragDrop item,
1551          * false otherwise
1552          * @static
1553          */
1554         isDragDrop: function(id) {
1555             return ( this.getDDById(id) ) ? true : false;
1556         },
1557
1558         /**
1559          * Returns the drag and drop instances that are in all groups the
1560          * passed in instance belongs to.
1561          * @method getRelated
1562          * @param {DragDrop} p_oDD the obj to get related data for
1563          * @param {boolean} bTargetsOnly if true, only return targetable objs
1564          * @return {DragDrop[]} the related instances
1565          * @static
1566          */
1567         getRelated: function(p_oDD, bTargetsOnly) {
1568             var oDDs = [];
1569             for (var i in p_oDD.groups) {
1570                 for (j in this.ids[i]) {
1571                     var dd = this.ids[i][j];
1572                     if (! this.isTypeOfDD(dd)) {
1573                         continue;
1574                     }
1575                     if (!bTargetsOnly || dd.isTarget) {
1576                         oDDs[oDDs.length] = dd;
1577                     }
1578                 }
1579             }
1580
1581             return oDDs;
1582         },
1583
1584         /**
1585          * Returns true if the specified dd target is a legal target for
1586          * the specifice drag obj
1587          * @method isLegalTarget
1588          * @param {DragDrop} the drag obj
1589          * @param {DragDrop} the target
1590          * @return {boolean} true if the target is a legal target for the
1591          * dd obj
1592          * @static
1593          */
1594         isLegalTarget: function (oDD, oTargetDD) {
1595             var targets = this.getRelated(oDD, true);
1596             for (var i=0, len=targets.length;i<len;++i) {
1597                 if (targets[i].id == oTargetDD.id) {
1598                     return true;
1599                 }
1600             }
1601
1602             return false;
1603         },
1604
1605         /**
1606          * My goal is to be able to transparently determine if an object is
1607          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1608          * returns "object", oDD.constructor.toString() always returns
1609          * "DragDrop" and not the name of the subclass.  So for now it just
1610          * evaluates a well-known variable in DragDrop.
1611          * @method isTypeOfDD
1612          * @param {Object} the object to evaluate
1613          * @return {boolean} true if typeof oDD = DragDrop
1614          * @static
1615          */
1616         isTypeOfDD: function (oDD) {
1617             return (oDD && oDD.__ygDragDrop);
1618         },
1619
1620         /**
1621          * Utility function to determine if a given element has been
1622          * registered as a drag drop handle for the given Drag Drop object.
1623          * @method isHandle
1624          * @param {String} id the element id to check
1625          * @return {boolean} true if this element is a DragDrop handle, false
1626          * otherwise
1627          * @static
1628          */
1629         isHandle: function(sDDId, sHandleId) {
1630             return ( this.handleIds[sDDId] &&
1631                             this.handleIds[sDDId][sHandleId] );
1632         },
1633
1634         /**
1635          * Returns the DragDrop instance for a given id
1636          * @method getDDById
1637          * @param {String} id the id of the DragDrop object
1638          * @return {DragDrop} the drag drop object, null if it is not found
1639          * @static
1640          */
1641         getDDById: function(id) {
1642             for (var i in this.ids) {
1643                 if (this.ids[i][id]) {
1644                     return this.ids[i][id];
1645                 }
1646             }
1647             return null;
1648         },
1649
1650         /**
1651          * Fired after a registered DragDrop object gets the mousedown event.
1652          * Sets up the events required to track the object being dragged
1653          * @method handleMouseDown
1654          * @param {Event} e the event
1655          * @param oDD the DragDrop object being dragged
1656          * @private
1657          * @static
1658          */
1659         handleMouseDown: function(e, oDD) {
1660             if(Roo.QuickTips){
1661                 Roo.QuickTips.disable();
1662             }
1663             this.currentTarget = e.getTarget();
1664
1665             this.dragCurrent = oDD;
1666
1667             var el = oDD.getEl();
1668
1669             // track start position
1670             this.startX = e.getPageX();
1671             this.startY = e.getPageY();
1672
1673             this.deltaX = this.startX - el.offsetLeft;
1674             this.deltaY = this.startY - el.offsetTop;
1675
1676             this.dragThreshMet = false;
1677
1678             this.clickTimeout = setTimeout(
1679                     function() {
1680                         var DDM = Roo.dd.DDM;
1681                         DDM.startDrag(DDM.startX, DDM.startY);
1682                     },
1683                     this.clickTimeThresh );
1684         },
1685
1686         /**
1687          * Fired when either the drag pixel threshol or the mousedown hold
1688          * time threshold has been met.
1689          * @method startDrag
1690          * @param x {int} the X position of the original mousedown
1691          * @param y {int} the Y position of the original mousedown
1692          * @static
1693          */
1694         startDrag: function(x, y) {
1695             clearTimeout(this.clickTimeout);
1696             if (this.dragCurrent) {
1697                 this.dragCurrent.b4StartDrag(x, y);
1698                 this.dragCurrent.startDrag(x, y);
1699             }
1700             this.dragThreshMet = true;
1701         },
1702
1703         /**
1704          * Internal function to handle the mouseup event.  Will be invoked
1705          * from the context of the document.
1706          * @method handleMouseUp
1707          * @param {Event} e the event
1708          * @private
1709          * @static
1710          */
1711         handleMouseUp: function(e) {
1712
1713             if(Roo.QuickTips){
1714                 Roo.QuickTips.enable();
1715             }
1716             if (! this.dragCurrent) {
1717                 return;
1718             }
1719
1720             clearTimeout(this.clickTimeout);
1721
1722             if (this.dragThreshMet) {
1723                 this.fireEvents(e, true);
1724             } else {
1725             }
1726
1727             this.stopDrag(e);
1728
1729             this.stopEvent(e);
1730         },
1731
1732         /**
1733          * Utility to stop event propagation and event default, if these
1734          * features are turned on.
1735          * @method stopEvent
1736          * @param {Event} e the event as returned by this.getEvent()
1737          * @static
1738          */
1739         stopEvent: function(e){
1740             if(this.stopPropagation) {
1741                 e.stopPropagation();
1742             }
1743
1744             if (this.preventDefault) {
1745                 e.preventDefault();
1746             }
1747         },
1748
1749         /**
1750          * Internal function to clean up event handlers after the drag
1751          * operation is complete
1752          * @method stopDrag
1753          * @param {Event} e the event
1754          * @private
1755          * @static
1756          */
1757         stopDrag: function(e) {
1758             // Fire the drag end event for the item that was dragged
1759             if (this.dragCurrent) {
1760                 if (this.dragThreshMet) {
1761                     this.dragCurrent.b4EndDrag(e);
1762                     this.dragCurrent.endDrag(e);
1763                 }
1764
1765                 this.dragCurrent.onMouseUp(e);
1766             }
1767
1768             this.dragCurrent = null;
1769             this.dragOvers = {};
1770         },
1771
1772         /**
1773          * Internal function to handle the mousemove event.  Will be invoked
1774          * from the context of the html element.
1775          *
1776          * @TODO figure out what we can do about mouse events lost when the
1777          * user drags objects beyond the window boundary.  Currently we can
1778          * detect this in internet explorer by verifying that the mouse is
1779          * down during the mousemove event.  Firefox doesn't give us the
1780          * button state on the mousemove event.
1781          * @method handleMouseMove
1782          * @param {Event} e the event
1783          * @private
1784          * @static
1785          */
1786         handleMouseMove: function(e) {
1787             if (! this.dragCurrent) {
1788                 return true;
1789             }
1790
1791             // var button = e.which || e.button;
1792
1793             // check for IE mouseup outside of page boundary
1794             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1795                 this.stopEvent(e);
1796                 return this.handleMouseUp(e);
1797             }
1798
1799             if (!this.dragThreshMet) {
1800                 var diffX = Math.abs(this.startX - e.getPageX());
1801                 var diffY = Math.abs(this.startY - e.getPageY());
1802                 if (diffX > this.clickPixelThresh ||
1803                             diffY > this.clickPixelThresh) {
1804                     this.startDrag(this.startX, this.startY);
1805                 }
1806             }
1807
1808             if (this.dragThreshMet) {
1809                 this.dragCurrent.b4Drag(e);
1810                 this.dragCurrent.onDrag(e);
1811                 if(!this.dragCurrent.moveOnly){
1812                     this.fireEvents(e, false);
1813                 }
1814             }
1815
1816             this.stopEvent(e);
1817
1818             return true;
1819         },
1820
1821         /**
1822          * Iterates over all of the DragDrop elements to find ones we are
1823          * hovering over or dropping on
1824          * @method fireEvents
1825          * @param {Event} e the event
1826          * @param {boolean} isDrop is this a drop op or a mouseover op?
1827          * @private
1828          * @static
1829          */
1830         fireEvents: function(e, isDrop) {
1831             var dc = this.dragCurrent;
1832
1833             // If the user did the mouse up outside of the window, we could
1834             // get here even though we have ended the drag.
1835             if (!dc || dc.isLocked()) {
1836                 return;
1837             }
1838
1839             var pt = e.getPoint();
1840
1841             // cache the previous dragOver array
1842             var oldOvers = [];
1843
1844             var outEvts   = [];
1845             var overEvts  = [];
1846             var dropEvts  = [];
1847             var enterEvts = [];
1848
1849             // Check to see if the object(s) we were hovering over is no longer
1850             // being hovered over so we can fire the onDragOut event
1851             for (var i in this.dragOvers) {
1852
1853                 var ddo = this.dragOvers[i];
1854
1855                 if (! this.isTypeOfDD(ddo)) {
1856                     continue;
1857                 }
1858
1859                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1860                     outEvts.push( ddo );
1861                 }
1862
1863                 oldOvers[i] = true;
1864                 delete this.dragOvers[i];
1865             }
1866
1867             for (var sGroup in dc.groups) {
1868
1869                 if ("string" != typeof sGroup) {
1870                     continue;
1871                 }
1872
1873                 for (i in this.ids[sGroup]) {
1874                     var oDD = this.ids[sGroup][i];
1875                     if (! this.isTypeOfDD(oDD)) {
1876                         continue;
1877                     }
1878
1879                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1880                         if (this.isOverTarget(pt, oDD, this.mode)) {
1881                             // look for drop interactions
1882                             if (isDrop) {
1883                                 dropEvts.push( oDD );
1884                             // look for drag enter and drag over interactions
1885                             } else {
1886
1887                                 // initial drag over: dragEnter fires
1888                                 if (!oldOvers[oDD.id]) {
1889                                     enterEvts.push( oDD );
1890                                 // subsequent drag overs: dragOver fires
1891                                 } else {
1892                                     overEvts.push( oDD );
1893                                 }
1894
1895                                 this.dragOvers[oDD.id] = oDD;
1896                             }
1897                         }
1898                     }
1899                 }
1900             }
1901
1902             if (this.mode) {
1903                 if (outEvts.length) {
1904                     dc.b4DragOut(e, outEvts);
1905                     dc.onDragOut(e, outEvts);
1906                 }
1907
1908                 if (enterEvts.length) {
1909                     dc.onDragEnter(e, enterEvts);
1910                 }
1911
1912                 if (overEvts.length) {
1913                     dc.b4DragOver(e, overEvts);
1914                     dc.onDragOver(e, overEvts);
1915                 }
1916
1917                 if (dropEvts.length) {
1918                     dc.b4DragDrop(e, dropEvts);
1919                     dc.onDragDrop(e, dropEvts);
1920                 }
1921
1922             } else {
1923                 // fire dragout events
1924                 var len = 0;
1925                 for (i=0, len=outEvts.length; i<len; ++i) {
1926                     dc.b4DragOut(e, outEvts[i].id);
1927                     dc.onDragOut(e, outEvts[i].id);
1928                 }
1929
1930                 // fire enter events
1931                 for (i=0,len=enterEvts.length; i<len; ++i) {
1932                     // dc.b4DragEnter(e, oDD.id);
1933                     dc.onDragEnter(e, enterEvts[i].id);
1934                 }
1935
1936                 // fire over events
1937                 for (i=0,len=overEvts.length; i<len; ++i) {
1938                     dc.b4DragOver(e, overEvts[i].id);
1939                     dc.onDragOver(e, overEvts[i].id);
1940                 }
1941
1942                 // fire drop events
1943                 for (i=0, len=dropEvts.length; i<len; ++i) {
1944                     dc.b4DragDrop(e, dropEvts[i].id);
1945                     dc.onDragDrop(e, dropEvts[i].id);
1946                 }
1947
1948             }
1949
1950             // notify about a drop that did not find a target
1951             if (isDrop && !dropEvts.length) {
1952                 dc.onInvalidDrop(e);
1953             }
1954
1955         },
1956
1957         /**
1958          * Helper function for getting the best match from the list of drag
1959          * and drop objects returned by the drag and drop events when we are
1960          * in INTERSECT mode.  It returns either the first object that the
1961          * cursor is over, or the object that has the greatest overlap with
1962          * the dragged element.
1963          * @method getBestMatch
1964          * @param  {DragDrop[]} dds The array of drag and drop objects
1965          * targeted
1966          * @return {DragDrop}       The best single match
1967          * @static
1968          */
1969         getBestMatch: function(dds) {
1970             var winner = null;
1971             // Return null if the input is not what we expect
1972             //if (!dds || !dds.length || dds.length == 0) {
1973                // winner = null;
1974             // If there is only one item, it wins
1975             //} else if (dds.length == 1) {
1976
1977             var len = dds.length;
1978
1979             if (len == 1) {
1980                 winner = dds[0];
1981             } else {
1982                 // Loop through the targeted items
1983                 for (var i=0; i<len; ++i) {
1984                     var dd = dds[i];
1985                     // If the cursor is over the object, it wins.  If the
1986                     // cursor is over multiple matches, the first one we come
1987                     // to wins.
1988                     if (dd.cursorIsOver) {
1989                         winner = dd;
1990                         break;
1991                     // Otherwise the object with the most overlap wins
1992                     } else {
1993                         if (!winner ||
1994                             winner.overlap.getArea() < dd.overlap.getArea()) {
1995                             winner = dd;
1996                         }
1997                     }
1998                 }
1999             }
2000
2001             return winner;
2002         },
2003
2004         /**
2005          * Refreshes the cache of the top-left and bottom-right points of the
2006          * drag and drop objects in the specified group(s).  This is in the
2007          * format that is stored in the drag and drop instance, so typical
2008          * usage is:
2009          * <code>
2010          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2011          * </code>
2012          * Alternatively:
2013          * <code>
2014          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2015          * </code>
2016          * @TODO this really should be an indexed array.  Alternatively this
2017          * method could accept both.
2018          * @method refreshCache
2019          * @param {Object} groups an associative array of groups to refresh
2020          * @static
2021          */
2022         refreshCache: function(groups) {
2023             for (var sGroup in groups) {
2024                 if ("string" != typeof sGroup) {
2025                     continue;
2026                 }
2027                 for (var i in this.ids[sGroup]) {
2028                     var oDD = this.ids[sGroup][i];
2029
2030                     if (this.isTypeOfDD(oDD)) {
2031                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2032                         var loc = this.getLocation(oDD);
2033                         if (loc) {
2034                             this.locationCache[oDD.id] = loc;
2035                         } else {
2036                             delete this.locationCache[oDD.id];
2037                             // this will unregister the drag and drop object if
2038                             // the element is not in a usable state
2039                             // oDD.unreg();
2040                         }
2041                     }
2042                 }
2043             }
2044         },
2045
2046         /**
2047          * This checks to make sure an element exists and is in the DOM.  The
2048          * main purpose is to handle cases where innerHTML is used to remove
2049          * drag and drop objects from the DOM.  IE provides an 'unspecified
2050          * error' when trying to access the offsetParent of such an element
2051          * @method verifyEl
2052          * @param {HTMLElement} el the element to check
2053          * @return {boolean} true if the element looks usable
2054          * @static
2055          */
2056         verifyEl: function(el) {
2057             if (el) {
2058                 var parent;
2059                 if(Roo.isIE){
2060                     try{
2061                         parent = el.offsetParent;
2062                     }catch(e){}
2063                 }else{
2064                     parent = el.offsetParent;
2065                 }
2066                 if (parent) {
2067                     return true;
2068                 }
2069             }
2070
2071             return false;
2072         },
2073
2074         /**
2075          * Returns a Region object containing the drag and drop element's position
2076          * and size, including the padding configured for it
2077          * @method getLocation
2078          * @param {DragDrop} oDD the drag and drop object to get the
2079          *                       location for
2080          * @return {Roo.lib.Region} a Region object representing the total area
2081          *                             the element occupies, including any padding
2082          *                             the instance is configured for.
2083          * @static
2084          */
2085         getLocation: function(oDD) {
2086             if (! this.isTypeOfDD(oDD)) {
2087                 return null;
2088             }
2089
2090             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2091
2092             try {
2093                 pos= Roo.lib.Dom.getXY(el);
2094             } catch (e) { }
2095
2096             if (!pos) {
2097                 return null;
2098             }
2099
2100             x1 = pos[0];
2101             x2 = x1 + el.offsetWidth;
2102             y1 = pos[1];
2103             y2 = y1 + el.offsetHeight;
2104
2105             t = y1 - oDD.padding[0];
2106             r = x2 + oDD.padding[1];
2107             b = y2 + oDD.padding[2];
2108             l = x1 - oDD.padding[3];
2109
2110             return new Roo.lib.Region( t, r, b, l );
2111         },
2112
2113         /**
2114          * Checks the cursor location to see if it over the target
2115          * @method isOverTarget
2116          * @param {Roo.lib.Point} pt The point to evaluate
2117          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2118          * @return {boolean} true if the mouse is over the target
2119          * @private
2120          * @static
2121          */
2122         isOverTarget: function(pt, oTarget, intersect) {
2123             // use cache if available
2124             var loc = this.locationCache[oTarget.id];
2125             if (!loc || !this.useCache) {
2126                 loc = this.getLocation(oTarget);
2127                 this.locationCache[oTarget.id] = loc;
2128
2129             }
2130
2131             if (!loc) {
2132                 return false;
2133             }
2134
2135             oTarget.cursorIsOver = loc.contains( pt );
2136
2137             // DragDrop is using this as a sanity check for the initial mousedown
2138             // in this case we are done.  In POINT mode, if the drag obj has no
2139             // contraints, we are also done. Otherwise we need to evaluate the
2140             // location of the target as related to the actual location of the
2141             // dragged element.
2142             var dc = this.dragCurrent;
2143             if (!dc || !dc.getTargetCoord ||
2144                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2145                 return oTarget.cursorIsOver;
2146             }
2147
2148             oTarget.overlap = null;
2149
2150             // Get the current location of the drag element, this is the
2151             // location of the mouse event less the delta that represents
2152             // where the original mousedown happened on the element.  We
2153             // need to consider constraints and ticks as well.
2154             var pos = dc.getTargetCoord(pt.x, pt.y);
2155
2156             var el = dc.getDragEl();
2157             var curRegion = new Roo.lib.Region( pos.y,
2158                                                    pos.x + el.offsetWidth,
2159                                                    pos.y + el.offsetHeight,
2160                                                    pos.x );
2161
2162             var overlap = curRegion.intersect(loc);
2163
2164             if (overlap) {
2165                 oTarget.overlap = overlap;
2166                 return (intersect) ? true : oTarget.cursorIsOver;
2167             } else {
2168                 return false;
2169             }
2170         },
2171
2172         /**
2173          * unload event handler
2174          * @method _onUnload
2175          * @private
2176          * @static
2177          */
2178         _onUnload: function(e, me) {
2179             Roo.dd.DragDropMgr.unregAll();
2180         },
2181
2182         /**
2183          * Cleans up the drag and drop events and objects.
2184          * @method unregAll
2185          * @private
2186          * @static
2187          */
2188         unregAll: function() {
2189
2190             if (this.dragCurrent) {
2191                 this.stopDrag();
2192                 this.dragCurrent = null;
2193             }
2194
2195             this._execOnAll("unreg", []);
2196
2197             for (i in this.elementCache) {
2198                 delete this.elementCache[i];
2199             }
2200
2201             this.elementCache = {};
2202             this.ids = {};
2203         },
2204
2205         /**
2206          * A cache of DOM elements
2207          * @property elementCache
2208          * @private
2209          * @static
2210          */
2211         elementCache: {},
2212
2213         /**
2214          * Get the wrapper for the DOM element specified
2215          * @method getElWrapper
2216          * @param {String} id the id of the element to get
2217          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2218          * @private
2219          * @deprecated This wrapper isn't that useful
2220          * @static
2221          */
2222         getElWrapper: function(id) {
2223             var oWrapper = this.elementCache[id];
2224             if (!oWrapper || !oWrapper.el) {
2225                 oWrapper = this.elementCache[id] =
2226                     new this.ElementWrapper(Roo.getDom(id));
2227             }
2228             return oWrapper;
2229         },
2230
2231         /**
2232          * Returns the actual DOM element
2233          * @method getElement
2234          * @param {String} id the id of the elment to get
2235          * @return {Object} The element
2236          * @deprecated use Roo.getDom instead
2237          * @static
2238          */
2239         getElement: function(id) {
2240             return Roo.getDom(id);
2241         },
2242
2243         /**
2244          * Returns the style property for the DOM element (i.e.,
2245          * document.getElById(id).style)
2246          * @method getCss
2247          * @param {String} id the id of the elment to get
2248          * @return {Object} The style property of the element
2249          * @deprecated use Roo.getDom instead
2250          * @static
2251          */
2252         getCss: function(id) {
2253             var el = Roo.getDom(id);
2254             return (el) ? el.style : null;
2255         },
2256
2257         /**
2258          * Inner class for cached elements
2259          * @class DragDropMgr.ElementWrapper
2260          * @for DragDropMgr
2261          * @private
2262          * @deprecated
2263          */
2264         ElementWrapper: function(el) {
2265                 /**
2266                  * The element
2267                  * @property el
2268                  */
2269                 this.el = el || null;
2270                 /**
2271                  * The element id
2272                  * @property id
2273                  */
2274                 this.id = this.el && el.id;
2275                 /**
2276                  * A reference to the style property
2277                  * @property css
2278                  */
2279                 this.css = this.el && el.style;
2280             },
2281
2282         /**
2283          * Returns the X position of an html element
2284          * @method getPosX
2285          * @param el the element for which to get the position
2286          * @return {int} the X coordinate
2287          * @for DragDropMgr
2288          * @deprecated use Roo.lib.Dom.getX instead
2289          * @static
2290          */
2291         getPosX: function(el) {
2292             return Roo.lib.Dom.getX(el);
2293         },
2294
2295         /**
2296          * Returns the Y position of an html element
2297          * @method getPosY
2298          * @param el the element for which to get the position
2299          * @return {int} the Y coordinate
2300          * @deprecated use Roo.lib.Dom.getY instead
2301          * @static
2302          */
2303         getPosY: function(el) {
2304             return Roo.lib.Dom.getY(el);
2305         },
2306
2307         /**
2308          * Swap two nodes.  In IE, we use the native method, for others we
2309          * emulate the IE behavior
2310          * @method swapNode
2311          * @param n1 the first node to swap
2312          * @param n2 the other node to swap
2313          * @static
2314          */
2315         swapNode: function(n1, n2) {
2316             if (n1.swapNode) {
2317                 n1.swapNode(n2);
2318             } else {
2319                 var p = n2.parentNode;
2320                 var s = n2.nextSibling;
2321
2322                 if (s == n1) {
2323                     p.insertBefore(n1, n2);
2324                 } else if (n2 == n1.nextSibling) {
2325                     p.insertBefore(n2, n1);
2326                 } else {
2327                     n1.parentNode.replaceChild(n2, n1);
2328                     p.insertBefore(n1, s);
2329                 }
2330             }
2331         },
2332
2333         /**
2334          * Returns the current scroll position
2335          * @method getScroll
2336          * @private
2337          * @static
2338          */
2339         getScroll: function () {
2340             var t, l, dde=document.documentElement, db=document.body;
2341             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2342                 t = dde.scrollTop;
2343                 l = dde.scrollLeft;
2344             } else if (db) {
2345                 t = db.scrollTop;
2346                 l = db.scrollLeft;
2347             } else {
2348
2349             }
2350             return { top: t, left: l };
2351         },
2352
2353         /**
2354          * Returns the specified element style property
2355          * @method getStyle
2356          * @param {HTMLElement} el          the element
2357          * @param {string}      styleProp   the style property
2358          * @return {string} The value of the style property
2359          * @deprecated use Roo.lib.Dom.getStyle
2360          * @static
2361          */
2362         getStyle: function(el, styleProp) {
2363             return Roo.fly(el).getStyle(styleProp);
2364         },
2365
2366         /**
2367          * Gets the scrollTop
2368          * @method getScrollTop
2369          * @return {int} the document's scrollTop
2370          * @static
2371          */
2372         getScrollTop: function () { return this.getScroll().top; },
2373
2374         /**
2375          * Gets the scrollLeft
2376          * @method getScrollLeft
2377          * @return {int} the document's scrollTop
2378          * @static
2379          */
2380         getScrollLeft: function () { return this.getScroll().left; },
2381
2382         /**
2383          * Sets the x/y position of an element to the location of the
2384          * target element.
2385          * @method moveToEl
2386          * @param {HTMLElement} moveEl      The element to move
2387          * @param {HTMLElement} targetEl    The position reference element
2388          * @static
2389          */
2390         moveToEl: function (moveEl, targetEl) {
2391             var aCoord = Roo.lib.Dom.getXY(targetEl);
2392             Roo.lib.Dom.setXY(moveEl, aCoord);
2393         },
2394
2395         /**
2396          * Numeric array sort function
2397          * @method numericSort
2398          * @static
2399          */
2400         numericSort: function(a, b) { return (a - b); },
2401
2402         /**
2403          * Internal counter
2404          * @property _timeoutCount
2405          * @private
2406          * @static
2407          */
2408         _timeoutCount: 0,
2409
2410         /**
2411          * Trying to make the load order less important.  Without this we get
2412          * an error if this file is loaded before the Event Utility.
2413          * @method _addListeners
2414          * @private
2415          * @static
2416          */
2417         _addListeners: function() {
2418             var DDM = Roo.dd.DDM;
2419             if ( Roo.lib.Event && document ) {
2420                 DDM._onLoad();
2421             } else {
2422                 if (DDM._timeoutCount > 2000) {
2423                 } else {
2424                     setTimeout(DDM._addListeners, 10);
2425                     if (document && document.body) {
2426                         DDM._timeoutCount += 1;
2427                     }
2428                 }
2429             }
2430         },
2431
2432         /**
2433          * Recursively searches the immediate parent and all child nodes for
2434          * the handle element in order to determine wheter or not it was
2435          * clicked.
2436          * @method handleWasClicked
2437          * @param node the html element to inspect
2438          * @static
2439          */
2440         handleWasClicked: function(node, id) {
2441             if (this.isHandle(id, node.id)) {
2442                 return true;
2443             } else {
2444                 // check to see if this is a text node child of the one we want
2445                 var p = node.parentNode;
2446
2447                 while (p) {
2448                     if (this.isHandle(id, p.id)) {
2449                         return true;
2450                     } else {
2451                         p = p.parentNode;
2452                     }
2453                 }
2454             }
2455
2456             return false;
2457         }
2458
2459     };
2460
2461 }();
2462
2463 // shorter alias, save a few bytes
2464 Roo.dd.DDM = Roo.dd.DragDropMgr;
2465 Roo.dd.DDM._addListeners();
2466
2467 }/*
2468  * Based on:
2469  * Ext JS Library 1.1.1
2470  * Copyright(c) 2006-2007, Ext JS, LLC.
2471  *
2472  * Originally Released Under LGPL - original licence link has changed is not relivant.
2473  *
2474  * Fork - LGPL
2475  * <script type="text/javascript">
2476  */
2477
2478 /**
2479  * @class Roo.dd.DD
2480  * A DragDrop implementation where the linked element follows the
2481  * mouse cursor during a drag.
2482  * @extends Roo.dd.DragDrop
2483  * @constructor
2484  * @param {String} id the id of the linked element
2485  * @param {String} sGroup the group of related DragDrop items
2486  * @param {object} config an object containing configurable attributes
2487  *                Valid properties for DD:
2488  *                    scroll
2489  */
2490 Roo.dd.DD = function(id, sGroup, config) {
2491     if (id) {
2492         this.init(id, sGroup, config);
2493     }
2494 };
2495
2496 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2497
2498     /**
2499      * When set to true, the utility automatically tries to scroll the browser
2500      * window wehn a drag and drop element is dragged near the viewport boundary.
2501      * Defaults to true.
2502      * @property scroll
2503      * @type boolean
2504      */
2505     scroll: true,
2506
2507     /**
2508      * Sets the pointer offset to the distance between the linked element's top
2509      * left corner and the location the element was clicked
2510      * @method autoOffset
2511      * @param {int} iPageX the X coordinate of the click
2512      * @param {int} iPageY the Y coordinate of the click
2513      */
2514     autoOffset: function(iPageX, iPageY) {
2515         var x = iPageX - this.startPageX;
2516         var y = iPageY - this.startPageY;
2517         this.setDelta(x, y);
2518     },
2519
2520     /**
2521      * Sets the pointer offset.  You can call this directly to force the
2522      * offset to be in a particular location (e.g., pass in 0,0 to set it
2523      * to the center of the object)
2524      * @method setDelta
2525      * @param {int} iDeltaX the distance from the left
2526      * @param {int} iDeltaY the distance from the top
2527      */
2528     setDelta: function(iDeltaX, iDeltaY) {
2529         this.deltaX = iDeltaX;
2530         this.deltaY = iDeltaY;
2531     },
2532
2533     /**
2534      * Sets the drag element to the location of the mousedown or click event,
2535      * maintaining the cursor location relative to the location on the element
2536      * that was clicked.  Override this if you want to place the element in a
2537      * location other than where the cursor is.
2538      * @method setDragElPos
2539      * @param {int} iPageX the X coordinate of the mousedown or drag event
2540      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2541      */
2542     setDragElPos: function(iPageX, iPageY) {
2543         // the first time we do this, we are going to check to make sure
2544         // the element has css positioning
2545
2546         var el = this.getDragEl();
2547         this.alignElWithMouse(el, iPageX, iPageY);
2548     },
2549
2550     /**
2551      * Sets the element to the location of the mousedown or click event,
2552      * maintaining the cursor location relative to the location on the element
2553      * that was clicked.  Override this if you want to place the element in a
2554      * location other than where the cursor is.
2555      * @method alignElWithMouse
2556      * @param {HTMLElement} el the element to move
2557      * @param {int} iPageX the X coordinate of the mousedown or drag event
2558      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2559      */
2560     alignElWithMouse: function(el, iPageX, iPageY) {
2561         var oCoord = this.getTargetCoord(iPageX, iPageY);
2562         var fly = el.dom ? el : Roo.fly(el);
2563         if (!this.deltaSetXY) {
2564             var aCoord = [oCoord.x, oCoord.y];
2565             fly.setXY(aCoord);
2566             var newLeft = fly.getLeft(true);
2567             var newTop  = fly.getTop(true);
2568             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2569         } else {
2570             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2571         }
2572
2573         this.cachePosition(oCoord.x, oCoord.y);
2574         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2575         return oCoord;
2576     },
2577
2578     /**
2579      * Saves the most recent position so that we can reset the constraints and
2580      * tick marks on-demand.  We need to know this so that we can calculate the
2581      * number of pixels the element is offset from its original position.
2582      * @method cachePosition
2583      * @param iPageX the current x position (optional, this just makes it so we
2584      * don't have to look it up again)
2585      * @param iPageY the current y position (optional, this just makes it so we
2586      * don't have to look it up again)
2587      */
2588     cachePosition: function(iPageX, iPageY) {
2589         if (iPageX) {
2590             this.lastPageX = iPageX;
2591             this.lastPageY = iPageY;
2592         } else {
2593             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2594             this.lastPageX = aCoord[0];
2595             this.lastPageY = aCoord[1];
2596         }
2597     },
2598
2599     /**
2600      * Auto-scroll the window if the dragged object has been moved beyond the
2601      * visible window boundary.
2602      * @method autoScroll
2603      * @param {int} x the drag element's x position
2604      * @param {int} y the drag element's y position
2605      * @param {int} h the height of the drag element
2606      * @param {int} w the width of the drag element
2607      * @private
2608      */
2609     autoScroll: function(x, y, h, w) {
2610
2611         if (this.scroll) {
2612             // The client height
2613             var clientH = Roo.lib.Dom.getViewWidth();
2614
2615             // The client width
2616             var clientW = Roo.lib.Dom.getViewHeight();
2617
2618             // The amt scrolled down
2619             var st = this.DDM.getScrollTop();
2620
2621             // The amt scrolled right
2622             var sl = this.DDM.getScrollLeft();
2623
2624             // Location of the bottom of the element
2625             var bot = h + y;
2626
2627             // Location of the right of the element
2628             var right = w + x;
2629
2630             // The distance from the cursor to the bottom of the visible area,
2631             // adjusted so that we don't scroll if the cursor is beyond the
2632             // element drag constraints
2633             var toBot = (clientH + st - y - this.deltaY);
2634
2635             // The distance from the cursor to the right of the visible area
2636             var toRight = (clientW + sl - x - this.deltaX);
2637
2638
2639             // How close to the edge the cursor must be before we scroll
2640             // var thresh = (document.all) ? 100 : 40;
2641             var thresh = 40;
2642
2643             // How many pixels to scroll per autoscroll op.  This helps to reduce
2644             // clunky scrolling. IE is more sensitive about this ... it needs this
2645             // value to be higher.
2646             var scrAmt = (document.all) ? 80 : 30;
2647
2648             // Scroll down if we are near the bottom of the visible page and the
2649             // obj extends below the crease
2650             if ( bot > clientH && toBot < thresh ) {
2651                 window.scrollTo(sl, st + scrAmt);
2652             }
2653
2654             // Scroll up if the window is scrolled down and the top of the object
2655             // goes above the top border
2656             if ( y < st && st > 0 && y - st < thresh ) {
2657                 window.scrollTo(sl, st - scrAmt);
2658             }
2659
2660             // Scroll right if the obj is beyond the right border and the cursor is
2661             // near the border.
2662             if ( right > clientW && toRight < thresh ) {
2663                 window.scrollTo(sl + scrAmt, st);
2664             }
2665
2666             // Scroll left if the window has been scrolled to the right and the obj
2667             // extends past the left border
2668             if ( x < sl && sl > 0 && x - sl < thresh ) {
2669                 window.scrollTo(sl - scrAmt, st);
2670             }
2671         }
2672     },
2673
2674     /**
2675      * Finds the location the element should be placed if we want to move
2676      * it to where the mouse location less the click offset would place us.
2677      * @method getTargetCoord
2678      * @param {int} iPageX the X coordinate of the click
2679      * @param {int} iPageY the Y coordinate of the click
2680      * @return an object that contains the coordinates (Object.x and Object.y)
2681      * @private
2682      */
2683     getTargetCoord: function(iPageX, iPageY) {
2684
2685
2686         var x = iPageX - this.deltaX;
2687         var y = iPageY - this.deltaY;
2688
2689         if (this.constrainX) {
2690             if (x < this.minX) { x = this.minX; }
2691             if (x > this.maxX) { x = this.maxX; }
2692         }
2693
2694         if (this.constrainY) {
2695             if (y < this.minY) { y = this.minY; }
2696             if (y > this.maxY) { y = this.maxY; }
2697         }
2698
2699         x = this.getTick(x, this.xTicks);
2700         y = this.getTick(y, this.yTicks);
2701
2702
2703         return {x:x, y:y};
2704     },
2705
2706     /*
2707      * Sets up config options specific to this class. Overrides
2708      * Roo.dd.DragDrop, but all versions of this method through the
2709      * inheritance chain are called
2710      */
2711     applyConfig: function() {
2712         Roo.dd.DD.superclass.applyConfig.call(this);
2713         this.scroll = (this.config.scroll !== false);
2714     },
2715
2716     /*
2717      * Event that fires prior to the onMouseDown event.  Overrides
2718      * Roo.dd.DragDrop.
2719      */
2720     b4MouseDown: function(e) {
2721         // this.resetConstraints();
2722         this.autoOffset(e.getPageX(),
2723                             e.getPageY());
2724     },
2725
2726     /*
2727      * Event that fires prior to the onDrag event.  Overrides
2728      * Roo.dd.DragDrop.
2729      */
2730     b4Drag: function(e) {
2731         this.setDragElPos(e.getPageX(),
2732                             e.getPageY());
2733     },
2734
2735     toString: function() {
2736         return ("DD " + this.id);
2737     }
2738
2739     //////////////////////////////////////////////////////////////////////////
2740     // Debugging ygDragDrop events that can be overridden
2741     //////////////////////////////////////////////////////////////////////////
2742     /*
2743     startDrag: function(x, y) {
2744     },
2745
2746     onDrag: function(e) {
2747     },
2748
2749     onDragEnter: function(e, id) {
2750     },
2751
2752     onDragOver: function(e, id) {
2753     },
2754
2755     onDragOut: function(e, id) {
2756     },
2757
2758     onDragDrop: function(e, id) {
2759     },
2760
2761     endDrag: function(e) {
2762     }
2763
2764     */
2765
2766 });/*
2767  * Based on:
2768  * Ext JS Library 1.1.1
2769  * Copyright(c) 2006-2007, Ext JS, LLC.
2770  *
2771  * Originally Released Under LGPL - original licence link has changed is not relivant.
2772  *
2773  * Fork - LGPL
2774  * <script type="text/javascript">
2775  */
2776
2777 /**
2778  * @class Roo.dd.DDProxy
2779  * A DragDrop implementation that inserts an empty, bordered div into
2780  * the document that follows the cursor during drag operations.  At the time of
2781  * the click, the frame div is resized to the dimensions of the linked html
2782  * element, and moved to the exact location of the linked element.
2783  *
2784  * References to the "frame" element refer to the single proxy element that
2785  * was created to be dragged in place of all DDProxy elements on the
2786  * page.
2787  *
2788  * @extends Roo.dd.DD
2789  * @constructor
2790  * @param {String} id the id of the linked html element
2791  * @param {String} sGroup the group of related DragDrop objects
2792  * @param {object} config an object containing configurable attributes
2793  *                Valid properties for DDProxy in addition to those in DragDrop:
2794  *                   resizeFrame, centerFrame, dragElId
2795  */
2796 Roo.dd.DDProxy = function(id, sGroup, config) {
2797     if (id) {
2798         this.init(id, sGroup, config);
2799         this.initFrame();
2800     }
2801 };
2802
2803 /**
2804  * The default drag frame div id
2805  * @property Roo.dd.DDProxy.dragElId
2806  * @type String
2807  * @static
2808  */
2809 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2810
2811 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2812
2813     /**
2814      * By default we resize the drag frame to be the same size as the element
2815      * we want to drag (this is to get the frame effect).  We can turn it off
2816      * if we want a different behavior.
2817      * @property resizeFrame
2818      * @type boolean
2819      */
2820     resizeFrame: true,
2821
2822     /**
2823      * By default the frame is positioned exactly where the drag element is, so
2824      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2825      * you do not have constraints on the obj is to have the drag frame centered
2826      * around the cursor.  Set centerFrame to true for this effect.
2827      * @property centerFrame
2828      * @type boolean
2829      */
2830     centerFrame: false,
2831
2832     /**
2833      * Creates the proxy element if it does not yet exist
2834      * @method createFrame
2835      */
2836     createFrame: function() {
2837         var self = this;
2838         var body = document.body;
2839
2840         if (!body || !body.firstChild) {
2841             setTimeout( function() { self.createFrame(); }, 50 );
2842             return;
2843         }
2844
2845         var div = this.getDragEl();
2846
2847         if (!div) {
2848             div    = document.createElement("div");
2849             div.id = this.dragElId;
2850             var s  = div.style;
2851
2852             s.position   = "absolute";
2853             s.visibility = "hidden";
2854             s.cursor     = "move";
2855             s.border     = "2px solid #aaa";
2856             s.zIndex     = 999;
2857
2858             // appendChild can blow up IE if invoked prior to the window load event
2859             // while rendering a table.  It is possible there are other scenarios
2860             // that would cause this to happen as well.
2861             body.insertBefore(div, body.firstChild);
2862         }
2863     },
2864
2865     /**
2866      * Initialization for the drag frame element.  Must be called in the
2867      * constructor of all subclasses
2868      * @method initFrame
2869      */
2870     initFrame: function() {
2871         this.createFrame();
2872     },
2873
2874     applyConfig: function() {
2875         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2876
2877         this.resizeFrame = (this.config.resizeFrame !== false);
2878         this.centerFrame = (this.config.centerFrame);
2879         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2880     },
2881
2882     /**
2883      * Resizes the drag frame to the dimensions of the clicked object, positions
2884      * it over the object, and finally displays it
2885      * @method showFrame
2886      * @param {int} iPageX X click position
2887      * @param {int} iPageY Y click position
2888      * @private
2889      */
2890     showFrame: function(iPageX, iPageY) {
2891         var el = this.getEl();
2892         var dragEl = this.getDragEl();
2893         var s = dragEl.style;
2894
2895         this._resizeProxy();
2896
2897         if (this.centerFrame) {
2898             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2899                            Math.round(parseInt(s.height, 10)/2) );
2900         }
2901
2902         this.setDragElPos(iPageX, iPageY);
2903
2904         Roo.fly(dragEl).show();
2905     },
2906
2907     /**
2908      * The proxy is automatically resized to the dimensions of the linked
2909      * element when a drag is initiated, unless resizeFrame is set to false
2910      * @method _resizeProxy
2911      * @private
2912      */
2913     _resizeProxy: function() {
2914         if (this.resizeFrame) {
2915             var el = this.getEl();
2916             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2917         }
2918     },
2919
2920     // overrides Roo.dd.DragDrop
2921     b4MouseDown: function(e) {
2922         var x = e.getPageX();
2923         var y = e.getPageY();
2924         this.autoOffset(x, y);
2925         this.setDragElPos(x, y);
2926     },
2927
2928     // overrides Roo.dd.DragDrop
2929     b4StartDrag: function(x, y) {
2930         // show the drag frame
2931         this.showFrame(x, y);
2932     },
2933
2934     // overrides Roo.dd.DragDrop
2935     b4EndDrag: function(e) {
2936         Roo.fly(this.getDragEl()).hide();
2937     },
2938
2939     // overrides Roo.dd.DragDrop
2940     // By default we try to move the element to the last location of the frame.
2941     // This is so that the default behavior mirrors that of Roo.dd.DD.
2942     endDrag: function(e) {
2943
2944         var lel = this.getEl();
2945         var del = this.getDragEl();
2946
2947         // Show the drag frame briefly so we can get its position
2948         del.style.visibility = "";
2949
2950         this.beforeMove();
2951         // Hide the linked element before the move to get around a Safari
2952         // rendering bug.
2953         lel.style.visibility = "hidden";
2954         Roo.dd.DDM.moveToEl(lel, del);
2955         del.style.visibility = "hidden";
2956         lel.style.visibility = "";
2957
2958         this.afterDrag();
2959     },
2960
2961     beforeMove : function(){
2962
2963     },
2964
2965     afterDrag : function(){
2966
2967     },
2968
2969     toString: function() {
2970         return ("DDProxy " + this.id);
2971     }
2972
2973 });
2974 /*
2975  * Based on:
2976  * Ext JS Library 1.1.1
2977  * Copyright(c) 2006-2007, Ext JS, LLC.
2978  *
2979  * Originally Released Under LGPL - original licence link has changed is not relivant.
2980  *
2981  * Fork - LGPL
2982  * <script type="text/javascript">
2983  */
2984
2985  /**
2986  * @class Roo.dd.DDTarget
2987  * A DragDrop implementation that does not move, but can be a drop
2988  * target.  You would get the same result by simply omitting implementation
2989  * for the event callbacks, but this way we reduce the processing cost of the
2990  * event listener and the callbacks.
2991  * @extends Roo.dd.DragDrop
2992  * @constructor
2993  * @param {String} id the id of the element that is a drop target
2994  * @param {String} sGroup the group of related DragDrop objects
2995  * @param {object} config an object containing configurable attributes
2996  *                 Valid properties for DDTarget in addition to those in
2997  *                 DragDrop:
2998  *                    none
2999  */
3000 Roo.dd.DDTarget = function(id, sGroup, config) {
3001     if (id) {
3002         this.initTarget(id, sGroup, config);
3003     }
3004     if (config.listeners || config.events) { 
3005        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
3006             listeners : config.listeners || {}, 
3007             events : config.events || {} 
3008         });    
3009     }
3010 };
3011
3012 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3013 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3014     toString: function() {
3015         return ("DDTarget " + this.id);
3016     }
3017 });
3018 /*
3019  * Based on:
3020  * Ext JS Library 1.1.1
3021  * Copyright(c) 2006-2007, Ext JS, LLC.
3022  *
3023  * Originally Released Under LGPL - original licence link has changed is not relivant.
3024  *
3025  * Fork - LGPL
3026  * <script type="text/javascript">
3027  */
3028  
3029
3030 /**
3031  * @class Roo.dd.ScrollManager
3032  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3033  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3034  * @singleton
3035  */
3036 Roo.dd.ScrollManager = function(){
3037     var ddm = Roo.dd.DragDropMgr;
3038     var els = {};
3039     var dragEl = null;
3040     var proc = {};
3041     
3042     var onStop = function(e){
3043         dragEl = null;
3044         clearProc();
3045     };
3046     
3047     var triggerRefresh = function(){
3048         if(ddm.dragCurrent){
3049              ddm.refreshCache(ddm.dragCurrent.groups);
3050         }
3051     };
3052     
3053     var doScroll = function(){
3054         if(ddm.dragCurrent){
3055             var dds = Roo.dd.ScrollManager;
3056             if(!dds.animate){
3057                 if(proc.el.scroll(proc.dir, dds.increment)){
3058                     triggerRefresh();
3059                 }
3060             }else{
3061                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3062             }
3063         }
3064     };
3065     
3066     var clearProc = function(){
3067         if(proc.id){
3068             clearInterval(proc.id);
3069         }
3070         proc.id = 0;
3071         proc.el = null;
3072         proc.dir = "";
3073     };
3074     
3075     var startProc = function(el, dir){
3076         clearProc();
3077         proc.el = el;
3078         proc.dir = dir;
3079         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3080     };
3081     
3082     var onFire = function(e, isDrop){
3083         if(isDrop || !ddm.dragCurrent){ return; }
3084         var dds = Roo.dd.ScrollManager;
3085         if(!dragEl || dragEl != ddm.dragCurrent){
3086             dragEl = ddm.dragCurrent;
3087             // refresh regions on drag start
3088             dds.refreshCache();
3089         }
3090         
3091         var xy = Roo.lib.Event.getXY(e);
3092         var pt = new Roo.lib.Point(xy[0], xy[1]);
3093         for(var id in els){
3094             var el = els[id], r = el._region;
3095             if(r && r.contains(pt) && el.isScrollable()){
3096                 if(r.bottom - pt.y <= dds.thresh){
3097                     if(proc.el != el){
3098                         startProc(el, "down");
3099                     }
3100                     return;
3101                 }else if(r.right - pt.x <= dds.thresh){
3102                     if(proc.el != el){
3103                         startProc(el, "left");
3104                     }
3105                     return;
3106                 }else if(pt.y - r.top <= dds.thresh){
3107                     if(proc.el != el){
3108                         startProc(el, "up");
3109                     }
3110                     return;
3111                 }else if(pt.x - r.left <= dds.thresh){
3112                     if(proc.el != el){
3113                         startProc(el, "right");
3114                     }
3115                     return;
3116                 }
3117             }
3118         }
3119         clearProc();
3120     };
3121     
3122     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3123     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3124     
3125     return {
3126         /**
3127          * Registers new overflow element(s) to auto scroll
3128          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3129          */
3130         register : function(el){
3131             if(el instanceof Array){
3132                 for(var i = 0, len = el.length; i < len; i++) {
3133                         this.register(el[i]);
3134                 }
3135             }else{
3136                 el = Roo.get(el);
3137                 els[el.id] = el;
3138             }
3139         },
3140         
3141         /**
3142          * Unregisters overflow element(s) so they are no longer scrolled
3143          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3144          */
3145         unregister : function(el){
3146             if(el instanceof Array){
3147                 for(var i = 0, len = el.length; i < len; i++) {
3148                         this.unregister(el[i]);
3149                 }
3150             }else{
3151                 el = Roo.get(el);
3152                 delete els[el.id];
3153             }
3154         },
3155         
3156         /**
3157          * The number of pixels from the edge of a container the pointer needs to be to 
3158          * trigger scrolling (defaults to 25)
3159          * @type Number
3160          */
3161         thresh : 25,
3162         
3163         /**
3164          * The number of pixels to scroll in each scroll increment (defaults to 50)
3165          * @type Number
3166          */
3167         increment : 100,
3168         
3169         /**
3170          * The frequency of scrolls in milliseconds (defaults to 500)
3171          * @type Number
3172          */
3173         frequency : 500,
3174         
3175         /**
3176          * True to animate the scroll (defaults to true)
3177          * @type Boolean
3178          */
3179         animate: true,
3180         
3181         /**
3182          * The animation duration in seconds - 
3183          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3184          * @type Number
3185          */
3186         animDuration: .4,
3187         
3188         /**
3189          * Manually trigger a cache refresh.
3190          */
3191         refreshCache : function(){
3192             for(var id in els){
3193                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3194                     els[id]._region = els[id].getRegion();
3195                 }
3196             }
3197         }
3198     };
3199 }();/*
3200  * Based on:
3201  * Ext JS Library 1.1.1
3202  * Copyright(c) 2006-2007, Ext JS, LLC.
3203  *
3204  * Originally Released Under LGPL - original licence link has changed is not relivant.
3205  *
3206  * Fork - LGPL
3207  * <script type="text/javascript">
3208  */
3209  
3210
3211 /**
3212  * @class Roo.dd.Registry
3213  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3214  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3215  * @singleton
3216  */
3217 Roo.dd.Registry = function(){
3218     var elements = {}; 
3219     var handles = {}; 
3220     var autoIdSeed = 0;
3221
3222     var getId = function(el, autogen){
3223         if(typeof el == "string"){
3224             return el;
3225         }
3226         var id = el.id;
3227         if(!id && autogen !== false){
3228             id = "roodd-" + (++autoIdSeed);
3229             el.id = id;
3230         }
3231         return id;
3232     };
3233     
3234     return {
3235     /**
3236      * Register a drag drop element
3237      * @param {String|HTMLElement} element The id or DOM node to register
3238      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3239      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3240      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3241      * populated in the data object (if applicable):
3242      * <pre>
3243 Value      Description<br />
3244 ---------  ------------------------------------------<br />
3245 handles    Array of DOM nodes that trigger dragging<br />
3246            for the element being registered<br />
3247 isHandle   True if the element passed in triggers<br />
3248            dragging itself, else false
3249 </pre>
3250      */
3251         register : function(el, data){
3252             data = data || {};
3253             if(typeof el == "string"){
3254                 el = document.getElementById(el);
3255             }
3256             data.ddel = el;
3257             elements[getId(el)] = data;
3258             if(data.isHandle !== false){
3259                 handles[data.ddel.id] = data;
3260             }
3261             if(data.handles){
3262                 var hs = data.handles;
3263                 for(var i = 0, len = hs.length; i < len; i++){
3264                         handles[getId(hs[i])] = data;
3265                 }
3266             }
3267         },
3268
3269     /**
3270      * Unregister a drag drop element
3271      * @param {String|HTMLElement}  element The id or DOM node to unregister
3272      */
3273         unregister : function(el){
3274             var id = getId(el, false);
3275             var data = elements[id];
3276             if(data){
3277                 delete elements[id];
3278                 if(data.handles){
3279                     var hs = data.handles;
3280                     for(var i = 0, len = hs.length; i < len; i++){
3281                         delete handles[getId(hs[i], false)];
3282                     }
3283                 }
3284             }
3285         },
3286
3287     /**
3288      * Returns the handle registered for a DOM Node by id
3289      * @param {String|HTMLElement} id The DOM node or id to look up
3290      * @return {Object} handle The custom handle data
3291      */
3292         getHandle : function(id){
3293             if(typeof id != "string"){ // must be element?
3294                 id = id.id;
3295             }
3296             return handles[id];
3297         },
3298
3299     /**
3300      * Returns the handle that is registered for the DOM node that is the target of the event
3301      * @param {Event} e The event
3302      * @return {Object} handle The custom handle data
3303      */
3304         getHandleFromEvent : function(e){
3305             var t = Roo.lib.Event.getTarget(e);
3306             return t ? handles[t.id] : null;
3307         },
3308
3309     /**
3310      * Returns a custom data object that is registered for a DOM node by id
3311      * @param {String|HTMLElement} id The DOM node or id to look up
3312      * @return {Object} data The custom data
3313      */
3314         getTarget : function(id){
3315             if(typeof id != "string"){ // must be element?
3316                 id = id.id;
3317             }
3318             return elements[id];
3319         },
3320
3321     /**
3322      * Returns a custom data object that is registered for the DOM node that is the target of the event
3323      * @param {Event} e The event
3324      * @return {Object} data The custom data
3325      */
3326         getTargetFromEvent : function(e){
3327             var t = Roo.lib.Event.getTarget(e);
3328             return t ? elements[t.id] || handles[t.id] : null;
3329         }
3330     };
3331 }();/*
3332  * Based on:
3333  * Ext JS Library 1.1.1
3334  * Copyright(c) 2006-2007, Ext JS, LLC.
3335  *
3336  * Originally Released Under LGPL - original licence link has changed is not relivant.
3337  *
3338  * Fork - LGPL
3339  * <script type="text/javascript">
3340  */
3341  
3342
3343 /**
3344  * @class Roo.dd.StatusProxy
3345  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3346  * default drag proxy used by all Roo.dd components.
3347  * @constructor
3348  * @param {Object} config
3349  */
3350 Roo.dd.StatusProxy = function(config){
3351     Roo.apply(this, config);
3352     this.id = this.id || Roo.id();
3353     this.el = new Roo.Layer({
3354         dh: {
3355             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3356                 {tag: "div", cls: "x-dd-drop-icon"},
3357                 {tag: "div", cls: "x-dd-drag-ghost"}
3358             ]
3359         }, 
3360         shadow: !config || config.shadow !== false
3361     });
3362     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3363     this.dropStatus = this.dropNotAllowed;
3364 };
3365
3366 Roo.dd.StatusProxy.prototype = {
3367     /**
3368      * @cfg {String} dropAllowed
3369      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3370      */
3371     dropAllowed : "x-dd-drop-ok",
3372     /**
3373      * @cfg {String} dropNotAllowed
3374      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3375      */
3376     dropNotAllowed : "x-dd-drop-nodrop",
3377
3378     /**
3379      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3380      * over the current target element.
3381      * @param {String} cssClass The css class for the new drop status indicator image
3382      */
3383     setStatus : function(cssClass){
3384         cssClass = cssClass || this.dropNotAllowed;
3385         if(this.dropStatus != cssClass){
3386             this.el.replaceClass(this.dropStatus, cssClass);
3387             this.dropStatus = cssClass;
3388         }
3389     },
3390
3391     /**
3392      * Resets the status indicator to the default dropNotAllowed value
3393      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3394      */
3395     reset : function(clearGhost){
3396         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3397         this.dropStatus = this.dropNotAllowed;
3398         if(clearGhost){
3399             this.ghost.update("");
3400         }
3401     },
3402
3403     /**
3404      * Updates the contents of the ghost element
3405      * @param {String} html The html that will replace the current innerHTML of the ghost element
3406      */
3407     update : function(html){
3408         if(typeof html == "string"){
3409             this.ghost.update(html);
3410         }else{
3411             this.ghost.update("");
3412             html.style.margin = "0";
3413             this.ghost.dom.appendChild(html);
3414         }
3415         // ensure float = none set?? cant remember why though.
3416         var el = this.ghost.dom.firstChild;
3417                 if(el){
3418                         Roo.fly(el).setStyle('float', 'none');
3419                 }
3420     },
3421     
3422     /**
3423      * Returns the underlying proxy {@link Roo.Layer}
3424      * @return {Roo.Layer} el
3425     */
3426     getEl : function(){
3427         return this.el;
3428     },
3429
3430     /**
3431      * Returns the ghost element
3432      * @return {Roo.Element} el
3433      */
3434     getGhost : function(){
3435         return this.ghost;
3436     },
3437
3438     /**
3439      * Hides the proxy
3440      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3441      */
3442     hide : function(clear){
3443         this.el.hide();
3444         if(clear){
3445             this.reset(true);
3446         }
3447     },
3448
3449     /**
3450      * Stops the repair animation if it's currently running
3451      */
3452     stop : function(){
3453         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3454             this.anim.stop();
3455         }
3456     },
3457
3458     /**
3459      * Displays this proxy
3460      */
3461     show : function(){
3462         this.el.show();
3463     },
3464
3465     /**
3466      * Force the Layer to sync its shadow and shim positions to the element
3467      */
3468     sync : function(){
3469         this.el.sync();
3470     },
3471
3472     /**
3473      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3474      * invalid drop operation by the item being dragged.
3475      * @param {Array} xy The XY position of the element ([x, y])
3476      * @param {Function} callback The function to call after the repair is complete
3477      * @param {Object} scope The scope in which to execute the callback
3478      */
3479     repair : function(xy, callback, scope){
3480         this.callback = callback;
3481         this.scope = scope;
3482         if(xy && this.animRepair !== false){
3483             this.el.addClass("x-dd-drag-repair");
3484             this.el.hideUnders(true);
3485             this.anim = this.el.shift({
3486                 duration: this.repairDuration || .5,
3487                 easing: 'easeOut',
3488                 xy: xy,
3489                 stopFx: true,
3490                 callback: this.afterRepair,
3491                 scope: this
3492             });
3493         }else{
3494             this.afterRepair();
3495         }
3496     },
3497
3498     // private
3499     afterRepair : function(){
3500         this.hide(true);
3501         if(typeof this.callback == "function"){
3502             this.callback.call(this.scope || this);
3503         }
3504         this.callback = null;
3505         this.scope = null;
3506     }
3507 };/*
3508  * Based on:
3509  * Ext JS Library 1.1.1
3510  * Copyright(c) 2006-2007, Ext JS, LLC.
3511  *
3512  * Originally Released Under LGPL - original licence link has changed is not relivant.
3513  *
3514  * Fork - LGPL
3515  * <script type="text/javascript">
3516  */
3517
3518 /**
3519  * @class Roo.dd.DragSource
3520  * @extends Roo.dd.DDProxy
3521  * A simple class that provides the basic implementation needed to make any element draggable.
3522  * @constructor
3523  * @param {String/HTMLElement/Element} el The container element
3524  * @param {Object} config
3525  */
3526 Roo.dd.DragSource = function(el, config){
3527     this.el = Roo.get(el);
3528     this.dragData = {};
3529     
3530     Roo.apply(this, config);
3531     
3532     if(!this.proxy){
3533         this.proxy = new Roo.dd.StatusProxy();
3534     }
3535
3536     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3537           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3538     
3539     this.dragging = false;
3540 };
3541
3542 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3543     /**
3544      * @cfg {String} dropAllowed
3545      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3546      */
3547     dropAllowed : "x-dd-drop-ok",
3548     /**
3549      * @cfg {String} dropNotAllowed
3550      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3551      */
3552     dropNotAllowed : "x-dd-drop-nodrop",
3553
3554     /**
3555      * Returns the data object associated with this drag source
3556      * @return {Object} data An object containing arbitrary data
3557      */
3558     getDragData : function(e){
3559         return this.dragData;
3560     },
3561
3562     // private
3563     onDragEnter : function(e, id){
3564         var target = Roo.dd.DragDropMgr.getDDById(id);
3565         this.cachedTarget = target;
3566         if(this.beforeDragEnter(target, e, id) !== false){
3567             if(target.isNotifyTarget){
3568                 var status = target.notifyEnter(this, e, this.dragData);
3569                 this.proxy.setStatus(status);
3570             }else{
3571                 this.proxy.setStatus(this.dropAllowed);
3572             }
3573             
3574             if(this.afterDragEnter){
3575                 /**
3576                  * An empty function by default, but provided so that you can perform a custom action
3577                  * when the dragged item enters the drop target by providing an implementation.
3578                  * @param {Roo.dd.DragDrop} target The drop target
3579                  * @param {Event} e The event object
3580                  * @param {String} id The id of the dragged element
3581                  * @method afterDragEnter
3582                  */
3583                 this.afterDragEnter(target, e, id);
3584             }
3585         }
3586     },
3587
3588     /**
3589      * An empty function by default, but provided so that you can perform a custom action
3590      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3591      * @param {Roo.dd.DragDrop} target The drop target
3592      * @param {Event} e The event object
3593      * @param {String} id The id of the dragged element
3594      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3595      */
3596     beforeDragEnter : function(target, e, id){
3597         return true;
3598     },
3599
3600     // private
3601     alignElWithMouse: function() {
3602         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3603         this.proxy.sync();
3604     },
3605
3606     // private
3607     onDragOver : function(e, id){
3608         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3609         if(this.beforeDragOver(target, e, id) !== false){
3610             if(target.isNotifyTarget){
3611                 var status = target.notifyOver(this, e, this.dragData);
3612                 this.proxy.setStatus(status);
3613             }
3614
3615             if(this.afterDragOver){
3616                 /**
3617                  * An empty function by default, but provided so that you can perform a custom action
3618                  * while the dragged item is over the drop target by providing an implementation.
3619                  * @param {Roo.dd.DragDrop} target The drop target
3620                  * @param {Event} e The event object
3621                  * @param {String} id The id of the dragged element
3622                  * @method afterDragOver
3623                  */
3624                 this.afterDragOver(target, e, id);
3625             }
3626         }
3627     },
3628
3629     /**
3630      * An empty function by default, but provided so that you can perform a custom action
3631      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3632      * @param {Roo.dd.DragDrop} target The drop target
3633      * @param {Event} e The event object
3634      * @param {String} id The id of the dragged element
3635      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3636      */
3637     beforeDragOver : function(target, e, id){
3638         return true;
3639     },
3640
3641     // private
3642     onDragOut : function(e, id){
3643         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3644         if(this.beforeDragOut(target, e, id) !== false){
3645             if(target.isNotifyTarget){
3646                 target.notifyOut(this, e, this.dragData);
3647             }
3648             this.proxy.reset();
3649             if(this.afterDragOut){
3650                 /**
3651                  * An empty function by default, but provided so that you can perform a custom action
3652                  * after the dragged item is dragged out of the target without dropping.
3653                  * @param {Roo.dd.DragDrop} target The drop target
3654                  * @param {Event} e The event object
3655                  * @param {String} id The id of the dragged element
3656                  * @method afterDragOut
3657                  */
3658                 this.afterDragOut(target, e, id);
3659             }
3660         }
3661         this.cachedTarget = null;
3662     },
3663
3664     /**
3665      * An empty function by default, but provided so that you can perform a custom action before the dragged
3666      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3667      * @param {Roo.dd.DragDrop} target The drop target
3668      * @param {Event} e The event object
3669      * @param {String} id The id of the dragged element
3670      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3671      */
3672     beforeDragOut : function(target, e, id){
3673         return true;
3674     },
3675     
3676     // private
3677     onDragDrop : function(e, id){
3678         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3679         if(this.beforeDragDrop(target, e, id) !== false){
3680             if(target.isNotifyTarget){
3681                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3682                     this.onValidDrop(target, e, id);
3683                 }else{
3684                     this.onInvalidDrop(target, e, id);
3685                 }
3686             }else{
3687                 this.onValidDrop(target, e, id);
3688             }
3689             
3690             if(this.afterDragDrop){
3691                 /**
3692                  * An empty function by default, but provided so that you can perform a custom action
3693                  * after a valid drag drop has occurred by providing an implementation.
3694                  * @param {Roo.dd.DragDrop} target The drop target
3695                  * @param {Event} e The event object
3696                  * @param {String} id The id of the dropped element
3697                  * @method afterDragDrop
3698                  */
3699                 this.afterDragDrop(target, e, id);
3700             }
3701         }
3702         delete this.cachedTarget;
3703     },
3704
3705     /**
3706      * An empty function by default, but provided so that you can perform a custom action before the dragged
3707      * item is dropped onto the target and optionally cancel the onDragDrop.
3708      * @param {Roo.dd.DragDrop} target The drop target
3709      * @param {Event} e The event object
3710      * @param {String} id The id of the dragged element
3711      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3712      */
3713     beforeDragDrop : function(target, e, id){
3714         return true;
3715     },
3716
3717     // private
3718     onValidDrop : function(target, e, id){
3719         this.hideProxy();
3720         if(this.afterValidDrop){
3721             /**
3722              * An empty function by default, but provided so that you can perform a custom action
3723              * after a valid drop has occurred by providing an implementation.
3724              * @param {Object} target The target DD 
3725              * @param {Event} e The event object
3726              * @param {String} id The id of the dropped element
3727              * @method afterInvalidDrop
3728              */
3729             this.afterValidDrop(target, e, id);
3730         }
3731     },
3732
3733     // private
3734     getRepairXY : function(e, data){
3735         return this.el.getXY();  
3736     },
3737
3738     // private
3739     onInvalidDrop : function(target, e, id){
3740         this.beforeInvalidDrop(target, e, id);
3741         if(this.cachedTarget){
3742             if(this.cachedTarget.isNotifyTarget){
3743                 this.cachedTarget.notifyOut(this, e, this.dragData);
3744             }
3745             this.cacheTarget = null;
3746         }
3747         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3748
3749         if(this.afterInvalidDrop){
3750             /**
3751              * An empty function by default, but provided so that you can perform a custom action
3752              * after an invalid drop has occurred by providing an implementation.
3753              * @param {Event} e The event object
3754              * @param {String} id The id of the dropped element
3755              * @method afterInvalidDrop
3756              */
3757             this.afterInvalidDrop(e, id);
3758         }
3759     },
3760
3761     // private
3762     afterRepair : function(){
3763         if(Roo.enableFx){
3764             this.el.highlight(this.hlColor || "c3daf9");
3765         }
3766         this.dragging = false;
3767     },
3768
3769     /**
3770      * An empty function by default, but provided so that you can perform a custom action after an invalid
3771      * drop has occurred.
3772      * @param {Roo.dd.DragDrop} target The drop target
3773      * @param {Event} e The event object
3774      * @param {String} id The id of the dragged element
3775      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3776      */
3777     beforeInvalidDrop : function(target, e, id){
3778         return true;
3779     },
3780
3781     // private
3782     handleMouseDown : function(e){
3783         if(this.dragging) {
3784             return;
3785         }
3786         var data = this.getDragData(e);
3787         if(data && this.onBeforeDrag(data, e) !== false){
3788             this.dragData = data;
3789             this.proxy.stop();
3790             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3791         } 
3792     },
3793
3794     /**
3795      * An empty function by default, but provided so that you can perform a custom action before the initial
3796      * drag event begins and optionally cancel it.
3797      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3798      * @param {Event} e The event object
3799      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3800      */
3801     onBeforeDrag : function(data, e){
3802         return true;
3803     },
3804
3805     /**
3806      * An empty function by default, but provided so that you can perform a custom action once the initial
3807      * drag event has begun.  The drag cannot be canceled from this function.
3808      * @param {Number} x The x position of the click on the dragged object
3809      * @param {Number} y The y position of the click on the dragged object
3810      */
3811     onStartDrag : Roo.emptyFn,
3812
3813     // private - YUI override
3814     startDrag : function(x, y){
3815         this.proxy.reset();
3816         this.dragging = true;
3817         this.proxy.update("");
3818         this.onInitDrag(x, y);
3819         this.proxy.show();
3820     },
3821
3822     // private
3823     onInitDrag : function(x, y){
3824         var clone = this.el.dom.cloneNode(true);
3825         clone.id = Roo.id(); // prevent duplicate ids
3826         this.proxy.update(clone);
3827         this.onStartDrag(x, y);
3828         return true;
3829     },
3830
3831     /**
3832      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3833      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3834      */
3835     getProxy : function(){
3836         return this.proxy;  
3837     },
3838
3839     /**
3840      * Hides the drag source's {@link Roo.dd.StatusProxy}
3841      */
3842     hideProxy : function(){
3843         this.proxy.hide();  
3844         this.proxy.reset(true);
3845         this.dragging = false;
3846     },
3847
3848     // private
3849     triggerCacheRefresh : function(){
3850         Roo.dd.DDM.refreshCache(this.groups);
3851     },
3852
3853     // private - override to prevent hiding
3854     b4EndDrag: function(e) {
3855     },
3856
3857     // private - override to prevent moving
3858     endDrag : function(e){
3859         this.onEndDrag(this.dragData, e);
3860     },
3861
3862     // private
3863     onEndDrag : function(data, e){
3864     },
3865     
3866     // private - pin to cursor
3867     autoOffset : function(x, y) {
3868         this.setDelta(-12, -20);
3869     }    
3870 });/*
3871  * Based on:
3872  * Ext JS Library 1.1.1
3873  * Copyright(c) 2006-2007, Ext JS, LLC.
3874  *
3875  * Originally Released Under LGPL - original licence link has changed is not relivant.
3876  *
3877  * Fork - LGPL
3878  * <script type="text/javascript">
3879  */
3880
3881
3882 /**
3883  * @class Roo.dd.DropTarget
3884  * @extends Roo.dd.DDTarget
3885  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3886  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3887  * @constructor
3888  * @param {String/HTMLElement/Element} el The container element
3889  * @param {Object} config
3890  */
3891 Roo.dd.DropTarget = function(el, config){
3892     this.el = Roo.get(el);
3893     
3894     Roo.apply(this, config);
3895     
3896     if(this.containerScroll){
3897         Roo.dd.ScrollManager.register(this.el);
3898     }
3899     this.addEvents( {
3900          /**
3901          * @scope Roo.dd.DropTarget
3902          */
3903          
3904          /**
3905          * @event enter
3906          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3907          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3908          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3909          * 
3910          * IMPORTANT : it should set this.overClass and this.dropAllowed
3911          * 
3912          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3913          * @param {Event} e The event
3914          * @param {Object} data An object containing arbitrary data supplied by the drag source
3915          */
3916         "enter" : true,
3917         
3918          /**
3919          * @event over
3920          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3921          * This method will be called on every mouse movement while the drag source is over the drop target.
3922          * This default implementation simply returns the dropAllowed config value.
3923          * 
3924          * IMPORTANT : it should set this.dropAllowed
3925          * 
3926          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3927          * @param {Event} e The event
3928          * @param {Object} data An object containing arbitrary data supplied by the drag source
3929          
3930          */
3931         "over" : true,
3932         /**
3933          * @event out
3934          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3935          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3936          * overClass (if any) from the drop element.
3937          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3938          * @param {Event} e The event
3939          * @param {Object} data An object containing arbitrary data supplied by the drag source
3940          */
3941          "out" : true,
3942          
3943         /**
3944          * @event drop
3945          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3946          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3947          * implementation that does something to process the drop event and returns true so that the drag source's
3948          * repair action does not run.
3949          * 
3950          * IMPORTANT : it should set this.success
3951          * 
3952          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3953          * @param {Event} e The event
3954          * @param {Object} data An object containing arbitrary data supplied by the drag source
3955         */
3956          "drop" : true
3957     });
3958             
3959      
3960     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3961         this.el.dom, 
3962         this.ddGroup || this.group,
3963         {
3964             isTarget: true,
3965             listeners : config.listeners || {} 
3966            
3967         
3968         }
3969     );
3970
3971 };
3972
3973 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
3974     /**
3975      * @cfg {String} overClass
3976      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
3977      */
3978      /**
3979      * @cfg {String} ddGroup
3980      * The drag drop group to handle drop events for
3981      */
3982      
3983     /**
3984      * @cfg {String} dropAllowed
3985      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3986      */
3987     dropAllowed : "x-dd-drop-ok",
3988     /**
3989      * @cfg {String} dropNotAllowed
3990      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3991      */
3992     dropNotAllowed : "x-dd-drop-nodrop",
3993     /**
3994      * @cfg {boolean} success
3995      * set this after drop listener.. 
3996      */
3997     success : false,
3998     /**
3999      * @cfg {boolean} valid
4000      * if the drop point is valid for over/enter..
4001      */
4002     valid : false,
4003     // private
4004     isTarget : true,
4005
4006     // private
4007     isNotifyTarget : true,
4008     
4009     /**
4010      * @hide
4011      */
4012     notifyEnter : function(dd, e, data){
4013         this.valid = true;
4014         this.fireEvent('enter', this, dd, e, data);
4015         if(this.overClass){
4016             this.el.addClass(this.overClass);
4017         }
4018         return this.valid ? this.dropAllowed : this.dropNotAllowed;
4019     },
4020
4021     /**
4022      * @hide
4023      */
4024     notifyOver : function(dd, e, data){
4025         this.valid = true;
4026         this.fireEvent('over', this, dd, e, data);
4027         return this.valid ? this.dropAllowed : this.dropNotAllowed;
4028     },
4029
4030     /**
4031      * @hide
4032      */
4033     notifyOut : function(dd, e, data){
4034         this.fireEvent('out', this, dd, e, data);
4035         if(this.overClass){
4036             this.el.removeClass(this.overClass);
4037         }
4038     },
4039
4040     /**
4041      * @hide
4042      */
4043     notifyDrop : function(dd, e, data){
4044         this.success = false;
4045         this.fireEvent('drop', this, dd, e, data);
4046         return this.success;
4047     }
4048 });/*
4049  * Based on:
4050  * Ext JS Library 1.1.1
4051  * Copyright(c) 2006-2007, Ext JS, LLC.
4052  *
4053  * Originally Released Under LGPL - original licence link has changed is not relivant.
4054  *
4055  * Fork - LGPL
4056  * <script type="text/javascript">
4057  */
4058
4059
4060 /**
4061  * @class Roo.dd.DragZone
4062  * @extends Roo.dd.DragSource
4063  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4064  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4065  * @constructor
4066  * @param {String/HTMLElement/Element} el The container element
4067  * @param {Object} config
4068  */
4069 Roo.dd.DragZone = function(el, config){
4070     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4071     if(this.containerScroll){
4072         Roo.dd.ScrollManager.register(this.el);
4073     }
4074 };
4075
4076 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4077     /**
4078      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4079      * for auto scrolling during drag operations.
4080      */
4081     /**
4082      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4083      * method after a failed drop (defaults to "c3daf9" - light blue)
4084      */
4085
4086     /**
4087      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4088      * for a valid target to drag based on the mouse down. Override this method
4089      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4090      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4091      * @param {EventObject} e The mouse down event
4092      * @return {Object} The dragData
4093      */
4094     getDragData : function(e){
4095         return Roo.dd.Registry.getHandleFromEvent(e);
4096     },
4097     
4098     /**
4099      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4100      * this.dragData.ddel
4101      * @param {Number} x The x position of the click on the dragged object
4102      * @param {Number} y The y position of the click on the dragged object
4103      * @return {Boolean} true to continue the drag, false to cancel
4104      */
4105     onInitDrag : function(x, y){
4106         this.proxy.update(this.dragData.ddel.cloneNode(true));
4107         this.onStartDrag(x, y);
4108         return true;
4109     },
4110     
4111     /**
4112      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4113      */
4114     afterRepair : function(){
4115         if(Roo.enableFx){
4116             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4117         }
4118         this.dragging = false;
4119     },
4120
4121     /**
4122      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4123      * the XY of this.dragData.ddel
4124      * @param {EventObject} e The mouse up event
4125      * @return {Array} The xy location (e.g. [100, 200])
4126      */
4127     getRepairXY : function(e){
4128         return Roo.Element.fly(this.dragData.ddel).getXY();  
4129     }
4130 });/*
4131  * Based on:
4132  * Ext JS Library 1.1.1
4133  * Copyright(c) 2006-2007, Ext JS, LLC.
4134  *
4135  * Originally Released Under LGPL - original licence link has changed is not relivant.
4136  *
4137  * Fork - LGPL
4138  * <script type="text/javascript">
4139  */
4140 /**
4141  * @class Roo.dd.DropZone
4142  * @extends Roo.dd.DropTarget
4143  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4144  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4145  * @constructor
4146  * @param {String/HTMLElement/Element} el The container element
4147  * @param {Object} config
4148  */
4149 Roo.dd.DropZone = function(el, config){
4150     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4151 };
4152
4153 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4154     /**
4155      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4156      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4157      * provide your own custom lookup.
4158      * @param {Event} e The event
4159      * @return {Object} data The custom data
4160      */
4161     getTargetFromEvent : function(e){
4162         return Roo.dd.Registry.getTargetFromEvent(e);
4163     },
4164
4165     /**
4166      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4167      * that it has registered.  This method has no default implementation and should be overridden to provide
4168      * node-specific processing if necessary.
4169      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4170      * {@link #getTargetFromEvent} for this node)
4171      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4172      * @param {Event} e The event
4173      * @param {Object} data An object containing arbitrary data supplied by the drag source
4174      */
4175     onNodeEnter : function(n, dd, e, data){
4176         
4177     },
4178
4179     /**
4180      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4181      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4182      * overridden to provide the proper feedback.
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      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4189      * underlying {@link Roo.dd.StatusProxy} can be updated
4190      */
4191     onNodeOver : function(n, dd, e, data){
4192         return this.dropAllowed;
4193     },
4194
4195     /**
4196      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4197      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4198      * node-specific processing if necessary.
4199      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4200      * {@link #getTargetFromEvent} for this node)
4201      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4202      * @param {Event} e The event
4203      * @param {Object} data An object containing arbitrary data supplied by the drag source
4204      */
4205     onNodeOut : function(n, dd, e, data){
4206         
4207     },
4208
4209     /**
4210      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4211      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4212      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
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      * @return {Boolean} True if the drop was valid, else false
4219      */
4220     onNodeDrop : function(n, dd, e, data){
4221         return false;
4222     },
4223
4224     /**
4225      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4226      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4227      * it should be overridden to provide the proper feedback if necessary.
4228      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4229      * @param {Event} e The event
4230      * @param {Object} data An object containing arbitrary data supplied by the drag source
4231      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4232      * underlying {@link Roo.dd.StatusProxy} can be updated
4233      */
4234     onContainerOver : function(dd, e, data){
4235         return this.dropNotAllowed;
4236     },
4237
4238     /**
4239      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4240      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4241      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4242      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4243      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4244      * @param {Event} e The event
4245      * @param {Object} data An object containing arbitrary data supplied by the drag source
4246      * @return {Boolean} True if the drop was valid, else false
4247      */
4248     onContainerDrop : function(dd, e, data){
4249         return false;
4250     },
4251
4252     /**
4253      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4254      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4255      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4256      * you should override this method and provide a custom implementation.
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 {String} status The CSS class that communicates the drop status back to the source so that the
4261      * underlying {@link Roo.dd.StatusProxy} can be updated
4262      */
4263     notifyEnter : function(dd, e, data){
4264         return this.dropNotAllowed;
4265     },
4266
4267     /**
4268      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4269      * This method will be called on every mouse movement while the drag source is over the drop zone.
4270      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4271      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4272      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4273      * registered node, it will call {@link #onContainerOver}.
4274      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4275      * @param {Event} e The event
4276      * @param {Object} data An object containing arbitrary data supplied by the drag source
4277      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4278      * underlying {@link Roo.dd.StatusProxy} can be updated
4279      */
4280     notifyOver : function(dd, e, data){
4281         var n = this.getTargetFromEvent(e);
4282         if(!n){ // not over valid drop target
4283             if(this.lastOverNode){
4284                 this.onNodeOut(this.lastOverNode, dd, e, data);
4285                 this.lastOverNode = null;
4286             }
4287             return this.onContainerOver(dd, e, data);
4288         }
4289         if(this.lastOverNode != n){
4290             if(this.lastOverNode){
4291                 this.onNodeOut(this.lastOverNode, dd, e, data);
4292             }
4293             this.onNodeEnter(n, dd, e, data);
4294             this.lastOverNode = n;
4295         }
4296         return this.onNodeOver(n, dd, e, data);
4297     },
4298
4299     /**
4300      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4301      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4302      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4303      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4304      * @param {Event} e The event
4305      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4306      */
4307     notifyOut : function(dd, e, data){
4308         if(this.lastOverNode){
4309             this.onNodeOut(this.lastOverNode, dd, e, data);
4310             this.lastOverNode = null;
4311         }
4312     },
4313
4314     /**
4315      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4316      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4317      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4318      * otherwise it will call {@link #onContainerDrop}.
4319      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4320      * @param {Event} e The event
4321      * @param {Object} data An object containing arbitrary data supplied by the drag source
4322      * @return {Boolean} True if the drop was valid, else false
4323      */
4324     notifyDrop : function(dd, e, data){
4325         if(this.lastOverNode){
4326             this.onNodeOut(this.lastOverNode, dd, e, data);
4327             this.lastOverNode = null;
4328         }
4329         var n = this.getTargetFromEvent(e);
4330         return n ?
4331             this.onNodeDrop(n, dd, e, data) :
4332             this.onContainerDrop(dd, e, data);
4333     },
4334
4335     // private
4336     triggerCacheRefresh : function(){
4337         Roo.dd.DDM.refreshCache(this.groups);
4338     }  
4339 });/*
4340  * Based on:
4341  * Ext JS Library 1.1.1
4342  * Copyright(c) 2006-2007, Ext JS, LLC.
4343  *
4344  * Originally Released Under LGPL - original licence link has changed is not relivant.
4345  *
4346  * Fork - LGPL
4347  * <script type="text/javascript">
4348  */
4349
4350
4351 /**
4352  * @class Roo.data.SortTypes
4353  * @singleton
4354  * Defines the default sorting (casting?) comparison functions used when sorting data.
4355  */
4356 Roo.data.SortTypes = {
4357     /**
4358      * Default sort that does nothing
4359      * @param {Mixed} s The value being converted
4360      * @return {Mixed} The comparison value
4361      */
4362     none : function(s){
4363         return s;
4364     },
4365     
4366     /**
4367      * The regular expression used to strip tags
4368      * @type {RegExp}
4369      * @property
4370      */
4371     stripTagsRE : /<\/?[^>]+>/gi,
4372     
4373     /**
4374      * Strips all HTML tags to sort on text only
4375      * @param {Mixed} s The value being converted
4376      * @return {String} The comparison value
4377      */
4378     asText : function(s){
4379         return String(s).replace(this.stripTagsRE, "");
4380     },
4381     
4382     /**
4383      * Strips all HTML tags to sort on text only - Case insensitive
4384      * @param {Mixed} s The value being converted
4385      * @return {String} The comparison value
4386      */
4387     asUCText : function(s){
4388         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4389     },
4390     
4391     /**
4392      * Case insensitive string
4393      * @param {Mixed} s The value being converted
4394      * @return {String} The comparison value
4395      */
4396     asUCString : function(s) {
4397         return String(s).toUpperCase();
4398     },
4399     
4400     /**
4401      * Date sorting
4402      * @param {Mixed} s The value being converted
4403      * @return {Number} The comparison value
4404      */
4405     asDate : function(s) {
4406         if(!s){
4407             return 0;
4408         }
4409         if(s instanceof Date){
4410             return s.getTime();
4411         }
4412         return Date.parse(String(s));
4413     },
4414     
4415     /**
4416      * Float sorting
4417      * @param {Mixed} s The value being converted
4418      * @return {Float} The comparison value
4419      */
4420     asFloat : function(s) {
4421         var val = parseFloat(String(s).replace(/,/g, ""));
4422         if(isNaN(val)) val = 0;
4423         return val;
4424     },
4425     
4426     /**
4427      * Integer sorting
4428      * @param {Mixed} s The value being converted
4429      * @return {Number} The comparison value
4430      */
4431     asInt : function(s) {
4432         var val = parseInt(String(s).replace(/,/g, ""));
4433         if(isNaN(val)) val = 0;
4434         return val;
4435     }
4436 };/*
4437  * Based on:
4438  * Ext JS Library 1.1.1
4439  * Copyright(c) 2006-2007, Ext JS, LLC.
4440  *
4441  * Originally Released Under LGPL - original licence link has changed is not relivant.
4442  *
4443  * Fork - LGPL
4444  * <script type="text/javascript">
4445  */
4446
4447 /**
4448 * @class Roo.data.Record
4449  * Instances of this class encapsulate both record <em>definition</em> information, and record
4450  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4451  * to access Records cached in an {@link Roo.data.Store} object.<br>
4452  * <p>
4453  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4454  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4455  * objects.<br>
4456  * <p>
4457  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4458  * @constructor
4459  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4460  * {@link #create}. The parameters are the same.
4461  * @param {Array} data An associative Array of data values keyed by the field name.
4462  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4463  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4464  * not specified an integer id is generated.
4465  */
4466 Roo.data.Record = function(data, id){
4467     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4468     this.data = data;
4469 };
4470
4471 /**
4472  * Generate a constructor for a specific record layout.
4473  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4474  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4475  * Each field definition object may contain the following properties: <ul>
4476  * <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,
4477  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4478  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4479  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4480  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4481  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4482  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4483  * this may be omitted.</p></li>
4484  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4485  * <ul><li>auto (Default, implies no conversion)</li>
4486  * <li>string</li>
4487  * <li>int</li>
4488  * <li>float</li>
4489  * <li>boolean</li>
4490  * <li>date</li></ul></p></li>
4491  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4492  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4493  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4494  * by the Reader into an object that will be stored in the Record. It is passed the
4495  * following parameters:<ul>
4496  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4497  * </ul></p></li>
4498  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4499  * </ul>
4500  * <br>usage:<br><pre><code>
4501 var TopicRecord = Roo.data.Record.create(
4502     {name: 'title', mapping: 'topic_title'},
4503     {name: 'author', mapping: 'username'},
4504     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4505     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4506     {name: 'lastPoster', mapping: 'user2'},
4507     {name: 'excerpt', mapping: 'post_text'}
4508 );
4509
4510 var myNewRecord = new TopicRecord({
4511     title: 'Do my job please',
4512     author: 'noobie',
4513     totalPosts: 1,
4514     lastPost: new Date(),
4515     lastPoster: 'Animal',
4516     excerpt: 'No way dude!'
4517 });
4518 myStore.add(myNewRecord);
4519 </code></pre>
4520  * @method create
4521  * @static
4522  */
4523 Roo.data.Record.create = function(o){
4524     var f = function(){
4525         f.superclass.constructor.apply(this, arguments);
4526     };
4527     Roo.extend(f, Roo.data.Record);
4528     var p = f.prototype;
4529     p.fields = new Roo.util.MixedCollection(false, function(field){
4530         return field.name;
4531     });
4532     for(var i = 0, len = o.length; i < len; i++){
4533         p.fields.add(new Roo.data.Field(o[i]));
4534     }
4535     f.getField = function(name){
4536         return p.fields.get(name);  
4537     };
4538     return f;
4539 };
4540
4541 Roo.data.Record.AUTO_ID = 1000;
4542 Roo.data.Record.EDIT = 'edit';
4543 Roo.data.Record.REJECT = 'reject';
4544 Roo.data.Record.COMMIT = 'commit';
4545
4546 Roo.data.Record.prototype = {
4547     /**
4548      * Readonly flag - true if this record has been modified.
4549      * @type Boolean
4550      */
4551     dirty : false,
4552     editing : false,
4553     error: null,
4554     modified: null,
4555
4556     // private
4557     join : function(store){
4558         this.store = store;
4559     },
4560
4561     /**
4562      * Set the named field to the specified value.
4563      * @param {String} name The name of the field to set.
4564      * @param {Object} value The value to set the field to.
4565      */
4566     set : function(name, value){
4567         if(this.data[name] == value){
4568             return;
4569         }
4570         this.dirty = true;
4571         if(!this.modified){
4572             this.modified = {};
4573         }
4574         if(typeof this.modified[name] == 'undefined'){
4575             this.modified[name] = this.data[name];
4576         }
4577         this.data[name] = value;
4578         if(!this.editing){
4579             this.store.afterEdit(this);
4580         }       
4581     },
4582
4583     /**
4584      * Get the value of the named field.
4585      * @param {String} name The name of the field to get the value of.
4586      * @return {Object} The value of the field.
4587      */
4588     get : function(name){
4589         return this.data[name]; 
4590     },
4591
4592     // private
4593     beginEdit : function(){
4594         this.editing = true;
4595         this.modified = {}; 
4596     },
4597
4598     // private
4599     cancelEdit : function(){
4600         this.editing = false;
4601         delete this.modified;
4602     },
4603
4604     // private
4605     endEdit : function(){
4606         this.editing = false;
4607         if(this.dirty && this.store){
4608             this.store.afterEdit(this);
4609         }
4610     },
4611
4612     /**
4613      * Usually called by the {@link Roo.data.Store} which owns the Record.
4614      * Rejects all changes made to the Record since either creation, or the last commit operation.
4615      * Modified fields are reverted to their original values.
4616      * <p>
4617      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4618      * of reject operations.
4619      */
4620     reject : function(){
4621         var m = this.modified;
4622         for(var n in m){
4623             if(typeof m[n] != "function"){
4624                 this.data[n] = m[n];
4625             }
4626         }
4627         this.dirty = false;
4628         delete this.modified;
4629         this.editing = false;
4630         if(this.store){
4631             this.store.afterReject(this);
4632         }
4633     },
4634
4635     /**
4636      * Usually called by the {@link Roo.data.Store} which owns the Record.
4637      * Commits all changes made to the Record since either creation, or the last commit operation.
4638      * <p>
4639      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4640      * of commit operations.
4641      */
4642     commit : function(){
4643         this.dirty = false;
4644         delete this.modified;
4645         this.editing = false;
4646         if(this.store){
4647             this.store.afterCommit(this);
4648         }
4649     },
4650
4651     // private
4652     hasError : function(){
4653         return this.error != null;
4654     },
4655
4656     // private
4657     clearError : function(){
4658         this.error = null;
4659     },
4660
4661     /**
4662      * Creates a copy of this record.
4663      * @param {String} id (optional) A new record id if you don't want to use this record's id
4664      * @return {Record}
4665      */
4666     copy : function(newId) {
4667         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4668     }
4669 };/*
4670  * Based on:
4671  * Ext JS Library 1.1.1
4672  * Copyright(c) 2006-2007, Ext JS, LLC.
4673  *
4674  * Originally Released Under LGPL - original licence link has changed is not relivant.
4675  *
4676  * Fork - LGPL
4677  * <script type="text/javascript">
4678  */
4679
4680
4681
4682 /**
4683  * @class Roo.data.Store
4684  * @extends Roo.util.Observable
4685  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4686  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4687  * <p>
4688  * 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
4689  * has no knowledge of the format of the data returned by the Proxy.<br>
4690  * <p>
4691  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4692  * instances from the data object. These records are cached and made available through accessor functions.
4693  * @constructor
4694  * Creates a new Store.
4695  * @param {Object} config A config object containing the objects needed for the Store to access data,
4696  * and read the data into Records.
4697  */
4698 Roo.data.Store = function(config){
4699     this.data = new Roo.util.MixedCollection(false);
4700     this.data.getKey = function(o){
4701         return o.id;
4702     };
4703     this.baseParams = {};
4704     // private
4705     this.paramNames = {
4706         "start" : "start",
4707         "limit" : "limit",
4708         "sort" : "sort",
4709         "dir" : "dir"
4710     };
4711
4712     if(config && config.data){
4713         this.inlineData = config.data;
4714         delete config.data;
4715     }
4716
4717     Roo.apply(this, config);
4718     
4719     if(this.reader){ // reader passed
4720         this.reader = Roo.factory(this.reader, Roo.data);
4721         this.reader.xmodule = this.xmodule || false;
4722         if(!this.recordType){
4723             this.recordType = this.reader.recordType;
4724         }
4725         if(this.reader.onMetaChange){
4726             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4727         }
4728     }
4729
4730     if(this.recordType){
4731         this.fields = this.recordType.prototype.fields;
4732     }
4733     this.modified = [];
4734
4735     this.addEvents({
4736         /**
4737          * @event datachanged
4738          * Fires when the data cache has changed, and a widget which is using this Store
4739          * as a Record cache should refresh its view.
4740          * @param {Store} this
4741          */
4742         datachanged : true,
4743         /**
4744          * @event metachange
4745          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4746          * @param {Store} this
4747          * @param {Object} meta The JSON metadata
4748          */
4749         metachange : true,
4750         /**
4751          * @event add
4752          * Fires when Records have been added to the Store
4753          * @param {Store} this
4754          * @param {Roo.data.Record[]} records The array of Records added
4755          * @param {Number} index The index at which the record(s) were added
4756          */
4757         add : true,
4758         /**
4759          * @event remove
4760          * Fires when a Record has been removed from the Store
4761          * @param {Store} this
4762          * @param {Roo.data.Record} record The Record that was removed
4763          * @param {Number} index The index at which the record was removed
4764          */
4765         remove : true,
4766         /**
4767          * @event update
4768          * Fires when a Record has been updated
4769          * @param {Store} this
4770          * @param {Roo.data.Record} record The Record that was updated
4771          * @param {String} operation The update operation being performed.  Value may be one of:
4772          * <pre><code>
4773  Roo.data.Record.EDIT
4774  Roo.data.Record.REJECT
4775  Roo.data.Record.COMMIT
4776          * </code></pre>
4777          */
4778         update : true,
4779         /**
4780          * @event clear
4781          * Fires when the data cache has been cleared.
4782          * @param {Store} this
4783          */
4784         clear : true,
4785         /**
4786          * @event beforeload
4787          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4788          * the load action will be canceled.
4789          * @param {Store} this
4790          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4791          */
4792         beforeload : true,
4793         /**
4794          * @event load
4795          * Fires after a new set of Records has been loaded.
4796          * @param {Store} this
4797          * @param {Roo.data.Record[]} records The Records that were loaded
4798          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4799          */
4800         load : true,
4801         /**
4802          * @event loadexception
4803          * Fires if an exception occurs in the Proxy during loading.
4804          * Called with the signature of the Proxy's "loadexception" event.
4805          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4806          * 
4807          * @param {Proxy} 
4808          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4809          * @param {Object} load options 
4810          * @param {Object} jsonData from your request (normally this contains the Exception)
4811          */
4812         loadexception : true
4813     });
4814     
4815     if(this.proxy){
4816         this.proxy = Roo.factory(this.proxy, Roo.data);
4817         this.proxy.xmodule = this.xmodule || false;
4818         this.relayEvents(this.proxy,  ["loadexception"]);
4819     }
4820     this.sortToggle = {};
4821
4822     Roo.data.Store.superclass.constructor.call(this);
4823
4824     if(this.inlineData){
4825         this.loadData(this.inlineData);
4826         delete this.inlineData;
4827     }
4828 };
4829 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4830      /**
4831     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4832     * without a remote query - used by combo/forms at present.
4833     */
4834     
4835     /**
4836     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4837     */
4838     /**
4839     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4840     */
4841     /**
4842     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4843     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4844     */
4845     /**
4846     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4847     * on any HTTP request
4848     */
4849     /**
4850     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4851     */
4852     /**
4853     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4854     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4855     */
4856     remoteSort : false,
4857
4858     /**
4859     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4860      * loaded or when a record is removed. (defaults to false).
4861     */
4862     pruneModifiedRecords : false,
4863
4864     // private
4865     lastOptions : null,
4866
4867     /**
4868      * Add Records to the Store and fires the add event.
4869      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4870      */
4871     add : function(records){
4872         records = [].concat(records);
4873         for(var i = 0, len = records.length; i < len; i++){
4874             records[i].join(this);
4875         }
4876         var index = this.data.length;
4877         this.data.addAll(records);
4878         this.fireEvent("add", this, records, index);
4879     },
4880
4881     /**
4882      * Remove a Record from the Store and fires the remove event.
4883      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4884      */
4885     remove : function(record){
4886         var index = this.data.indexOf(record);
4887         this.data.removeAt(index);
4888         if(this.pruneModifiedRecords){
4889             this.modified.remove(record);
4890         }
4891         this.fireEvent("remove", this, record, index);
4892     },
4893
4894     /**
4895      * Remove all Records from the Store and fires the clear event.
4896      */
4897     removeAll : function(){
4898         this.data.clear();
4899         if(this.pruneModifiedRecords){
4900             this.modified = [];
4901         }
4902         this.fireEvent("clear", this);
4903     },
4904
4905     /**
4906      * Inserts Records to the Store at the given index and fires the add event.
4907      * @param {Number} index The start index at which to insert the passed Records.
4908      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4909      */
4910     insert : function(index, records){
4911         records = [].concat(records);
4912         for(var i = 0, len = records.length; i < len; i++){
4913             this.data.insert(index, records[i]);
4914             records[i].join(this);
4915         }
4916         this.fireEvent("add", this, records, index);
4917     },
4918
4919     /**
4920      * Get the index within the cache of the passed Record.
4921      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4922      * @return {Number} The index of the passed Record. Returns -1 if not found.
4923      */
4924     indexOf : function(record){
4925         return this.data.indexOf(record);
4926     },
4927
4928     /**
4929      * Get the index within the cache of the Record with the passed id.
4930      * @param {String} id The id of the Record to find.
4931      * @return {Number} The index of the Record. Returns -1 if not found.
4932      */
4933     indexOfId : function(id){
4934         return this.data.indexOfKey(id);
4935     },
4936
4937     /**
4938      * Get the Record with the specified id.
4939      * @param {String} id The id of the Record to find.
4940      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4941      */
4942     getById : function(id){
4943         return this.data.key(id);
4944     },
4945
4946     /**
4947      * Get the Record at the specified index.
4948      * @param {Number} index The index of the Record to find.
4949      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4950      */
4951     getAt : function(index){
4952         return this.data.itemAt(index);
4953     },
4954
4955     /**
4956      * Returns a range of Records between specified indices.
4957      * @param {Number} startIndex (optional) The starting index (defaults to 0)
4958      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
4959      * @return {Roo.data.Record[]} An array of Records
4960      */
4961     getRange : function(start, end){
4962         return this.data.getRange(start, end);
4963     },
4964
4965     // private
4966     storeOptions : function(o){
4967         o = Roo.apply({}, o);
4968         delete o.callback;
4969         delete o.scope;
4970         this.lastOptions = o;
4971     },
4972
4973     /**
4974      * Loads the Record cache from the configured Proxy using the configured Reader.
4975      * <p>
4976      * If using remote paging, then the first load call must specify the <em>start</em>
4977      * and <em>limit</em> properties in the options.params property to establish the initial
4978      * position within the dataset, and the number of Records to cache on each read from the Proxy.
4979      * <p>
4980      * <strong>It is important to note that for remote data sources, loading is asynchronous,
4981      * and this call will return before the new data has been loaded. Perform any post-processing
4982      * in a callback function, or in a "load" event handler.</strong>
4983      * <p>
4984      * @param {Object} options An object containing properties which control loading options:<ul>
4985      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
4986      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
4987      * passed the following arguments:<ul>
4988      * <li>r : Roo.data.Record[]</li>
4989      * <li>options: Options object from the load call</li>
4990      * <li>success: Boolean success indicator</li></ul></li>
4991      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
4992      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
4993      * </ul>
4994      */
4995     load : function(options){
4996         options = options || {};
4997         if(this.fireEvent("beforeload", this, options) !== false){
4998             this.storeOptions(options);
4999             var p = Roo.apply(options.params || {}, this.baseParams);
5000             // if meta was not loaded from remote source.. try requesting it.
5001             if (!this.reader.metaFromRemote) {
5002                 p._requestMeta = 1;
5003             }
5004             if(this.sortInfo && this.remoteSort){
5005                 var pn = this.paramNames;
5006                 p[pn["sort"]] = this.sortInfo.field;
5007                 p[pn["dir"]] = this.sortInfo.direction;
5008             }
5009             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5010         }
5011     },
5012
5013     /**
5014      * Reloads the Record cache from the configured Proxy using the configured Reader and
5015      * the options from the last load operation performed.
5016      * @param {Object} options (optional) An object containing properties which may override the options
5017      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5018      * the most recently used options are reused).
5019      */
5020     reload : function(options){
5021         this.load(Roo.applyIf(options||{}, this.lastOptions));
5022     },
5023
5024     // private
5025     // Called as a callback by the Reader during a load operation.
5026     loadRecords : function(o, options, success){
5027         if(!o || success === false){
5028             if(success !== false){
5029                 this.fireEvent("load", this, [], options);
5030             }
5031             if(options.callback){
5032                 options.callback.call(options.scope || this, [], options, false);
5033             }
5034             return;
5035         }
5036         // if data returned failure - throw an exception.
5037         if (o.success === false) {
5038             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
5039             return;
5040         }
5041         var r = o.records, t = o.totalRecords || r.length;
5042         if(!options || options.add !== true){
5043             if(this.pruneModifiedRecords){
5044                 this.modified = [];
5045             }
5046             for(var i = 0, len = r.length; i < len; i++){
5047                 r[i].join(this);
5048             }
5049             if(this.snapshot){
5050                 this.data = this.snapshot;
5051                 delete this.snapshot;
5052             }
5053             this.data.clear();
5054             this.data.addAll(r);
5055             this.totalLength = t;
5056             this.applySort();
5057             this.fireEvent("datachanged", this);
5058         }else{
5059             this.totalLength = Math.max(t, this.data.length+r.length);
5060             this.add(r);
5061         }
5062         this.fireEvent("load", this, r, options);
5063         if(options.callback){
5064             options.callback.call(options.scope || this, r, options, true);
5065         }
5066     },
5067
5068     /**
5069      * Loads data from a passed data block. A Reader which understands the format of the data
5070      * must have been configured in the constructor.
5071      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5072      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5073      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5074      */
5075     loadData : function(o, append){
5076         var r = this.reader.readRecords(o);
5077         this.loadRecords(r, {add: append}, true);
5078     },
5079
5080     /**
5081      * Gets the number of cached records.
5082      * <p>
5083      * <em>If using paging, this may not be the total size of the dataset. If the data object
5084      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5085      * the data set size</em>
5086      */
5087     getCount : function(){
5088         return this.data.length || 0;
5089     },
5090
5091     /**
5092      * Gets the total number of records in the dataset as returned by the server.
5093      * <p>
5094      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5095      * the dataset size</em>
5096      */
5097     getTotalCount : function(){
5098         return this.totalLength || 0;
5099     },
5100
5101     /**
5102      * Returns the sort state of the Store as an object with two properties:
5103      * <pre><code>
5104  field {String} The name of the field by which the Records are sorted
5105  direction {String} The sort order, "ASC" or "DESC"
5106      * </code></pre>
5107      */
5108     getSortState : function(){
5109         return this.sortInfo;
5110     },
5111
5112     // private
5113     applySort : function(){
5114         if(this.sortInfo && !this.remoteSort){
5115             var s = this.sortInfo, f = s.field;
5116             var st = this.fields.get(f).sortType;
5117             var fn = function(r1, r2){
5118                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5119                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5120             };
5121             this.data.sort(s.direction, fn);
5122             if(this.snapshot && this.snapshot != this.data){
5123                 this.snapshot.sort(s.direction, fn);
5124             }
5125         }
5126     },
5127
5128     /**
5129      * Sets the default sort column and order to be used by the next load operation.
5130      * @param {String} fieldName The name of the field to sort by.
5131      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5132      */
5133     setDefaultSort : function(field, dir){
5134         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5135     },
5136
5137     /**
5138      * Sort the Records.
5139      * If remote sorting is used, the sort is performed on the server, and the cache is
5140      * reloaded. If local sorting is used, the cache is sorted internally.
5141      * @param {String} fieldName The name of the field to sort by.
5142      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5143      */
5144     sort : function(fieldName, dir){
5145         var f = this.fields.get(fieldName);
5146         if(!dir){
5147             if(this.sortInfo && this.sortInfo.field == f.name){ // toggle sort dir
5148                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5149             }else{
5150                 dir = f.sortDir;
5151             }
5152         }
5153         this.sortToggle[f.name] = dir;
5154         this.sortInfo = {field: f.name, direction: dir};
5155         if(!this.remoteSort){
5156             this.applySort();
5157             this.fireEvent("datachanged", this);
5158         }else{
5159             this.load(this.lastOptions);
5160         }
5161     },
5162
5163     /**
5164      * Calls the specified function for each of the Records in the cache.
5165      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5166      * Returning <em>false</em> aborts and exits the iteration.
5167      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5168      */
5169     each : function(fn, scope){
5170         this.data.each(fn, scope);
5171     },
5172
5173     /**
5174      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5175      * (e.g., during paging).
5176      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5177      */
5178     getModifiedRecords : function(){
5179         return this.modified;
5180     },
5181
5182     // private
5183     createFilterFn : function(property, value, anyMatch){
5184         if(!value.exec){ // not a regex
5185             value = String(value);
5186             if(value.length == 0){
5187                 return false;
5188             }
5189             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5190         }
5191         return function(r){
5192             return value.test(r.data[property]);
5193         };
5194     },
5195
5196     /**
5197      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5198      * @param {String} property A field on your records
5199      * @param {Number} start The record index to start at (defaults to 0)
5200      * @param {Number} end The last record index to include (defaults to length - 1)
5201      * @return {Number} The sum
5202      */
5203     sum : function(property, start, end){
5204         var rs = this.data.items, v = 0;
5205         start = start || 0;
5206         end = (end || end === 0) ? end : rs.length-1;
5207
5208         for(var i = start; i <= end; i++){
5209             v += (rs[i].data[property] || 0);
5210         }
5211         return v;
5212     },
5213
5214     /**
5215      * Filter the records by a specified property.
5216      * @param {String} field A field on your records
5217      * @param {String/RegExp} value Either a string that the field
5218      * should start with or a RegExp to test against the field
5219      * @param {Boolean} anyMatch True to match any part not just the beginning
5220      */
5221     filter : function(property, value, anyMatch){
5222         var fn = this.createFilterFn(property, value, anyMatch);
5223         return fn ? this.filterBy(fn) : this.clearFilter();
5224     },
5225
5226     /**
5227      * Filter by a function. The specified function will be called with each
5228      * record in this data source. If the function returns true the record is included,
5229      * otherwise it is filtered.
5230      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5231      * @param {Object} scope (optional) The scope of the function (defaults to this)
5232      */
5233     filterBy : function(fn, scope){
5234         this.snapshot = this.snapshot || this.data;
5235         this.data = this.queryBy(fn, scope||this);
5236         this.fireEvent("datachanged", this);
5237     },
5238
5239     /**
5240      * Query the records by a specified property.
5241      * @param {String} field A field on your records
5242      * @param {String/RegExp} value Either a string that the field
5243      * should start with or a RegExp to test against the field
5244      * @param {Boolean} anyMatch True to match any part not just the beginning
5245      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5246      */
5247     query : function(property, value, anyMatch){
5248         var fn = this.createFilterFn(property, value, anyMatch);
5249         return fn ? this.queryBy(fn) : this.data.clone();
5250     },
5251
5252     /**
5253      * Query by a function. The specified function will be called with each
5254      * record in this data source. If the function returns true the record is included
5255      * in the results.
5256      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5257      * @param {Object} scope (optional) The scope of the function (defaults to this)
5258       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5259      **/
5260     queryBy : function(fn, scope){
5261         var data = this.snapshot || this.data;
5262         return data.filterBy(fn, scope||this);
5263     },
5264
5265     /**
5266      * Collects unique values for a particular dataIndex from this store.
5267      * @param {String} dataIndex The property to collect
5268      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5269      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5270      * @return {Array} An array of the unique values
5271      **/
5272     collect : function(dataIndex, allowNull, bypassFilter){
5273         var d = (bypassFilter === true && this.snapshot) ?
5274                 this.snapshot.items : this.data.items;
5275         var v, sv, r = [], l = {};
5276         for(var i = 0, len = d.length; i < len; i++){
5277             v = d[i].data[dataIndex];
5278             sv = String(v);
5279             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5280                 l[sv] = true;
5281                 r[r.length] = v;
5282             }
5283         }
5284         return r;
5285     },
5286
5287     /**
5288      * Revert to a view of the Record cache with no filtering applied.
5289      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5290      */
5291     clearFilter : function(suppressEvent){
5292         if(this.snapshot && this.snapshot != this.data){
5293             this.data = this.snapshot;
5294             delete this.snapshot;
5295             if(suppressEvent !== true){
5296                 this.fireEvent("datachanged", this);
5297             }
5298         }
5299     },
5300
5301     // private
5302     afterEdit : function(record){
5303         if(this.modified.indexOf(record) == -1){
5304             this.modified.push(record);
5305         }
5306         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5307     },
5308
5309     // private
5310     afterReject : function(record){
5311         this.modified.remove(record);
5312         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5313     },
5314
5315     // private
5316     afterCommit : function(record){
5317         this.modified.remove(record);
5318         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5319     },
5320
5321     /**
5322      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5323      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5324      */
5325     commitChanges : function(){
5326         var m = this.modified.slice(0);
5327         this.modified = [];
5328         for(var i = 0, len = m.length; i < len; i++){
5329             m[i].commit();
5330         }
5331     },
5332
5333     /**
5334      * Cancel outstanding changes on all changed records.
5335      */
5336     rejectChanges : function(){
5337         var m = this.modified.slice(0);
5338         this.modified = [];
5339         for(var i = 0, len = m.length; i < len; i++){
5340             m[i].reject();
5341         }
5342     },
5343
5344     onMetaChange : function(meta, rtype, o){
5345         this.recordType = rtype;
5346         this.fields = rtype.prototype.fields;
5347         delete this.snapshot;
5348         this.sortInfo = meta.sortInfo || this.sortInfo;
5349         this.modified = [];
5350         this.fireEvent('metachange', this, this.reader.meta);
5351     }
5352 });/*
5353  * Based on:
5354  * Ext JS Library 1.1.1
5355  * Copyright(c) 2006-2007, Ext JS, LLC.
5356  *
5357  * Originally Released Under LGPL - original licence link has changed is not relivant.
5358  *
5359  * Fork - LGPL
5360  * <script type="text/javascript">
5361  */
5362
5363 /**
5364  * @class Roo.data.SimpleStore
5365  * @extends Roo.data.Store
5366  * Small helper class to make creating Stores from Array data easier.
5367  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5368  * @cfg {Array} fields An array of field definition objects, or field name strings.
5369  * @cfg {Array} data The multi-dimensional array of data
5370  * @constructor
5371  * @param {Object} config
5372  */
5373 Roo.data.SimpleStore = function(config){
5374     Roo.data.SimpleStore.superclass.constructor.call(this, {
5375         isLocal : true,
5376         reader: new Roo.data.ArrayReader({
5377                 id: config.id
5378             },
5379             Roo.data.Record.create(config.fields)
5380         ),
5381         proxy : new Roo.data.MemoryProxy(config.data)
5382     });
5383     this.load();
5384 };
5385 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
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 /**
5398  * @extends Roo.data.Store
5399  * @class Roo.data.JsonStore
5400  * Small helper class to make creating Stores for JSON data easier. <br/>
5401 <pre><code>
5402 var store = new Roo.data.JsonStore({
5403     url: 'get-images.php',
5404     root: 'images',
5405     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5406 });
5407 </code></pre>
5408  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5409  * JsonReader and HttpProxy (unless inline data is provided).</b>
5410  * @cfg {Array} fields An array of field definition objects, or field name strings.
5411  * @constructor
5412  * @param {Object} config
5413  */
5414 Roo.data.JsonStore = function(c){
5415     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5416         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5417         reader: new Roo.data.JsonReader(c, c.fields)
5418     }));
5419 };
5420 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5421  * Based on:
5422  * Ext JS Library 1.1.1
5423  * Copyright(c) 2006-2007, Ext JS, LLC.
5424  *
5425  * Originally Released Under LGPL - original licence link has changed is not relivant.
5426  *
5427  * Fork - LGPL
5428  * <script type="text/javascript">
5429  */
5430
5431  
5432 Roo.data.Field = function(config){
5433     if(typeof config == "string"){
5434         config = {name: config};
5435     }
5436     Roo.apply(this, config);
5437     
5438     if(!this.type){
5439         this.type = "auto";
5440     }
5441     
5442     var st = Roo.data.SortTypes;
5443     // named sortTypes are supported, here we look them up
5444     if(typeof this.sortType == "string"){
5445         this.sortType = st[this.sortType];
5446     }
5447     
5448     // set default sortType for strings and dates
5449     if(!this.sortType){
5450         switch(this.type){
5451             case "string":
5452                 this.sortType = st.asUCString;
5453                 break;
5454             case "date":
5455                 this.sortType = st.asDate;
5456                 break;
5457             default:
5458                 this.sortType = st.none;
5459         }
5460     }
5461
5462     // define once
5463     var stripRe = /[\$,%]/g;
5464
5465     // prebuilt conversion function for this field, instead of
5466     // switching every time we're reading a value
5467     if(!this.convert){
5468         var cv, dateFormat = this.dateFormat;
5469         switch(this.type){
5470             case "":
5471             case "auto":
5472             case undefined:
5473                 cv = function(v){ return v; };
5474                 break;
5475             case "string":
5476                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5477                 break;
5478             case "int":
5479                 cv = function(v){
5480                     return v !== undefined && v !== null && v !== '' ?
5481                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5482                     };
5483                 break;
5484             case "float":
5485                 cv = function(v){
5486                     return v !== undefined && v !== null && v !== '' ?
5487                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5488                     };
5489                 break;
5490             case "bool":
5491             case "boolean":
5492                 cv = function(v){ return v === true || v === "true" || v == 1; };
5493                 break;
5494             case "date":
5495                 cv = function(v){
5496                     if(!v){
5497                         return '';
5498                     }
5499                     if(v instanceof Date){
5500                         return v;
5501                     }
5502                     if(dateFormat){
5503                         if(dateFormat == "timestamp"){
5504                             return new Date(v*1000);
5505                         }
5506                         return Date.parseDate(v, dateFormat);
5507                     }
5508                     var parsed = Date.parse(v);
5509                     return parsed ? new Date(parsed) : null;
5510                 };
5511              break;
5512             
5513         }
5514         this.convert = cv;
5515     }
5516 };
5517
5518 Roo.data.Field.prototype = {
5519     dateFormat: null,
5520     defaultValue: "",
5521     mapping: null,
5522     sortType : null,
5523     sortDir : "ASC"
5524 };/*
5525  * Based on:
5526  * Ext JS Library 1.1.1
5527  * Copyright(c) 2006-2007, Ext JS, LLC.
5528  *
5529  * Originally Released Under LGPL - original licence link has changed is not relivant.
5530  *
5531  * Fork - LGPL
5532  * <script type="text/javascript">
5533  */
5534  
5535 // Base class for reading structured data from a data source.  This class is intended to be
5536 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5537
5538 /**
5539  * @class Roo.data.DataReader
5540  * Base class for reading structured data from a data source.  This class is intended to be
5541  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5542  */
5543
5544 Roo.data.DataReader = function(meta, recordType){
5545     
5546     this.meta = meta;
5547     
5548     this.recordType = recordType instanceof Array ? 
5549         Roo.data.Record.create(recordType) : recordType;
5550 };
5551
5552 Roo.data.DataReader.prototype = {
5553      /**
5554      * Create an empty record
5555      * @param {Object} data (optional) - overlay some values
5556      * @return {Roo.data.Record} record created.
5557      */
5558     newRow :  function(d) {
5559         var da =  {};
5560         this.recordType.prototype.fields.each(function(c) {
5561             switch( c.type) {
5562                 case 'int' : da[c.name] = 0; break;
5563                 case 'date' : da[c.name] = new Date(); break;
5564                 case 'float' : da[c.name] = 0.0; break;
5565                 case 'boolean' : da[c.name] = false; break;
5566                 default : da[c.name] = ""; break;
5567             }
5568             
5569         });
5570         return new this.recordType(Roo.apply(da, d));
5571     }
5572     
5573 };/*
5574  * Based on:
5575  * Ext JS Library 1.1.1
5576  * Copyright(c) 2006-2007, Ext JS, LLC.
5577  *
5578  * Originally Released Under LGPL - original licence link has changed is not relivant.
5579  *
5580  * Fork - LGPL
5581  * <script type="text/javascript">
5582  */
5583
5584 /**
5585  * @class Roo.data.DataProxy
5586  * @extends Roo.data.Observable
5587  * This class is an abstract base class for implementations which provide retrieval of
5588  * unformatted data objects.<br>
5589  * <p>
5590  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5591  * (of the appropriate type which knows how to parse the data object) to provide a block of
5592  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5593  * <p>
5594  * Custom implementations must implement the load method as described in
5595  * {@link Roo.data.HttpProxy#load}.
5596  */
5597 Roo.data.DataProxy = function(){
5598     this.addEvents({
5599         /**
5600          * @event beforeload
5601          * Fires before a network request is made to retrieve a data object.
5602          * @param {Object} This DataProxy object.
5603          * @param {Object} params The params parameter to the load function.
5604          */
5605         beforeload : true,
5606         /**
5607          * @event load
5608          * Fires before the load method's callback is called.
5609          * @param {Object} This DataProxy object.
5610          * @param {Object} o The data object.
5611          * @param {Object} arg The callback argument object passed to the load function.
5612          */
5613         load : true,
5614         /**
5615          * @event loadexception
5616          * Fires if an Exception occurs during data retrieval.
5617          * @param {Object} This DataProxy object.
5618          * @param {Object} o The data object.
5619          * @param {Object} arg The callback argument object passed to the load function.
5620          * @param {Object} e The Exception.
5621          */
5622         loadexception : true
5623     });
5624     Roo.data.DataProxy.superclass.constructor.call(this);
5625 };
5626
5627 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5628
5629     /**
5630      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5631      */
5632 /*
5633  * Based on:
5634  * Ext JS Library 1.1.1
5635  * Copyright(c) 2006-2007, Ext JS, LLC.
5636  *
5637  * Originally Released Under LGPL - original licence link has changed is not relivant.
5638  *
5639  * Fork - LGPL
5640  * <script type="text/javascript">
5641  */
5642 /**
5643  * @class Roo.data.MemoryProxy
5644  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5645  * to the Reader when its load method is called.
5646  * @constructor
5647  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5648  */
5649 Roo.data.MemoryProxy = function(data){
5650     if (data.data) {
5651         data = data.data;
5652     }
5653     Roo.data.MemoryProxy.superclass.constructor.call(this);
5654     this.data = data;
5655 };
5656
5657 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5658     /**
5659      * Load data from the requested source (in this case an in-memory
5660      * data object passed to the constructor), read the data object into
5661      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5662      * process that block using the passed callback.
5663      * @param {Object} params This parameter is not used by the MemoryProxy class.
5664      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5665      * object into a block of Roo.data.Records.
5666      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5667      * The function must be passed <ul>
5668      * <li>The Record block object</li>
5669      * <li>The "arg" argument from the load function</li>
5670      * <li>A boolean success indicator</li>
5671      * </ul>
5672      * @param {Object} scope The scope in which to call the callback
5673      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5674      */
5675     load : function(params, reader, callback, scope, arg){
5676         params = params || {};
5677         var result;
5678         try {
5679             result = reader.readRecords(this.data);
5680         }catch(e){
5681             this.fireEvent("loadexception", this, arg, null, e);
5682             callback.call(scope, null, arg, false);
5683             return;
5684         }
5685         callback.call(scope, result, arg, true);
5686     },
5687     
5688     // private
5689     update : function(params, records){
5690         
5691     }
5692 });/*
5693  * Based on:
5694  * Ext JS Library 1.1.1
5695  * Copyright(c) 2006-2007, Ext JS, LLC.
5696  *
5697  * Originally Released Under LGPL - original licence link has changed is not relivant.
5698  *
5699  * Fork - LGPL
5700  * <script type="text/javascript">
5701  */
5702 /**
5703  * @class Roo.data.HttpProxy
5704  * @extends Roo.data.DataProxy
5705  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5706  * configured to reference a certain URL.<br><br>
5707  * <p>
5708  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5709  * from which the running page was served.<br><br>
5710  * <p>
5711  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5712  * <p>
5713  * Be aware that to enable the browser to parse an XML document, the server must set
5714  * the Content-Type header in the HTTP response to "text/xml".
5715  * @constructor
5716  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5717  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5718  * will be used to make the request.
5719  */
5720 Roo.data.HttpProxy = function(conn){
5721     Roo.data.HttpProxy.superclass.constructor.call(this);
5722     // is conn a conn config or a real conn?
5723     this.conn = conn;
5724     this.useAjax = !conn || !conn.events;
5725   
5726 };
5727
5728 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5729     // thse are take from connection...
5730     
5731     /**
5732      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5733      */
5734     /**
5735      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5736      * extra parameters to each request made by this object. (defaults to undefined)
5737      */
5738     /**
5739      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5740      *  to each request made by this object. (defaults to undefined)
5741      */
5742     /**
5743      * @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)
5744      */
5745     /**
5746      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5747      */
5748      /**
5749      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5750      * @type Boolean
5751      */
5752   
5753
5754     /**
5755      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5756      * @type Boolean
5757      */
5758     /**
5759      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5760      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5761      * a finer-grained basis than the DataProxy events.
5762      */
5763     getConnection : function(){
5764         return this.useAjax ? Roo.Ajax : this.conn;
5765     },
5766
5767     /**
5768      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5769      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5770      * process that block using the passed callback.
5771      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5772      * for the request to the remote server.
5773      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5774      * object into a block of Roo.data.Records.
5775      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5776      * The function must be passed <ul>
5777      * <li>The Record block object</li>
5778      * <li>The "arg" argument from the load function</li>
5779      * <li>A boolean success indicator</li>
5780      * </ul>
5781      * @param {Object} scope The scope in which to call the callback
5782      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5783      */
5784     load : function(params, reader, callback, scope, arg){
5785         if(this.fireEvent("beforeload", this, params) !== false){
5786             var  o = {
5787                 params : params || {},
5788                 request: {
5789                     callback : callback,
5790                     scope : scope,
5791                     arg : arg
5792                 },
5793                 reader: reader,
5794                 callback : this.loadResponse,
5795                 scope: this
5796             };
5797             if(this.useAjax){
5798                 Roo.applyIf(o, this.conn);
5799                 if(this.activeRequest){
5800                     Roo.Ajax.abort(this.activeRequest);
5801                 }
5802                 this.activeRequest = Roo.Ajax.request(o);
5803             }else{
5804                 this.conn.request(o);
5805             }
5806         }else{
5807             callback.call(scope||this, null, arg, false);
5808         }
5809     },
5810
5811     // private
5812     loadResponse : function(o, success, response){
5813         delete this.activeRequest;
5814         if(!success){
5815             this.fireEvent("loadexception", this, o, response);
5816             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5817             return;
5818         }
5819         var result;
5820         try {
5821             result = o.reader.read(response);
5822         }catch(e){
5823             this.fireEvent("loadexception", this, o, response, e);
5824             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5825             return;
5826         }
5827         
5828         this.fireEvent("load", this, o, o.request.arg);
5829         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5830     },
5831
5832     // private
5833     update : function(dataSet){
5834
5835     },
5836
5837     // private
5838     updateResponse : function(dataSet){
5839
5840     }
5841 });/*
5842  * Based on:
5843  * Ext JS Library 1.1.1
5844  * Copyright(c) 2006-2007, Ext JS, LLC.
5845  *
5846  * Originally Released Under LGPL - original licence link has changed is not relivant.
5847  *
5848  * Fork - LGPL
5849  * <script type="text/javascript">
5850  */
5851
5852 /**
5853  * @class Roo.data.ScriptTagProxy
5854  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5855  * other than the originating domain of the running page.<br><br>
5856  * <p>
5857  * <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
5858  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5859  * <p>
5860  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5861  * source code that is used as the source inside a &lt;script> tag.<br><br>
5862  * <p>
5863  * In order for the browser to process the returned data, the server must wrap the data object
5864  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5865  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5866  * depending on whether the callback name was passed:
5867  * <p>
5868  * <pre><code>
5869 boolean scriptTag = false;
5870 String cb = request.getParameter("callback");
5871 if (cb != null) {
5872     scriptTag = true;
5873     response.setContentType("text/javascript");
5874 } else {
5875     response.setContentType("application/x-json");
5876 }
5877 Writer out = response.getWriter();
5878 if (scriptTag) {
5879     out.write(cb + "(");
5880 }
5881 out.print(dataBlock.toJsonString());
5882 if (scriptTag) {
5883     out.write(");");
5884 }
5885 </pre></code>
5886  *
5887  * @constructor
5888  * @param {Object} config A configuration object.
5889  */
5890 Roo.data.ScriptTagProxy = function(config){
5891     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5892     Roo.apply(this, config);
5893     this.head = document.getElementsByTagName("head")[0];
5894 };
5895
5896 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5897
5898 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5899     /**
5900      * @cfg {String} url The URL from which to request the data object.
5901      */
5902     /**
5903      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5904      */
5905     timeout : 30000,
5906     /**
5907      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5908      * the server the name of the callback function set up by the load call to process the returned data object.
5909      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5910      * javascript output which calls this named function passing the data object as its only parameter.
5911      */
5912     callbackParam : "callback",
5913     /**
5914      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5915      * name to the request.
5916      */
5917     nocache : true,
5918
5919     /**
5920      * Load data from the configured URL, read the data object into
5921      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5922      * process that block using the passed callback.
5923      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5924      * for the request to the remote server.
5925      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5926      * object into a block of Roo.data.Records.
5927      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5928      * The function must be passed <ul>
5929      * <li>The Record block object</li>
5930      * <li>The "arg" argument from the load function</li>
5931      * <li>A boolean success indicator</li>
5932      * </ul>
5933      * @param {Object} scope The scope in which to call the callback
5934      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5935      */
5936     load : function(params, reader, callback, scope, arg){
5937         if(this.fireEvent("beforeload", this, params) !== false){
5938
5939             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5940
5941             var url = this.url;
5942             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5943             if(this.nocache){
5944                 url += "&_dc=" + (new Date().getTime());
5945             }
5946             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5947             var trans = {
5948                 id : transId,
5949                 cb : "stcCallback"+transId,
5950                 scriptId : "stcScript"+transId,
5951                 params : params,
5952                 arg : arg,
5953                 url : url,
5954                 callback : callback,
5955                 scope : scope,
5956                 reader : reader
5957             };
5958             var conn = this;
5959
5960             window[trans.cb] = function(o){
5961                 conn.handleResponse(o, trans);
5962             };
5963
5964             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
5965
5966             if(this.autoAbort !== false){
5967                 this.abort();
5968             }
5969
5970             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
5971
5972             var script = document.createElement("script");
5973             script.setAttribute("src", url);
5974             script.setAttribute("type", "text/javascript");
5975             script.setAttribute("id", trans.scriptId);
5976             this.head.appendChild(script);
5977
5978             this.trans = trans;
5979         }else{
5980             callback.call(scope||this, null, arg, false);
5981         }
5982     },
5983
5984     // private
5985     isLoading : function(){
5986         return this.trans ? true : false;
5987     },
5988
5989     /**
5990      * Abort the current server request.
5991      */
5992     abort : function(){
5993         if(this.isLoading()){
5994             this.destroyTrans(this.trans);
5995         }
5996     },
5997
5998     // private
5999     destroyTrans : function(trans, isLoaded){
6000         this.head.removeChild(document.getElementById(trans.scriptId));
6001         clearTimeout(trans.timeoutId);
6002         if(isLoaded){
6003             window[trans.cb] = undefined;
6004             try{
6005                 delete window[trans.cb];
6006             }catch(e){}
6007         }else{
6008             // if hasn't been loaded, wait for load to remove it to prevent script error
6009             window[trans.cb] = function(){
6010                 window[trans.cb] = undefined;
6011                 try{
6012                     delete window[trans.cb];
6013                 }catch(e){}
6014             };
6015         }
6016     },
6017
6018     // private
6019     handleResponse : function(o, trans){
6020         this.trans = false;
6021         this.destroyTrans(trans, true);
6022         var result;
6023         try {
6024             result = trans.reader.readRecords(o);
6025         }catch(e){
6026             this.fireEvent("loadexception", this, o, trans.arg, e);
6027             trans.callback.call(trans.scope||window, null, trans.arg, false);
6028             return;
6029         }
6030         this.fireEvent("load", this, o, trans.arg);
6031         trans.callback.call(trans.scope||window, result, trans.arg, true);
6032     },
6033
6034     // private
6035     handleFailure : function(trans){
6036         this.trans = false;
6037         this.destroyTrans(trans, false);
6038         this.fireEvent("loadexception", this, null, trans.arg);
6039         trans.callback.call(trans.scope||window, null, trans.arg, false);
6040     }
6041 });/*
6042  * Based on:
6043  * Ext JS Library 1.1.1
6044  * Copyright(c) 2006-2007, Ext JS, LLC.
6045  *
6046  * Originally Released Under LGPL - original licence link has changed is not relivant.
6047  *
6048  * Fork - LGPL
6049  * <script type="text/javascript">
6050  */
6051
6052 /**
6053  * @class Roo.data.JsonReader
6054  * @extends Roo.data.DataReader
6055  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6056  * based on mappings in a provided Roo.data.Record constructor.
6057  * 
6058  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6059  * in the reply previously. 
6060  * 
6061  * <p>
6062  * Example code:
6063  * <pre><code>
6064 var RecordDef = Roo.data.Record.create([
6065     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6066     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6067 ]);
6068 var myReader = new Roo.data.JsonReader({
6069     totalProperty: "results",    // The property which contains the total dataset size (optional)
6070     root: "rows",                // The property which contains an Array of row objects
6071     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6072 }, RecordDef);
6073 </code></pre>
6074  * <p>
6075  * This would consume a JSON file like this:
6076  * <pre><code>
6077 { 'results': 2, 'rows': [
6078     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6079     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6080 }
6081 </code></pre>
6082  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6083  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6084  * paged from the remote server.
6085  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6086  * @cfg {String} root name of the property which contains the Array of row objects.
6087  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6088  * @constructor
6089  * Create a new JsonReader
6090  * @param {Object} meta Metadata configuration options
6091  * @param {Object} recordType Either an Array of field definition objects,
6092  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6093  */
6094 Roo.data.JsonReader = function(meta, recordType){
6095     
6096     meta = meta || {};
6097     // set some defaults:
6098     Roo.applyIf(meta, {
6099         totalProperty: 'total',
6100         successProperty : 'success',
6101         root : 'data',
6102         id : 'id'
6103     });
6104     
6105     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6106 };
6107 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6108     
6109     /**
6110      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6111      * Used by Store query builder to append _requestMeta to params.
6112      * 
6113      */
6114     metaFromRemote : false,
6115     /**
6116      * This method is only used by a DataProxy which has retrieved data from a remote server.
6117      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6118      * @return {Object} data A data block which is used by an Roo.data.Store object as
6119      * a cache of Roo.data.Records.
6120      */
6121     read : function(response){
6122         var json = response.responseText;
6123        
6124         var o = /* eval:var:o */ eval("("+json+")");
6125         if(!o) {
6126             throw {message: "JsonReader.read: Json object not found"};
6127         }
6128         
6129         if(o.metaData){
6130             
6131             delete this.ef;
6132             this.metaFromRemote = true;
6133             this.meta = o.metaData;
6134             this.recordType = Roo.data.Record.create(o.metaData.fields);
6135             this.onMetaChange(this.meta, this.recordType, o);
6136         }
6137         return this.readRecords(o);
6138     },
6139
6140     // private function a store will implement
6141     onMetaChange : function(meta, recordType, o){
6142
6143     },
6144
6145     /**
6146          * @ignore
6147          */
6148     simpleAccess: function(obj, subsc) {
6149         return obj[subsc];
6150     },
6151
6152         /**
6153          * @ignore
6154          */
6155     getJsonAccessor: function(){
6156         var re = /[\[\.]/;
6157         return function(expr) {
6158             try {
6159                 return(re.test(expr))
6160                     ? new Function("obj", "return obj." + expr)
6161                     : function(obj){
6162                         return obj[expr];
6163                     };
6164             } catch(e){}
6165             return Roo.emptyFn;
6166         };
6167     }(),
6168
6169     /**
6170      * Create a data block containing Roo.data.Records from an XML document.
6171      * @param {Object} o An object which contains an Array of row objects in the property specified
6172      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6173      * which contains the total size of the dataset.
6174      * @return {Object} data A data block which is used by an Roo.data.Store object as
6175      * a cache of Roo.data.Records.
6176      */
6177     readRecords : function(o){
6178         /**
6179          * After any data loads, the raw JSON data is available for further custom processing.
6180          * @type Object
6181          */
6182         this.jsonData = o;
6183         var s = this.meta, Record = this.recordType,
6184             f = Record.prototype.fields, fi = f.items, fl = f.length;
6185
6186 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6187         if (!this.ef) {
6188             if(s.totalProperty) {
6189                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6190                 }
6191                 if(s.successProperty) {
6192                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6193                 }
6194                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6195                 if (s.id) {
6196                         var g = this.getJsonAccessor(s.id);
6197                         this.getId = function(rec) {
6198                                 var r = g(rec);
6199                                 return (r === undefined || r === "") ? null : r;
6200                         };
6201                 } else {
6202                         this.getId = function(){return null;};
6203                 }
6204             this.ef = [];
6205             for(var jj = 0; jj < fl; jj++){
6206                 f = fi[jj];
6207                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6208                 this.ef[jj] = this.getJsonAccessor(map);
6209             }
6210         }
6211
6212         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6213         if(s.totalProperty){
6214             var vt = parseInt(this.getTotal(o), 10);
6215             if(!isNaN(vt)){
6216                 totalRecords = vt;
6217             }
6218         }
6219         if(s.successProperty){
6220             var vs = this.getSuccess(o);
6221             if(vs === false || vs === 'false'){
6222                 success = false;
6223             }
6224         }
6225         var records = [];
6226             for(var i = 0; i < c; i++){
6227                     var n = root[i];
6228                 var values = {};
6229                 var id = this.getId(n);
6230                 for(var j = 0; j < fl; j++){
6231                     f = fi[j];
6232                 var v = this.ef[j](n);
6233                 if (!f.convert) {
6234                     Roo.log('missing convert for ' + f.name);
6235                     Roo.log(f);
6236                     continue;
6237                 }
6238                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6239                 }
6240                 var record = new Record(values, id);
6241                 record.json = n;
6242                 records[i] = record;
6243             }
6244             return {
6245                 success : success,
6246                 records : records,
6247                 totalRecords : totalRecords
6248             };
6249     }
6250 });/*
6251  * Based on:
6252  * Ext JS Library 1.1.1
6253  * Copyright(c) 2006-2007, Ext JS, LLC.
6254  *
6255  * Originally Released Under LGPL - original licence link has changed is not relivant.
6256  *
6257  * Fork - LGPL
6258  * <script type="text/javascript">
6259  */
6260
6261 /**
6262  * @class Roo.data.XmlReader
6263  * @extends Roo.data.DataReader
6264  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6265  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6266  * <p>
6267  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6268  * header in the HTTP response must be set to "text/xml".</em>
6269  * <p>
6270  * Example code:
6271  * <pre><code>
6272 var RecordDef = Roo.data.Record.create([
6273    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6274    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6275 ]);
6276 var myReader = new Roo.data.XmlReader({
6277    totalRecords: "results", // The element which contains the total dataset size (optional)
6278    record: "row",           // The repeated element which contains row information
6279    id: "id"                 // The element within the row that provides an ID for the record (optional)
6280 }, RecordDef);
6281 </code></pre>
6282  * <p>
6283  * This would consume an XML file like this:
6284  * <pre><code>
6285 &lt;?xml?>
6286 &lt;dataset>
6287  &lt;results>2&lt;/results>
6288  &lt;row>
6289    &lt;id>1&lt;/id>
6290    &lt;name>Bill&lt;/name>
6291    &lt;occupation>Gardener&lt;/occupation>
6292  &lt;/row>
6293  &lt;row>
6294    &lt;id>2&lt;/id>
6295    &lt;name>Ben&lt;/name>
6296    &lt;occupation>Horticulturalist&lt;/occupation>
6297  &lt;/row>
6298 &lt;/dataset>
6299 </code></pre>
6300  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6301  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6302  * paged from the remote server.
6303  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6304  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6305  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6306  * a record identifier value.
6307  * @constructor
6308  * Create a new XmlReader
6309  * @param {Object} meta Metadata configuration options
6310  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6311  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6312  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6313  */
6314 Roo.data.XmlReader = function(meta, recordType){
6315     meta = meta || {};
6316     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6317 };
6318 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6319     /**
6320      * This method is only used by a DataProxy which has retrieved data from a remote server.
6321          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6322          * to contain a method called 'responseXML' that returns an XML document object.
6323      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6324      * a cache of Roo.data.Records.
6325      */
6326     read : function(response){
6327         var doc = response.responseXML;
6328         if(!doc) {
6329             throw {message: "XmlReader.read: XML Document not available"};
6330         }
6331         return this.readRecords(doc);
6332     },
6333
6334     /**
6335      * Create a data block containing Roo.data.Records from an XML document.
6336          * @param {Object} doc A parsed XML document.
6337      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6338      * a cache of Roo.data.Records.
6339      */
6340     readRecords : function(doc){
6341         /**
6342          * After any data loads/reads, the raw XML Document is available for further custom processing.
6343          * @type XMLDocument
6344          */
6345         this.xmlData = doc;
6346         var root = doc.documentElement || doc;
6347         var q = Roo.DomQuery;
6348         var recordType = this.recordType, fields = recordType.prototype.fields;
6349         var sid = this.meta.id;
6350         var totalRecords = 0, success = true;
6351         if(this.meta.totalRecords){
6352             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6353         }
6354         
6355         if(this.meta.success){
6356             var sv = q.selectValue(this.meta.success, root, true);
6357             success = sv !== false && sv !== 'false';
6358         }
6359         var records = [];
6360         var ns = q.select(this.meta.record, root);
6361         for(var i = 0, len = ns.length; i < len; i++) {
6362                 var n = ns[i];
6363                 var values = {};
6364                 var id = sid ? q.selectValue(sid, n) : undefined;
6365                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6366                     var f = fields.items[j];
6367                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6368                     v = f.convert(v);
6369                     values[f.name] = v;
6370                 }
6371                 var record = new recordType(values, id);
6372                 record.node = n;
6373                 records[records.length] = record;
6374             }
6375
6376             return {
6377                 success : success,
6378                 records : records,
6379                 totalRecords : totalRecords || records.length
6380             };
6381     }
6382 });/*
6383  * Based on:
6384  * Ext JS Library 1.1.1
6385  * Copyright(c) 2006-2007, Ext JS, LLC.
6386  *
6387  * Originally Released Under LGPL - original licence link has changed is not relivant.
6388  *
6389  * Fork - LGPL
6390  * <script type="text/javascript">
6391  */
6392
6393 /**
6394  * @class Roo.data.ArrayReader
6395  * @extends Roo.data.DataReader
6396  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6397  * Each element of that Array represents a row of data fields. The
6398  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6399  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6400  * <p>
6401  * Example code:.
6402  * <pre><code>
6403 var RecordDef = Roo.data.Record.create([
6404     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6405     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6406 ]);
6407 var myReader = new Roo.data.ArrayReader({
6408     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6409 }, RecordDef);
6410 </code></pre>
6411  * <p>
6412  * This would consume an Array like this:
6413  * <pre><code>
6414 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6415   </code></pre>
6416  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6417  * @constructor
6418  * Create a new JsonReader
6419  * @param {Object} meta Metadata configuration options.
6420  * @param {Object} recordType Either an Array of field definition objects
6421  * as specified to {@link Roo.data.Record#create},
6422  * or an {@link Roo.data.Record} object
6423  * created using {@link Roo.data.Record#create}.
6424  */
6425 Roo.data.ArrayReader = function(meta, recordType){
6426     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6427 };
6428
6429 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6430     /**
6431      * Create a data block containing Roo.data.Records from an XML document.
6432      * @param {Object} o An Array of row objects which represents the dataset.
6433      * @return {Object} data A data block which is used by an Roo.data.Store object as
6434      * a cache of Roo.data.Records.
6435      */
6436     readRecords : function(o){
6437         var sid = this.meta ? this.meta.id : null;
6438         var recordType = this.recordType, fields = recordType.prototype.fields;
6439         var records = [];
6440         var root = o;
6441             for(var i = 0; i < root.length; i++){
6442                     var n = root[i];
6443                 var values = {};
6444                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6445                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6446                 var f = fields.items[j];
6447                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6448                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6449                 v = f.convert(v);
6450                 values[f.name] = v;
6451             }
6452                 var record = new recordType(values, id);
6453                 record.json = n;
6454                 records[records.length] = record;
6455             }
6456             return {
6457                 records : records,
6458                 totalRecords : records.length
6459             };
6460     }
6461 });/*
6462  * Based on:
6463  * Ext JS Library 1.1.1
6464  * Copyright(c) 2006-2007, Ext JS, LLC.
6465  *
6466  * Originally Released Under LGPL - original licence link has changed is not relivant.
6467  *
6468  * Fork - LGPL
6469  * <script type="text/javascript">
6470  */
6471
6472
6473 /**
6474  * @class Roo.data.Tree
6475  * @extends Roo.util.Observable
6476  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6477  * in the tree have most standard DOM functionality.
6478  * @constructor
6479  * @param {Node} root (optional) The root node
6480  */
6481 Roo.data.Tree = function(root){
6482    this.nodeHash = {};
6483    /**
6484     * The root node for this tree
6485     * @type Node
6486     */
6487    this.root = null;
6488    if(root){
6489        this.setRootNode(root);
6490    }
6491    this.addEvents({
6492        /**
6493         * @event append
6494         * Fires when a new child node is appended to a node in this tree.
6495         * @param {Tree} tree The owner tree
6496         * @param {Node} parent The parent node
6497         * @param {Node} node The newly appended node
6498         * @param {Number} index The index of the newly appended node
6499         */
6500        "append" : true,
6501        /**
6502         * @event remove
6503         * Fires when a child node is removed from a node in this tree.
6504         * @param {Tree} tree The owner tree
6505         * @param {Node} parent The parent node
6506         * @param {Node} node The child node removed
6507         */
6508        "remove" : true,
6509        /**
6510         * @event move
6511         * Fires when a node is moved to a new location in the tree
6512         * @param {Tree} tree The owner tree
6513         * @param {Node} node The node moved
6514         * @param {Node} oldParent The old parent of this node
6515         * @param {Node} newParent The new parent of this node
6516         * @param {Number} index The index it was moved to
6517         */
6518        "move" : true,
6519        /**
6520         * @event insert
6521         * Fires when a new child node is inserted in a node in this tree.
6522         * @param {Tree} tree The owner tree
6523         * @param {Node} parent The parent node
6524         * @param {Node} node The child node inserted
6525         * @param {Node} refNode The child node the node was inserted before
6526         */
6527        "insert" : true,
6528        /**
6529         * @event beforeappend
6530         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6531         * @param {Tree} tree The owner tree
6532         * @param {Node} parent The parent node
6533         * @param {Node} node The child node to be appended
6534         */
6535        "beforeappend" : true,
6536        /**
6537         * @event beforeremove
6538         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6539         * @param {Tree} tree The owner tree
6540         * @param {Node} parent The parent node
6541         * @param {Node} node The child node to be removed
6542         */
6543        "beforeremove" : true,
6544        /**
6545         * @event beforemove
6546         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6547         * @param {Tree} tree The owner tree
6548         * @param {Node} node The node being moved
6549         * @param {Node} oldParent The parent of the node
6550         * @param {Node} newParent The new parent the node is moving to
6551         * @param {Number} index The index it is being moved to
6552         */
6553        "beforemove" : true,
6554        /**
6555         * @event beforeinsert
6556         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6557         * @param {Tree} tree The owner tree
6558         * @param {Node} parent The parent node
6559         * @param {Node} node The child node to be inserted
6560         * @param {Node} refNode The child node the node is being inserted before
6561         */
6562        "beforeinsert" : true
6563    });
6564
6565     Roo.data.Tree.superclass.constructor.call(this);
6566 };
6567
6568 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6569     pathSeparator: "/",
6570
6571     proxyNodeEvent : function(){
6572         return this.fireEvent.apply(this, arguments);
6573     },
6574
6575     /**
6576      * Returns the root node for this tree.
6577      * @return {Node}
6578      */
6579     getRootNode : function(){
6580         return this.root;
6581     },
6582
6583     /**
6584      * Sets the root node for this tree.
6585      * @param {Node} node
6586      * @return {Node}
6587      */
6588     setRootNode : function(node){
6589         this.root = node;
6590         node.ownerTree = this;
6591         node.isRoot = true;
6592         this.registerNode(node);
6593         return node;
6594     },
6595
6596     /**
6597      * Gets a node in this tree by its id.
6598      * @param {String} id
6599      * @return {Node}
6600      */
6601     getNodeById : function(id){
6602         return this.nodeHash[id];
6603     },
6604
6605     registerNode : function(node){
6606         this.nodeHash[node.id] = node;
6607     },
6608
6609     unregisterNode : function(node){
6610         delete this.nodeHash[node.id];
6611     },
6612
6613     toString : function(){
6614         return "[Tree"+(this.id?" "+this.id:"")+"]";
6615     }
6616 });
6617
6618 /**
6619  * @class Roo.data.Node
6620  * @extends Roo.util.Observable
6621  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6622  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6623  * @constructor
6624  * @param {Object} attributes The attributes/config for the node
6625  */
6626 Roo.data.Node = function(attributes){
6627     /**
6628      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6629      * @type {Object}
6630      */
6631     this.attributes = attributes || {};
6632     this.leaf = this.attributes.leaf;
6633     /**
6634      * The node id. @type String
6635      */
6636     this.id = this.attributes.id;
6637     if(!this.id){
6638         this.id = Roo.id(null, "ynode-");
6639         this.attributes.id = this.id;
6640     }
6641     /**
6642      * All child nodes of this node. @type Array
6643      */
6644     this.childNodes = [];
6645     if(!this.childNodes.indexOf){ // indexOf is a must
6646         this.childNodes.indexOf = function(o){
6647             for(var i = 0, len = this.length; i < len; i++){
6648                 if(this[i] == o) {
6649                     return i;
6650                 }
6651             }
6652             return -1;
6653         };
6654     }
6655     /**
6656      * The parent node for this node. @type Node
6657      */
6658     this.parentNode = null;
6659     /**
6660      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6661      */
6662     this.firstChild = null;
6663     /**
6664      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6665      */
6666     this.lastChild = null;
6667     /**
6668      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6669      */
6670     this.previousSibling = null;
6671     /**
6672      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6673      */
6674     this.nextSibling = null;
6675
6676     this.addEvents({
6677        /**
6678         * @event append
6679         * Fires when a new child node is appended
6680         * @param {Tree} tree The owner tree
6681         * @param {Node} this This node
6682         * @param {Node} node The newly appended node
6683         * @param {Number} index The index of the newly appended node
6684         */
6685        "append" : true,
6686        /**
6687         * @event remove
6688         * Fires when a child node is removed
6689         * @param {Tree} tree The owner tree
6690         * @param {Node} this This node
6691         * @param {Node} node The removed node
6692         */
6693        "remove" : true,
6694        /**
6695         * @event move
6696         * Fires when this node is moved to a new location in the tree
6697         * @param {Tree} tree The owner tree
6698         * @param {Node} this This node
6699         * @param {Node} oldParent The old parent of this node
6700         * @param {Node} newParent The new parent of this node
6701         * @param {Number} index The index it was moved to
6702         */
6703        "move" : true,
6704        /**
6705         * @event insert
6706         * Fires when a new child node is inserted.
6707         * @param {Tree} tree The owner tree
6708         * @param {Node} this This node
6709         * @param {Node} node The child node inserted
6710         * @param {Node} refNode The child node the node was inserted before
6711         */
6712        "insert" : true,
6713        /**
6714         * @event beforeappend
6715         * Fires before a new child is appended, return false to cancel the append.
6716         * @param {Tree} tree The owner tree
6717         * @param {Node} this This node
6718         * @param {Node} node The child node to be appended
6719         */
6720        "beforeappend" : true,
6721        /**
6722         * @event beforeremove
6723         * Fires before a child is removed, return false to cancel the remove.
6724         * @param {Tree} tree The owner tree
6725         * @param {Node} this This node
6726         * @param {Node} node The child node to be removed
6727         */
6728        "beforeremove" : true,
6729        /**
6730         * @event beforemove
6731         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6732         * @param {Tree} tree The owner tree
6733         * @param {Node} this This node
6734         * @param {Node} oldParent The parent of this node
6735         * @param {Node} newParent The new parent this node is moving to
6736         * @param {Number} index The index it is being moved to
6737         */
6738        "beforemove" : true,
6739        /**
6740         * @event beforeinsert
6741         * Fires before a new child is inserted, return false to cancel the insert.
6742         * @param {Tree} tree The owner tree
6743         * @param {Node} this This node
6744         * @param {Node} node The child node to be inserted
6745         * @param {Node} refNode The child node the node is being inserted before
6746         */
6747        "beforeinsert" : true
6748    });
6749     this.listeners = this.attributes.listeners;
6750     Roo.data.Node.superclass.constructor.call(this);
6751 };
6752
6753 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6754     fireEvent : function(evtName){
6755         // first do standard event for this node
6756         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6757             return false;
6758         }
6759         // then bubble it up to the tree if the event wasn't cancelled
6760         var ot = this.getOwnerTree();
6761         if(ot){
6762             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6763                 return false;
6764             }
6765         }
6766         return true;
6767     },
6768
6769     /**
6770      * Returns true if this node is a leaf
6771      * @return {Boolean}
6772      */
6773     isLeaf : function(){
6774         return this.leaf === true;
6775     },
6776
6777     // private
6778     setFirstChild : function(node){
6779         this.firstChild = node;
6780     },
6781
6782     //private
6783     setLastChild : function(node){
6784         this.lastChild = node;
6785     },
6786
6787
6788     /**
6789      * Returns true if this node is the last child of its parent
6790      * @return {Boolean}
6791      */
6792     isLast : function(){
6793        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6794     },
6795
6796     /**
6797      * Returns true if this node is the first child of its parent
6798      * @return {Boolean}
6799      */
6800     isFirst : function(){
6801        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6802     },
6803
6804     hasChildNodes : function(){
6805         return !this.isLeaf() && this.childNodes.length > 0;
6806     },
6807
6808     /**
6809      * Insert node(s) as the last child node of this node.
6810      * @param {Node/Array} node The node or Array of nodes to append
6811      * @return {Node} The appended node if single append, or null if an array was passed
6812      */
6813     appendChild : function(node){
6814         var multi = false;
6815         if(node instanceof Array){
6816             multi = node;
6817         }else if(arguments.length > 1){
6818             multi = arguments;
6819         }
6820         // if passed an array or multiple args do them one by one
6821         if(multi){
6822             for(var i = 0, len = multi.length; i < len; i++) {
6823                 this.appendChild(multi[i]);
6824             }
6825         }else{
6826             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6827                 return false;
6828             }
6829             var index = this.childNodes.length;
6830             var oldParent = node.parentNode;
6831             // it's a move, make sure we move it cleanly
6832             if(oldParent){
6833                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6834                     return false;
6835                 }
6836                 oldParent.removeChild(node);
6837             }
6838             index = this.childNodes.length;
6839             if(index == 0){
6840                 this.setFirstChild(node);
6841             }
6842             this.childNodes.push(node);
6843             node.parentNode = this;
6844             var ps = this.childNodes[index-1];
6845             if(ps){
6846                 node.previousSibling = ps;
6847                 ps.nextSibling = node;
6848             }else{
6849                 node.previousSibling = null;
6850             }
6851             node.nextSibling = null;
6852             this.setLastChild(node);
6853             node.setOwnerTree(this.getOwnerTree());
6854             this.fireEvent("append", this.ownerTree, this, node, index);
6855             if(oldParent){
6856                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6857             }
6858             return node;
6859         }
6860     },
6861
6862     /**
6863      * Removes a child node from this node.
6864      * @param {Node} node The node to remove
6865      * @return {Node} The removed node
6866      */
6867     removeChild : function(node){
6868         var index = this.childNodes.indexOf(node);
6869         if(index == -1){
6870             return false;
6871         }
6872         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6873             return false;
6874         }
6875
6876         // remove it from childNodes collection
6877         this.childNodes.splice(index, 1);
6878
6879         // update siblings
6880         if(node.previousSibling){
6881             node.previousSibling.nextSibling = node.nextSibling;
6882         }
6883         if(node.nextSibling){
6884             node.nextSibling.previousSibling = node.previousSibling;
6885         }
6886
6887         // update child refs
6888         if(this.firstChild == node){
6889             this.setFirstChild(node.nextSibling);
6890         }
6891         if(this.lastChild == node){
6892             this.setLastChild(node.previousSibling);
6893         }
6894
6895         node.setOwnerTree(null);
6896         // clear any references from the node
6897         node.parentNode = null;
6898         node.previousSibling = null;
6899         node.nextSibling = null;
6900         this.fireEvent("remove", this.ownerTree, this, node);
6901         return node;
6902     },
6903
6904     /**
6905      * Inserts the first node before the second node in this nodes childNodes collection.
6906      * @param {Node} node The node to insert
6907      * @param {Node} refNode The node to insert before (if null the node is appended)
6908      * @return {Node} The inserted node
6909      */
6910     insertBefore : function(node, refNode){
6911         if(!refNode){ // like standard Dom, refNode can be null for append
6912             return this.appendChild(node);
6913         }
6914         // nothing to do
6915         if(node == refNode){
6916             return false;
6917         }
6918
6919         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6920             return false;
6921         }
6922         var index = this.childNodes.indexOf(refNode);
6923         var oldParent = node.parentNode;
6924         var refIndex = index;
6925
6926         // when moving internally, indexes will change after remove
6927         if(oldParent == this && this.childNodes.indexOf(node) < index){
6928             refIndex--;
6929         }
6930
6931         // it's a move, make sure we move it cleanly
6932         if(oldParent){
6933             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6934                 return false;
6935             }
6936             oldParent.removeChild(node);
6937         }
6938         if(refIndex == 0){
6939             this.setFirstChild(node);
6940         }
6941         this.childNodes.splice(refIndex, 0, node);
6942         node.parentNode = this;
6943         var ps = this.childNodes[refIndex-1];
6944         if(ps){
6945             node.previousSibling = ps;
6946             ps.nextSibling = node;
6947         }else{
6948             node.previousSibling = null;
6949         }
6950         node.nextSibling = refNode;
6951         refNode.previousSibling = node;
6952         node.setOwnerTree(this.getOwnerTree());
6953         this.fireEvent("insert", this.ownerTree, this, node, refNode);
6954         if(oldParent){
6955             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
6956         }
6957         return node;
6958     },
6959
6960     /**
6961      * Returns the child node at the specified index.
6962      * @param {Number} index
6963      * @return {Node}
6964      */
6965     item : function(index){
6966         return this.childNodes[index];
6967     },
6968
6969     /**
6970      * Replaces one child node in this node with another.
6971      * @param {Node} newChild The replacement node
6972      * @param {Node} oldChild The node to replace
6973      * @return {Node} The replaced node
6974      */
6975     replaceChild : function(newChild, oldChild){
6976         this.insertBefore(newChild, oldChild);
6977         this.removeChild(oldChild);
6978         return oldChild;
6979     },
6980
6981     /**
6982      * Returns the index of a child node
6983      * @param {Node} node
6984      * @return {Number} The index of the node or -1 if it was not found
6985      */
6986     indexOf : function(child){
6987         return this.childNodes.indexOf(child);
6988     },
6989
6990     /**
6991      * Returns the tree this node is in.
6992      * @return {Tree}
6993      */
6994     getOwnerTree : function(){
6995         // if it doesn't have one, look for one
6996         if(!this.ownerTree){
6997             var p = this;
6998             while(p){
6999                 if(p.ownerTree){
7000                     this.ownerTree = p.ownerTree;
7001                     break;
7002                 }
7003                 p = p.parentNode;
7004             }
7005         }
7006         return this.ownerTree;
7007     },
7008
7009     /**
7010      * Returns depth of this node (the root node has a depth of 0)
7011      * @return {Number}
7012      */
7013     getDepth : function(){
7014         var depth = 0;
7015         var p = this;
7016         while(p.parentNode){
7017             ++depth;
7018             p = p.parentNode;
7019         }
7020         return depth;
7021     },
7022
7023     // private
7024     setOwnerTree : function(tree){
7025         // if it's move, we need to update everyone
7026         if(tree != this.ownerTree){
7027             if(this.ownerTree){
7028                 this.ownerTree.unregisterNode(this);
7029             }
7030             this.ownerTree = tree;
7031             var cs = this.childNodes;
7032             for(var i = 0, len = cs.length; i < len; i++) {
7033                 cs[i].setOwnerTree(tree);
7034             }
7035             if(tree){
7036                 tree.registerNode(this);
7037             }
7038         }
7039     },
7040
7041     /**
7042      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7043      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7044      * @return {String} The path
7045      */
7046     getPath : function(attr){
7047         attr = attr || "id";
7048         var p = this.parentNode;
7049         var b = [this.attributes[attr]];
7050         while(p){
7051             b.unshift(p.attributes[attr]);
7052             p = p.parentNode;
7053         }
7054         var sep = this.getOwnerTree().pathSeparator;
7055         return sep + b.join(sep);
7056     },
7057
7058     /**
7059      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7060      * function call will be the scope provided or the current node. The arguments to the function
7061      * will be the args provided or the current node. If the function returns false at any point,
7062      * the bubble is stopped.
7063      * @param {Function} fn The function to call
7064      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7065      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7066      */
7067     bubble : function(fn, scope, args){
7068         var p = this;
7069         while(p){
7070             if(fn.call(scope || p, args || p) === false){
7071                 break;
7072             }
7073             p = p.parentNode;
7074         }
7075     },
7076
7077     /**
7078      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7079      * function call will be the scope provided or the current node. The arguments to the function
7080      * will be the args provided or the current node. If the function returns false at any point,
7081      * the cascade is stopped on that branch.
7082      * @param {Function} fn The function to call
7083      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7084      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7085      */
7086     cascade : function(fn, scope, args){
7087         if(fn.call(scope || this, args || this) !== false){
7088             var cs = this.childNodes;
7089             for(var i = 0, len = cs.length; i < len; i++) {
7090                 cs[i].cascade(fn, scope, args);
7091             }
7092         }
7093     },
7094
7095     /**
7096      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7097      * function call will be the scope provided or the current node. The arguments to the function
7098      * will be the args provided or the current node. If the function returns false at any point,
7099      * the iteration stops.
7100      * @param {Function} fn The function to call
7101      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7102      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7103      */
7104     eachChild : function(fn, scope, args){
7105         var cs = this.childNodes;
7106         for(var i = 0, len = cs.length; i < len; i++) {
7107                 if(fn.call(scope || this, args || cs[i]) === false){
7108                     break;
7109                 }
7110         }
7111     },
7112
7113     /**
7114      * Finds the first child that has the attribute with the specified value.
7115      * @param {String} attribute The attribute name
7116      * @param {Mixed} value The value to search for
7117      * @return {Node} The found child or null if none was found
7118      */
7119     findChild : function(attribute, value){
7120         var cs = this.childNodes;
7121         for(var i = 0, len = cs.length; i < len; i++) {
7122                 if(cs[i].attributes[attribute] == value){
7123                     return cs[i];
7124                 }
7125         }
7126         return null;
7127     },
7128
7129     /**
7130      * Finds the first child by a custom function. The child matches if the function passed
7131      * returns true.
7132      * @param {Function} fn
7133      * @param {Object} scope (optional)
7134      * @return {Node} The found child or null if none was found
7135      */
7136     findChildBy : function(fn, scope){
7137         var cs = this.childNodes;
7138         for(var i = 0, len = cs.length; i < len; i++) {
7139                 if(fn.call(scope||cs[i], cs[i]) === true){
7140                     return cs[i];
7141                 }
7142         }
7143         return null;
7144     },
7145
7146     /**
7147      * Sorts this nodes children using the supplied sort function
7148      * @param {Function} fn
7149      * @param {Object} scope (optional)
7150      */
7151     sort : function(fn, scope){
7152         var cs = this.childNodes;
7153         var len = cs.length;
7154         if(len > 0){
7155             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7156             cs.sort(sortFn);
7157             for(var i = 0; i < len; i++){
7158                 var n = cs[i];
7159                 n.previousSibling = cs[i-1];
7160                 n.nextSibling = cs[i+1];
7161                 if(i == 0){
7162                     this.setFirstChild(n);
7163                 }
7164                 if(i == len-1){
7165                     this.setLastChild(n);
7166                 }
7167             }
7168         }
7169     },
7170
7171     /**
7172      * Returns true if this node is an ancestor (at any point) of the passed node.
7173      * @param {Node} node
7174      * @return {Boolean}
7175      */
7176     contains : function(node){
7177         return node.isAncestor(this);
7178     },
7179
7180     /**
7181      * Returns true if the passed node is an ancestor (at any point) of this node.
7182      * @param {Node} node
7183      * @return {Boolean}
7184      */
7185     isAncestor : function(node){
7186         var p = this.parentNode;
7187         while(p){
7188             if(p == node){
7189                 return true;
7190             }
7191             p = p.parentNode;
7192         }
7193         return false;
7194     },
7195
7196     toString : function(){
7197         return "[Node"+(this.id?" "+this.id:"")+"]";
7198     }
7199 });/*
7200  * Based on:
7201  * Ext JS Library 1.1.1
7202  * Copyright(c) 2006-2007, Ext JS, LLC.
7203  *
7204  * Originally Released Under LGPL - original licence link has changed is not relivant.
7205  *
7206  * Fork - LGPL
7207  * <script type="text/javascript">
7208  */
7209  
7210
7211 /**
7212  * @class Roo.ComponentMgr
7213  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7214  * @singleton
7215  */
7216 Roo.ComponentMgr = function(){
7217     var all = new Roo.util.MixedCollection();
7218
7219     return {
7220         /**
7221          * Registers a component.
7222          * @param {Roo.Component} c The component
7223          */
7224         register : function(c){
7225             all.add(c);
7226         },
7227
7228         /**
7229          * Unregisters a component.
7230          * @param {Roo.Component} c The component
7231          */
7232         unregister : function(c){
7233             all.remove(c);
7234         },
7235
7236         /**
7237          * Returns a component by id
7238          * @param {String} id The component id
7239          */
7240         get : function(id){
7241             return all.get(id);
7242         },
7243
7244         /**
7245          * Registers a function that will be called when a specified component is added to ComponentMgr
7246          * @param {String} id The component id
7247          * @param {Funtction} fn The callback function
7248          * @param {Object} scope The scope of the callback
7249          */
7250         onAvailable : function(id, fn, scope){
7251             all.on("add", function(index, o){
7252                 if(o.id == id){
7253                     fn.call(scope || o, o);
7254                     all.un("add", fn, scope);
7255                 }
7256             });
7257         }
7258     };
7259 }();/*
7260  * Based on:
7261  * Ext JS Library 1.1.1
7262  * Copyright(c) 2006-2007, Ext JS, LLC.
7263  *
7264  * Originally Released Under LGPL - original licence link has changed is not relivant.
7265  *
7266  * Fork - LGPL
7267  * <script type="text/javascript">
7268  */
7269  
7270 /**
7271  * @class Roo.Component
7272  * @extends Roo.util.Observable
7273  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7274  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7275  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7276  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7277  * All visual components (widgets) that require rendering into a layout should subclass Component.
7278  * @constructor
7279  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7280  * 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
7281  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7282  */
7283 Roo.Component = function(config){
7284     config = config || {};
7285     if(config.tagName || config.dom || typeof config == "string"){ // element object
7286         config = {el: config, id: config.id || config};
7287     }
7288     this.initialConfig = config;
7289
7290     Roo.apply(this, config);
7291     this.addEvents({
7292         /**
7293          * @event disable
7294          * Fires after the component is disabled.
7295              * @param {Roo.Component} this
7296              */
7297         disable : true,
7298         /**
7299          * @event enable
7300          * Fires after the component is enabled.
7301              * @param {Roo.Component} this
7302              */
7303         enable : true,
7304         /**
7305          * @event beforeshow
7306          * Fires before the component is shown.  Return false to stop the show.
7307              * @param {Roo.Component} this
7308              */
7309         beforeshow : true,
7310         /**
7311          * @event show
7312          * Fires after the component is shown.
7313              * @param {Roo.Component} this
7314              */
7315         show : true,
7316         /**
7317          * @event beforehide
7318          * Fires before the component is hidden. Return false to stop the hide.
7319              * @param {Roo.Component} this
7320              */
7321         beforehide : true,
7322         /**
7323          * @event hide
7324          * Fires after the component is hidden.
7325              * @param {Roo.Component} this
7326              */
7327         hide : true,
7328         /**
7329          * @event beforerender
7330          * Fires before the component is rendered. Return false to stop the render.
7331              * @param {Roo.Component} this
7332              */
7333         beforerender : true,
7334         /**
7335          * @event render
7336          * Fires after the component is rendered.
7337              * @param {Roo.Component} this
7338              */
7339         render : true,
7340         /**
7341          * @event beforedestroy
7342          * Fires before the component is destroyed. Return false to stop the destroy.
7343              * @param {Roo.Component} this
7344              */
7345         beforedestroy : true,
7346         /**
7347          * @event destroy
7348          * Fires after the component is destroyed.
7349              * @param {Roo.Component} this
7350              */
7351         destroy : true
7352     });
7353     if(!this.id){
7354         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7355     }
7356     Roo.ComponentMgr.register(this);
7357     Roo.Component.superclass.constructor.call(this);
7358     this.initComponent();
7359     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7360         this.render(this.renderTo);
7361         delete this.renderTo;
7362     }
7363 };
7364
7365 // private
7366 Roo.Component.AUTO_ID = 1000;
7367
7368 Roo.extend(Roo.Component, Roo.util.Observable, {
7369     /**
7370      * @property {Boolean} hidden
7371      * true if this component is hidden. Read-only.
7372      */
7373     hidden : false,
7374     /**
7375      * true if this component is disabled. Read-only.
7376      */
7377     disabled : false,
7378     /**
7379      * true if this component has been rendered. Read-only.
7380      */
7381     rendered : false,
7382     
7383     /** @cfg {String} disableClass
7384      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7385      */
7386     disabledClass : "x-item-disabled",
7387         /** @cfg {Boolean} allowDomMove
7388          * Whether the component can move the Dom node when rendering (defaults to true).
7389          */
7390     allowDomMove : true,
7391     /** @cfg {String} hideMode
7392      * How this component should hidden. Supported values are
7393      * "visibility" (css visibility), "offsets" (negative offset position) and
7394      * "display" (css display) - defaults to "display".
7395      */
7396     hideMode: 'display',
7397
7398     // private
7399     ctype : "Roo.Component",
7400
7401     /** @cfg {String} actionMode 
7402      * which property holds the element that used for  hide() / show() / disable() / enable()
7403      * default is 'el' 
7404      */
7405     actionMode : "el",
7406
7407     // private
7408     getActionEl : function(){
7409         return this[this.actionMode];
7410     },
7411
7412     initComponent : Roo.emptyFn,
7413     /**
7414      * If this is a lazy rendering component, render it to its container element.
7415      * @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.
7416      */
7417     render : function(container, position){
7418         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7419             if(!container && this.el){
7420                 this.el = Roo.get(this.el);
7421                 container = this.el.dom.parentNode;
7422                 this.allowDomMove = false;
7423             }
7424             this.container = Roo.get(container);
7425             this.rendered = true;
7426             if(position !== undefined){
7427                 if(typeof position == 'number'){
7428                     position = this.container.dom.childNodes[position];
7429                 }else{
7430                     position = Roo.getDom(position);
7431                 }
7432             }
7433             this.onRender(this.container, position || null);
7434             if(this.cls){
7435                 this.el.addClass(this.cls);
7436                 delete this.cls;
7437             }
7438             if(this.style){
7439                 this.el.applyStyles(this.style);
7440                 delete this.style;
7441             }
7442             this.fireEvent("render", this);
7443             this.afterRender(this.container);
7444             if(this.hidden){
7445                 this.hide();
7446             }
7447             if(this.disabled){
7448                 this.disable();
7449             }
7450         }
7451         return this;
7452     },
7453
7454     // private
7455     // default function is not really useful
7456     onRender : function(ct, position){
7457         if(this.el){
7458             this.el = Roo.get(this.el);
7459             if(this.allowDomMove !== false){
7460                 ct.dom.insertBefore(this.el.dom, position);
7461             }
7462         }
7463     },
7464
7465     // private
7466     getAutoCreate : function(){
7467         var cfg = typeof this.autoCreate == "object" ?
7468                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7469         if(this.id && !cfg.id){
7470             cfg.id = this.id;
7471         }
7472         return cfg;
7473     },
7474
7475     // private
7476     afterRender : Roo.emptyFn,
7477
7478     /**
7479      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7480      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7481      */
7482     destroy : function(){
7483         if(this.fireEvent("beforedestroy", this) !== false){
7484             this.purgeListeners();
7485             this.beforeDestroy();
7486             if(this.rendered){
7487                 this.el.removeAllListeners();
7488                 this.el.remove();
7489                 if(this.actionMode == "container"){
7490                     this.container.remove();
7491                 }
7492             }
7493             this.onDestroy();
7494             Roo.ComponentMgr.unregister(this);
7495             this.fireEvent("destroy", this);
7496         }
7497     },
7498
7499         // private
7500     beforeDestroy : function(){
7501
7502     },
7503
7504         // private
7505         onDestroy : function(){
7506
7507     },
7508
7509     /**
7510      * Returns the underlying {@link Roo.Element}.
7511      * @return {Roo.Element} The element
7512      */
7513     getEl : function(){
7514         return this.el;
7515     },
7516
7517     /**
7518      * Returns the id of this component.
7519      * @return {String}
7520      */
7521     getId : function(){
7522         return this.id;
7523     },
7524
7525     /**
7526      * Try to focus this component.
7527      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7528      * @return {Roo.Component} this
7529      */
7530     focus : function(selectText){
7531         if(this.rendered){
7532             this.el.focus();
7533             if(selectText === true){
7534                 this.el.dom.select();
7535             }
7536         }
7537         return this;
7538     },
7539
7540     // private
7541     blur : function(){
7542         if(this.rendered){
7543             this.el.blur();
7544         }
7545         return this;
7546     },
7547
7548     /**
7549      * Disable this component.
7550      * @return {Roo.Component} this
7551      */
7552     disable : function(){
7553         if(this.rendered){
7554             this.onDisable();
7555         }
7556         this.disabled = true;
7557         this.fireEvent("disable", this);
7558         return this;
7559     },
7560
7561         // private
7562     onDisable : function(){
7563         this.getActionEl().addClass(this.disabledClass);
7564         this.el.dom.disabled = true;
7565     },
7566
7567     /**
7568      * Enable this component.
7569      * @return {Roo.Component} this
7570      */
7571     enable : function(){
7572         if(this.rendered){
7573             this.onEnable();
7574         }
7575         this.disabled = false;
7576         this.fireEvent("enable", this);
7577         return this;
7578     },
7579
7580         // private
7581     onEnable : function(){
7582         this.getActionEl().removeClass(this.disabledClass);
7583         this.el.dom.disabled = false;
7584     },
7585
7586     /**
7587      * Convenience function for setting disabled/enabled by boolean.
7588      * @param {Boolean} disabled
7589      */
7590     setDisabled : function(disabled){
7591         this[disabled ? "disable" : "enable"]();
7592     },
7593
7594     /**
7595      * Show this component.
7596      * @return {Roo.Component} this
7597      */
7598     show: function(){
7599         if(this.fireEvent("beforeshow", this) !== false){
7600             this.hidden = false;
7601             if(this.rendered){
7602                 this.onShow();
7603             }
7604             this.fireEvent("show", this);
7605         }
7606         return this;
7607     },
7608
7609     // private
7610     onShow : function(){
7611         var ae = this.getActionEl();
7612         if(this.hideMode == 'visibility'){
7613             ae.dom.style.visibility = "visible";
7614         }else if(this.hideMode == 'offsets'){
7615             ae.removeClass('x-hidden');
7616         }else{
7617             ae.dom.style.display = "";
7618         }
7619     },
7620
7621     /**
7622      * Hide this component.
7623      * @return {Roo.Component} this
7624      */
7625     hide: function(){
7626         if(this.fireEvent("beforehide", this) !== false){
7627             this.hidden = true;
7628             if(this.rendered){
7629                 this.onHide();
7630             }
7631             this.fireEvent("hide", this);
7632         }
7633         return this;
7634     },
7635
7636     // private
7637     onHide : function(){
7638         var ae = this.getActionEl();
7639         if(this.hideMode == 'visibility'){
7640             ae.dom.style.visibility = "hidden";
7641         }else if(this.hideMode == 'offsets'){
7642             ae.addClass('x-hidden');
7643         }else{
7644             ae.dom.style.display = "none";
7645         }
7646     },
7647
7648     /**
7649      * Convenience function to hide or show this component by boolean.
7650      * @param {Boolean} visible True to show, false to hide
7651      * @return {Roo.Component} this
7652      */
7653     setVisible: function(visible){
7654         if(visible) {
7655             this.show();
7656         }else{
7657             this.hide();
7658         }
7659         return this;
7660     },
7661
7662     /**
7663      * Returns true if this component is visible.
7664      */
7665     isVisible : function(){
7666         return this.getActionEl().isVisible();
7667     },
7668
7669     cloneConfig : function(overrides){
7670         overrides = overrides || {};
7671         var id = overrides.id || Roo.id();
7672         var cfg = Roo.applyIf(overrides, this.initialConfig);
7673         cfg.id = id; // prevent dup id
7674         return new this.constructor(cfg);
7675     }
7676 });/*
7677  * Based on:
7678  * Ext JS Library 1.1.1
7679  * Copyright(c) 2006-2007, Ext JS, LLC.
7680  *
7681  * Originally Released Under LGPL - original licence link has changed is not relivant.
7682  *
7683  * Fork - LGPL
7684  * <script type="text/javascript">
7685  */
7686  (function(){ 
7687 /**
7688  * @class Roo.Layer
7689  * @extends Roo.Element
7690  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7691  * automatic maintaining of shadow/shim positions.
7692  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7693  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7694  * you can pass a string with a CSS class name. False turns off the shadow.
7695  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7696  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7697  * @cfg {String} cls CSS class to add to the element
7698  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7699  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7700  * @constructor
7701  * @param {Object} config An object with config options.
7702  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7703  */
7704
7705 Roo.Layer = function(config, existingEl){
7706     config = config || {};
7707     var dh = Roo.DomHelper;
7708     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7709     if(existingEl){
7710         this.dom = Roo.getDom(existingEl);
7711     }
7712     if(!this.dom){
7713         var o = config.dh || {tag: "div", cls: "x-layer"};
7714         this.dom = dh.append(pel, o);
7715     }
7716     if(config.cls){
7717         this.addClass(config.cls);
7718     }
7719     this.constrain = config.constrain !== false;
7720     this.visibilityMode = Roo.Element.VISIBILITY;
7721     if(config.id){
7722         this.id = this.dom.id = config.id;
7723     }else{
7724         this.id = Roo.id(this.dom);
7725     }
7726     this.zindex = config.zindex || this.getZIndex();
7727     this.position("absolute", this.zindex);
7728     if(config.shadow){
7729         this.shadowOffset = config.shadowOffset || 4;
7730         this.shadow = new Roo.Shadow({
7731             offset : this.shadowOffset,
7732             mode : config.shadow
7733         });
7734     }else{
7735         this.shadowOffset = 0;
7736     }
7737     this.useShim = config.shim !== false && Roo.useShims;
7738     this.useDisplay = config.useDisplay;
7739     this.hide();
7740 };
7741
7742 var supr = Roo.Element.prototype;
7743
7744 // shims are shared among layer to keep from having 100 iframes
7745 var shims = [];
7746
7747 Roo.extend(Roo.Layer, Roo.Element, {
7748
7749     getZIndex : function(){
7750         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7751     },
7752
7753     getShim : function(){
7754         if(!this.useShim){
7755             return null;
7756         }
7757         if(this.shim){
7758             return this.shim;
7759         }
7760         var shim = shims.shift();
7761         if(!shim){
7762             shim = this.createShim();
7763             shim.enableDisplayMode('block');
7764             shim.dom.style.display = 'none';
7765             shim.dom.style.visibility = 'visible';
7766         }
7767         var pn = this.dom.parentNode;
7768         if(shim.dom.parentNode != pn){
7769             pn.insertBefore(shim.dom, this.dom);
7770         }
7771         shim.setStyle('z-index', this.getZIndex()-2);
7772         this.shim = shim;
7773         return shim;
7774     },
7775
7776     hideShim : function(){
7777         if(this.shim){
7778             this.shim.setDisplayed(false);
7779             shims.push(this.shim);
7780             delete this.shim;
7781         }
7782     },
7783
7784     disableShadow : function(){
7785         if(this.shadow){
7786             this.shadowDisabled = true;
7787             this.shadow.hide();
7788             this.lastShadowOffset = this.shadowOffset;
7789             this.shadowOffset = 0;
7790         }
7791     },
7792
7793     enableShadow : function(show){
7794         if(this.shadow){
7795             this.shadowDisabled = false;
7796             this.shadowOffset = this.lastShadowOffset;
7797             delete this.lastShadowOffset;
7798             if(show){
7799                 this.sync(true);
7800             }
7801         }
7802     },
7803
7804     // private
7805     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7806     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7807     sync : function(doShow){
7808         var sw = this.shadow;
7809         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7810             var sh = this.getShim();
7811
7812             var w = this.getWidth(),
7813                 h = this.getHeight();
7814
7815             var l = this.getLeft(true),
7816                 t = this.getTop(true);
7817
7818             if(sw && !this.shadowDisabled){
7819                 if(doShow && !sw.isVisible()){
7820                     sw.show(this);
7821                 }else{
7822                     sw.realign(l, t, w, h);
7823                 }
7824                 if(sh){
7825                     if(doShow){
7826                        sh.show();
7827                     }
7828                     // fit the shim behind the shadow, so it is shimmed too
7829                     var a = sw.adjusts, s = sh.dom.style;
7830                     s.left = (Math.min(l, l+a.l))+"px";
7831                     s.top = (Math.min(t, t+a.t))+"px";
7832                     s.width = (w+a.w)+"px";
7833                     s.height = (h+a.h)+"px";
7834                 }
7835             }else if(sh){
7836                 if(doShow){
7837                    sh.show();
7838                 }
7839                 sh.setSize(w, h);
7840                 sh.setLeftTop(l, t);
7841             }
7842             
7843         }
7844     },
7845
7846     // private
7847     destroy : function(){
7848         this.hideShim();
7849         if(this.shadow){
7850             this.shadow.hide();
7851         }
7852         this.removeAllListeners();
7853         var pn = this.dom.parentNode;
7854         if(pn){
7855             pn.removeChild(this.dom);
7856         }
7857         Roo.Element.uncache(this.id);
7858     },
7859
7860     remove : function(){
7861         this.destroy();
7862     },
7863
7864     // private
7865     beginUpdate : function(){
7866         this.updating = true;
7867     },
7868
7869     // private
7870     endUpdate : function(){
7871         this.updating = false;
7872         this.sync(true);
7873     },
7874
7875     // private
7876     hideUnders : function(negOffset){
7877         if(this.shadow){
7878             this.shadow.hide();
7879         }
7880         this.hideShim();
7881     },
7882
7883     // private
7884     constrainXY : function(){
7885         if(this.constrain){
7886             var vw = Roo.lib.Dom.getViewWidth(),
7887                 vh = Roo.lib.Dom.getViewHeight();
7888             var s = Roo.get(document).getScroll();
7889
7890             var xy = this.getXY();
7891             var x = xy[0], y = xy[1];   
7892             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7893             // only move it if it needs it
7894             var moved = false;
7895             // first validate right/bottom
7896             if((x + w) > vw+s.left){
7897                 x = vw - w - this.shadowOffset;
7898                 moved = true;
7899             }
7900             if((y + h) > vh+s.top){
7901                 y = vh - h - this.shadowOffset;
7902                 moved = true;
7903             }
7904             // then make sure top/left isn't negative
7905             if(x < s.left){
7906                 x = s.left;
7907                 moved = true;
7908             }
7909             if(y < s.top){
7910                 y = s.top;
7911                 moved = true;
7912             }
7913             if(moved){
7914                 if(this.avoidY){
7915                     var ay = this.avoidY;
7916                     if(y <= ay && (y+h) >= ay){
7917                         y = ay-h-5;   
7918                     }
7919                 }
7920                 xy = [x, y];
7921                 this.storeXY(xy);
7922                 supr.setXY.call(this, xy);
7923                 this.sync();
7924             }
7925         }
7926     },
7927
7928     isVisible : function(){
7929         return this.visible;    
7930     },
7931
7932     // private
7933     showAction : function(){
7934         this.visible = true; // track visibility to prevent getStyle calls
7935         if(this.useDisplay === true){
7936             this.setDisplayed("");
7937         }else if(this.lastXY){
7938             supr.setXY.call(this, this.lastXY);
7939         }else if(this.lastLT){
7940             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7941         }
7942     },
7943
7944     // private
7945     hideAction : function(){
7946         this.visible = false;
7947         if(this.useDisplay === true){
7948             this.setDisplayed(false);
7949         }else{
7950             this.setLeftTop(-10000,-10000);
7951         }
7952     },
7953
7954     // overridden Element method
7955     setVisible : function(v, a, d, c, e){
7956         if(v){
7957             this.showAction();
7958         }
7959         if(a && v){
7960             var cb = function(){
7961                 this.sync(true);
7962                 if(c){
7963                     c();
7964                 }
7965             }.createDelegate(this);
7966             supr.setVisible.call(this, true, true, d, cb, e);
7967         }else{
7968             if(!v){
7969                 this.hideUnders(true);
7970             }
7971             var cb = c;
7972             if(a){
7973                 cb = function(){
7974                     this.hideAction();
7975                     if(c){
7976                         c();
7977                     }
7978                 }.createDelegate(this);
7979             }
7980             supr.setVisible.call(this, v, a, d, cb, e);
7981             if(v){
7982                 this.sync(true);
7983             }else if(!a){
7984                 this.hideAction();
7985             }
7986         }
7987     },
7988
7989     storeXY : function(xy){
7990         delete this.lastLT;
7991         this.lastXY = xy;
7992     },
7993
7994     storeLeftTop : function(left, top){
7995         delete this.lastXY;
7996         this.lastLT = [left, top];
7997     },
7998
7999     // private
8000     beforeFx : function(){
8001         this.beforeAction();
8002         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
8003     },
8004
8005     // private
8006     afterFx : function(){
8007         Roo.Layer.superclass.afterFx.apply(this, arguments);
8008         this.sync(this.isVisible());
8009     },
8010
8011     // private
8012     beforeAction : function(){
8013         if(!this.updating && this.shadow){
8014             this.shadow.hide();
8015         }
8016     },
8017
8018     // overridden Element method
8019     setLeft : function(left){
8020         this.storeLeftTop(left, this.getTop(true));
8021         supr.setLeft.apply(this, arguments);
8022         this.sync();
8023     },
8024
8025     setTop : function(top){
8026         this.storeLeftTop(this.getLeft(true), top);
8027         supr.setTop.apply(this, arguments);
8028         this.sync();
8029     },
8030
8031     setLeftTop : function(left, top){
8032         this.storeLeftTop(left, top);
8033         supr.setLeftTop.apply(this, arguments);
8034         this.sync();
8035     },
8036
8037     setXY : function(xy, a, d, c, e){
8038         this.fixDisplay();
8039         this.beforeAction();
8040         this.storeXY(xy);
8041         var cb = this.createCB(c);
8042         supr.setXY.call(this, xy, a, d, cb, e);
8043         if(!a){
8044             cb();
8045         }
8046     },
8047
8048     // private
8049     createCB : function(c){
8050         var el = this;
8051         return function(){
8052             el.constrainXY();
8053             el.sync(true);
8054             if(c){
8055                 c();
8056             }
8057         };
8058     },
8059
8060     // overridden Element method
8061     setX : function(x, a, d, c, e){
8062         this.setXY([x, this.getY()], a, d, c, e);
8063     },
8064
8065     // overridden Element method
8066     setY : function(y, a, d, c, e){
8067         this.setXY([this.getX(), y], a, d, c, e);
8068     },
8069
8070     // overridden Element method
8071     setSize : function(w, h, a, d, c, e){
8072         this.beforeAction();
8073         var cb = this.createCB(c);
8074         supr.setSize.call(this, w, h, a, d, cb, e);
8075         if(!a){
8076             cb();
8077         }
8078     },
8079
8080     // overridden Element method
8081     setWidth : function(w, a, d, c, e){
8082         this.beforeAction();
8083         var cb = this.createCB(c);
8084         supr.setWidth.call(this, w, a, d, cb, e);
8085         if(!a){
8086             cb();
8087         }
8088     },
8089
8090     // overridden Element method
8091     setHeight : function(h, a, d, c, e){
8092         this.beforeAction();
8093         var cb = this.createCB(c);
8094         supr.setHeight.call(this, h, a, d, cb, e);
8095         if(!a){
8096             cb();
8097         }
8098     },
8099
8100     // overridden Element method
8101     setBounds : function(x, y, w, h, a, d, c, e){
8102         this.beforeAction();
8103         var cb = this.createCB(c);
8104         if(!a){
8105             this.storeXY([x, y]);
8106             supr.setXY.call(this, [x, y]);
8107             supr.setSize.call(this, w, h, a, d, cb, e);
8108             cb();
8109         }else{
8110             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8111         }
8112         return this;
8113     },
8114     
8115     /**
8116      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8117      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8118      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8119      * @param {Number} zindex The new z-index to set
8120      * @return {this} The Layer
8121      */
8122     setZIndex : function(zindex){
8123         this.zindex = zindex;
8124         this.setStyle("z-index", zindex + 2);
8125         if(this.shadow){
8126             this.shadow.setZIndex(zindex + 1);
8127         }
8128         if(this.shim){
8129             this.shim.setStyle("z-index", zindex);
8130         }
8131     }
8132 });
8133 })();/*
8134  * Based on:
8135  * Ext JS Library 1.1.1
8136  * Copyright(c) 2006-2007, Ext JS, LLC.
8137  *
8138  * Originally Released Under LGPL - original licence link has changed is not relivant.
8139  *
8140  * Fork - LGPL
8141  * <script type="text/javascript">
8142  */
8143
8144
8145 /**
8146  * @class Roo.Shadow
8147  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8148  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8149  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8150  * @constructor
8151  * Create a new Shadow
8152  * @param {Object} config The config object
8153  */
8154 Roo.Shadow = function(config){
8155     Roo.apply(this, config);
8156     if(typeof this.mode != "string"){
8157         this.mode = this.defaultMode;
8158     }
8159     var o = this.offset, a = {h: 0};
8160     var rad = Math.floor(this.offset/2);
8161     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8162         case "drop":
8163             a.w = 0;
8164             a.l = a.t = o;
8165             a.t -= 1;
8166             if(Roo.isIE){
8167                 a.l -= this.offset + rad;
8168                 a.t -= this.offset + rad;
8169                 a.w -= rad;
8170                 a.h -= rad;
8171                 a.t += 1;
8172             }
8173         break;
8174         case "sides":
8175             a.w = (o*2);
8176             a.l = -o;
8177             a.t = o-1;
8178             if(Roo.isIE){
8179                 a.l -= (this.offset - rad);
8180                 a.t -= this.offset + rad;
8181                 a.l += 1;
8182                 a.w -= (this.offset - rad)*2;
8183                 a.w -= rad + 1;
8184                 a.h -= 1;
8185             }
8186         break;
8187         case "frame":
8188             a.w = a.h = (o*2);
8189             a.l = a.t = -o;
8190             a.t += 1;
8191             a.h -= 2;
8192             if(Roo.isIE){
8193                 a.l -= (this.offset - rad);
8194                 a.t -= (this.offset - rad);
8195                 a.l += 1;
8196                 a.w -= (this.offset + rad + 1);
8197                 a.h -= (this.offset + rad);
8198                 a.h += 1;
8199             }
8200         break;
8201     };
8202
8203     this.adjusts = a;
8204 };
8205
8206 Roo.Shadow.prototype = {
8207     /**
8208      * @cfg {String} mode
8209      * The shadow display mode.  Supports the following options:<br />
8210      * sides: Shadow displays on both sides and bottom only<br />
8211      * frame: Shadow displays equally on all four sides<br />
8212      * drop: Traditional bottom-right drop shadow (default)
8213      */
8214     /**
8215      * @cfg {String} offset
8216      * The number of pixels to offset the shadow from the element (defaults to 4)
8217      */
8218     offset: 4,
8219
8220     // private
8221     defaultMode: "drop",
8222
8223     /**
8224      * Displays the shadow under the target element
8225      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8226      */
8227     show : function(target){
8228         target = Roo.get(target);
8229         if(!this.el){
8230             this.el = Roo.Shadow.Pool.pull();
8231             if(this.el.dom.nextSibling != target.dom){
8232                 this.el.insertBefore(target);
8233             }
8234         }
8235         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8236         if(Roo.isIE){
8237             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8238         }
8239         this.realign(
8240             target.getLeft(true),
8241             target.getTop(true),
8242             target.getWidth(),
8243             target.getHeight()
8244         );
8245         this.el.dom.style.display = "block";
8246     },
8247
8248     /**
8249      * Returns true if the shadow is visible, else false
8250      */
8251     isVisible : function(){
8252         return this.el ? true : false;  
8253     },
8254
8255     /**
8256      * Direct alignment when values are already available. Show must be called at least once before
8257      * calling this method to ensure it is initialized.
8258      * @param {Number} left The target element left position
8259      * @param {Number} top The target element top position
8260      * @param {Number} width The target element width
8261      * @param {Number} height The target element height
8262      */
8263     realign : function(l, t, w, h){
8264         if(!this.el){
8265             return;
8266         }
8267         var a = this.adjusts, d = this.el.dom, s = d.style;
8268         var iea = 0;
8269         s.left = (l+a.l)+"px";
8270         s.top = (t+a.t)+"px";
8271         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8272  
8273         if(s.width != sws || s.height != shs){
8274             s.width = sws;
8275             s.height = shs;
8276             if(!Roo.isIE){
8277                 var cn = d.childNodes;
8278                 var sww = Math.max(0, (sw-12))+"px";
8279                 cn[0].childNodes[1].style.width = sww;
8280                 cn[1].childNodes[1].style.width = sww;
8281                 cn[2].childNodes[1].style.width = sww;
8282                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8283             }
8284         }
8285     },
8286
8287     /**
8288      * Hides this shadow
8289      */
8290     hide : function(){
8291         if(this.el){
8292             this.el.dom.style.display = "none";
8293             Roo.Shadow.Pool.push(this.el);
8294             delete this.el;
8295         }
8296     },
8297
8298     /**
8299      * Adjust the z-index of this shadow
8300      * @param {Number} zindex The new z-index
8301      */
8302     setZIndex : function(z){
8303         this.zIndex = z;
8304         if(this.el){
8305             this.el.setStyle("z-index", z);
8306         }
8307     }
8308 };
8309
8310 // Private utility class that manages the internal Shadow cache
8311 Roo.Shadow.Pool = function(){
8312     var p = [];
8313     var markup = Roo.isIE ?
8314                  '<div class="x-ie-shadow"></div>' :
8315                  '<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>';
8316     return {
8317         pull : function(){
8318             var sh = p.shift();
8319             if(!sh){
8320                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8321                 sh.autoBoxAdjust = false;
8322             }
8323             return sh;
8324         },
8325
8326         push : function(sh){
8327             p.push(sh);
8328         }
8329     };
8330 }();/*
8331  * Based on:
8332  * Ext JS Library 1.1.1
8333  * Copyright(c) 2006-2007, Ext JS, LLC.
8334  *
8335  * Originally Released Under LGPL - original licence link has changed is not relivant.
8336  *
8337  * Fork - LGPL
8338  * <script type="text/javascript">
8339  */
8340
8341 /**
8342  * @class Roo.BoxComponent
8343  * @extends Roo.Component
8344  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8345  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8346  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8347  * layout containers.
8348  * @constructor
8349  * @param {Roo.Element/String/Object} config The configuration options.
8350  */
8351 Roo.BoxComponent = function(config){
8352     Roo.Component.call(this, config);
8353     this.addEvents({
8354         /**
8355          * @event resize
8356          * Fires after the component is resized.
8357              * @param {Roo.Component} this
8358              * @param {Number} adjWidth The box-adjusted width that was set
8359              * @param {Number} adjHeight The box-adjusted height that was set
8360              * @param {Number} rawWidth The width that was originally specified
8361              * @param {Number} rawHeight The height that was originally specified
8362              */
8363         resize : true,
8364         /**
8365          * @event move
8366          * Fires after the component is moved.
8367              * @param {Roo.Component} this
8368              * @param {Number} x The new x position
8369              * @param {Number} y The new y position
8370              */
8371         move : true
8372     });
8373 };
8374
8375 Roo.extend(Roo.BoxComponent, Roo.Component, {
8376     // private, set in afterRender to signify that the component has been rendered
8377     boxReady : false,
8378     // private, used to defer height settings to subclasses
8379     deferHeight: false,
8380     /** @cfg {Number} width
8381      * width (optional) size of component
8382      */
8383      /** @cfg {Number} height
8384      * height (optional) size of component
8385      */
8386      
8387     /**
8388      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8389      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8390      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8391      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8392      * @return {Roo.BoxComponent} this
8393      */
8394     setSize : function(w, h){
8395         // support for standard size objects
8396         if(typeof w == 'object'){
8397             h = w.height;
8398             w = w.width;
8399         }
8400         // not rendered
8401         if(!this.boxReady){
8402             this.width = w;
8403             this.height = h;
8404             return this;
8405         }
8406
8407         // prevent recalcs when not needed
8408         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8409             return this;
8410         }
8411         this.lastSize = {width: w, height: h};
8412
8413         var adj = this.adjustSize(w, h);
8414         var aw = adj.width, ah = adj.height;
8415         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8416             var rz = this.getResizeEl();
8417             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8418                 rz.setSize(aw, ah);
8419             }else if(!this.deferHeight && ah !== undefined){
8420                 rz.setHeight(ah);
8421             }else if(aw !== undefined){
8422                 rz.setWidth(aw);
8423             }
8424             this.onResize(aw, ah, w, h);
8425             this.fireEvent('resize', this, aw, ah, w, h);
8426         }
8427         return this;
8428     },
8429
8430     /**
8431      * Gets the current size of the component's underlying element.
8432      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8433      */
8434     getSize : function(){
8435         return this.el.getSize();
8436     },
8437
8438     /**
8439      * Gets the current XY position of the component's underlying element.
8440      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8441      * @return {Array} The XY position of the element (e.g., [100, 200])
8442      */
8443     getPosition : function(local){
8444         if(local === true){
8445             return [this.el.getLeft(true), this.el.getTop(true)];
8446         }
8447         return this.xy || this.el.getXY();
8448     },
8449
8450     /**
8451      * Gets the current box measurements of the component's underlying element.
8452      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8453      * @returns {Object} box An object in the format {x, y, width, height}
8454      */
8455     getBox : function(local){
8456         var s = this.el.getSize();
8457         if(local){
8458             s.x = this.el.getLeft(true);
8459             s.y = this.el.getTop(true);
8460         }else{
8461             var xy = this.xy || this.el.getXY();
8462             s.x = xy[0];
8463             s.y = xy[1];
8464         }
8465         return s;
8466     },
8467
8468     /**
8469      * Sets the current box measurements of the component's underlying element.
8470      * @param {Object} box An object in the format {x, y, width, height}
8471      * @returns {Roo.BoxComponent} this
8472      */
8473     updateBox : function(box){
8474         this.setSize(box.width, box.height);
8475         this.setPagePosition(box.x, box.y);
8476         return this;
8477     },
8478
8479     // protected
8480     getResizeEl : function(){
8481         return this.resizeEl || this.el;
8482     },
8483
8484     // protected
8485     getPositionEl : function(){
8486         return this.positionEl || this.el;
8487     },
8488
8489     /**
8490      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8491      * This method fires the move event.
8492      * @param {Number} left The new left
8493      * @param {Number} top The new top
8494      * @returns {Roo.BoxComponent} this
8495      */
8496     setPosition : function(x, y){
8497         this.x = x;
8498         this.y = y;
8499         if(!this.boxReady){
8500             return this;
8501         }
8502         var adj = this.adjustPosition(x, y);
8503         var ax = adj.x, ay = adj.y;
8504
8505         var el = this.getPositionEl();
8506         if(ax !== undefined || ay !== undefined){
8507             if(ax !== undefined && ay !== undefined){
8508                 el.setLeftTop(ax, ay);
8509             }else if(ax !== undefined){
8510                 el.setLeft(ax);
8511             }else if(ay !== undefined){
8512                 el.setTop(ay);
8513             }
8514             this.onPosition(ax, ay);
8515             this.fireEvent('move', this, ax, ay);
8516         }
8517         return this;
8518     },
8519
8520     /**
8521      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8522      * This method fires the move event.
8523      * @param {Number} x The new x position
8524      * @param {Number} y The new y position
8525      * @returns {Roo.BoxComponent} this
8526      */
8527     setPagePosition : function(x, y){
8528         this.pageX = x;
8529         this.pageY = y;
8530         if(!this.boxReady){
8531             return;
8532         }
8533         if(x === undefined || y === undefined){ // cannot translate undefined points
8534             return;
8535         }
8536         var p = this.el.translatePoints(x, y);
8537         this.setPosition(p.left, p.top);
8538         return this;
8539     },
8540
8541     // private
8542     onRender : function(ct, position){
8543         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8544         if(this.resizeEl){
8545             this.resizeEl = Roo.get(this.resizeEl);
8546         }
8547         if(this.positionEl){
8548             this.positionEl = Roo.get(this.positionEl);
8549         }
8550     },
8551
8552     // private
8553     afterRender : function(){
8554         Roo.BoxComponent.superclass.afterRender.call(this);
8555         this.boxReady = true;
8556         this.setSize(this.width, this.height);
8557         if(this.x || this.y){
8558             this.setPosition(this.x, this.y);
8559         }
8560         if(this.pageX || this.pageY){
8561             this.setPagePosition(this.pageX, this.pageY);
8562         }
8563     },
8564
8565     /**
8566      * Force the component's size to recalculate based on the underlying element's current height and width.
8567      * @returns {Roo.BoxComponent} this
8568      */
8569     syncSize : function(){
8570         delete this.lastSize;
8571         this.setSize(this.el.getWidth(), this.el.getHeight());
8572         return this;
8573     },
8574
8575     /**
8576      * Called after the component is resized, this method is empty by default but can be implemented by any
8577      * subclass that needs to perform custom logic after a resize occurs.
8578      * @param {Number} adjWidth The box-adjusted width that was set
8579      * @param {Number} adjHeight The box-adjusted height that was set
8580      * @param {Number} rawWidth The width that was originally specified
8581      * @param {Number} rawHeight The height that was originally specified
8582      */
8583     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8584
8585     },
8586
8587     /**
8588      * Called after the component is moved, this method is empty by default but can be implemented by any
8589      * subclass that needs to perform custom logic after a move occurs.
8590      * @param {Number} x The new x position
8591      * @param {Number} y The new y position
8592      */
8593     onPosition : function(x, y){
8594
8595     },
8596
8597     // private
8598     adjustSize : function(w, h){
8599         if(this.autoWidth){
8600             w = 'auto';
8601         }
8602         if(this.autoHeight){
8603             h = 'auto';
8604         }
8605         return {width : w, height: h};
8606     },
8607
8608     // private
8609     adjustPosition : function(x, y){
8610         return {x : x, y: y};
8611     }
8612 });/*
8613  * Based on:
8614  * Ext JS Library 1.1.1
8615  * Copyright(c) 2006-2007, Ext JS, LLC.
8616  *
8617  * Originally Released Under LGPL - original licence link has changed is not relivant.
8618  *
8619  * Fork - LGPL
8620  * <script type="text/javascript">
8621  */
8622
8623
8624 /**
8625  * @class Roo.SplitBar
8626  * @extends Roo.util.Observable
8627  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8628  * <br><br>
8629  * Usage:
8630  * <pre><code>
8631 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8632                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8633 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8634 split.minSize = 100;
8635 split.maxSize = 600;
8636 split.animate = true;
8637 split.on('moved', splitterMoved);
8638 </code></pre>
8639  * @constructor
8640  * Create a new SplitBar
8641  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8642  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8643  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8644  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8645                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8646                         position of the SplitBar).
8647  */
8648 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8649     
8650     /** @private */
8651     this.el = Roo.get(dragElement, true);
8652     this.el.dom.unselectable = "on";
8653     /** @private */
8654     this.resizingEl = Roo.get(resizingElement, true);
8655
8656     /**
8657      * @private
8658      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8659      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8660      * @type Number
8661      */
8662     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8663     
8664     /**
8665      * The minimum size of the resizing element. (Defaults to 0)
8666      * @type Number
8667      */
8668     this.minSize = 0;
8669     
8670     /**
8671      * The maximum size of the resizing element. (Defaults to 2000)
8672      * @type Number
8673      */
8674     this.maxSize = 2000;
8675     
8676     /**
8677      * Whether to animate the transition to the new size
8678      * @type Boolean
8679      */
8680     this.animate = false;
8681     
8682     /**
8683      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8684      * @type Boolean
8685      */
8686     this.useShim = false;
8687     
8688     /** @private */
8689     this.shim = null;
8690     
8691     if(!existingProxy){
8692         /** @private */
8693         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8694     }else{
8695         this.proxy = Roo.get(existingProxy).dom;
8696     }
8697     /** @private */
8698     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8699     
8700     /** @private */
8701     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8702     
8703     /** @private */
8704     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8705     
8706     /** @private */
8707     this.dragSpecs = {};
8708     
8709     /**
8710      * @private The adapter to use to positon and resize elements
8711      */
8712     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8713     this.adapter.init(this);
8714     
8715     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8716         /** @private */
8717         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8718         this.el.addClass("x-splitbar-h");
8719     }else{
8720         /** @private */
8721         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8722         this.el.addClass("x-splitbar-v");
8723     }
8724     
8725     this.addEvents({
8726         /**
8727          * @event resize
8728          * Fires when the splitter is moved (alias for {@link #event-moved})
8729          * @param {Roo.SplitBar} this
8730          * @param {Number} newSize the new width or height
8731          */
8732         "resize" : true,
8733         /**
8734          * @event moved
8735          * Fires when the splitter is moved
8736          * @param {Roo.SplitBar} this
8737          * @param {Number} newSize the new width or height
8738          */
8739         "moved" : true,
8740         /**
8741          * @event beforeresize
8742          * Fires before the splitter is dragged
8743          * @param {Roo.SplitBar} this
8744          */
8745         "beforeresize" : true,
8746
8747         "beforeapply" : true
8748     });
8749
8750     Roo.util.Observable.call(this);
8751 };
8752
8753 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8754     onStartProxyDrag : function(x, y){
8755         this.fireEvent("beforeresize", this);
8756         if(!this.overlay){
8757             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8758             o.unselectable();
8759             o.enableDisplayMode("block");
8760             // all splitbars share the same overlay
8761             Roo.SplitBar.prototype.overlay = o;
8762         }
8763         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8764         this.overlay.show();
8765         Roo.get(this.proxy).setDisplayed("block");
8766         var size = this.adapter.getElementSize(this);
8767         this.activeMinSize = this.getMinimumSize();;
8768         this.activeMaxSize = this.getMaximumSize();;
8769         var c1 = size - this.activeMinSize;
8770         var c2 = Math.max(this.activeMaxSize - size, 0);
8771         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8772             this.dd.resetConstraints();
8773             this.dd.setXConstraint(
8774                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8775                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8776             );
8777             this.dd.setYConstraint(0, 0);
8778         }else{
8779             this.dd.resetConstraints();
8780             this.dd.setXConstraint(0, 0);
8781             this.dd.setYConstraint(
8782                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8783                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8784             );
8785          }
8786         this.dragSpecs.startSize = size;
8787         this.dragSpecs.startPoint = [x, y];
8788         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8789     },
8790     
8791     /** 
8792      * @private Called after the drag operation by the DDProxy
8793      */
8794     onEndProxyDrag : function(e){
8795         Roo.get(this.proxy).setDisplayed(false);
8796         var endPoint = Roo.lib.Event.getXY(e);
8797         if(this.overlay){
8798             this.overlay.hide();
8799         }
8800         var newSize;
8801         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8802             newSize = this.dragSpecs.startSize + 
8803                 (this.placement == Roo.SplitBar.LEFT ?
8804                     endPoint[0] - this.dragSpecs.startPoint[0] :
8805                     this.dragSpecs.startPoint[0] - endPoint[0]
8806                 );
8807         }else{
8808             newSize = this.dragSpecs.startSize + 
8809                 (this.placement == Roo.SplitBar.TOP ?
8810                     endPoint[1] - this.dragSpecs.startPoint[1] :
8811                     this.dragSpecs.startPoint[1] - endPoint[1]
8812                 );
8813         }
8814         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8815         if(newSize != this.dragSpecs.startSize){
8816             if(this.fireEvent('beforeapply', this, newSize) !== false){
8817                 this.adapter.setElementSize(this, newSize);
8818                 this.fireEvent("moved", this, newSize);
8819                 this.fireEvent("resize", this, newSize);
8820             }
8821         }
8822     },
8823     
8824     /**
8825      * Get the adapter this SplitBar uses
8826      * @return The adapter object
8827      */
8828     getAdapter : function(){
8829         return this.adapter;
8830     },
8831     
8832     /**
8833      * Set the adapter this SplitBar uses
8834      * @param {Object} adapter A SplitBar adapter object
8835      */
8836     setAdapter : function(adapter){
8837         this.adapter = adapter;
8838         this.adapter.init(this);
8839     },
8840     
8841     /**
8842      * Gets the minimum size for the resizing element
8843      * @return {Number} The minimum size
8844      */
8845     getMinimumSize : function(){
8846         return this.minSize;
8847     },
8848     
8849     /**
8850      * Sets the minimum size for the resizing element
8851      * @param {Number} minSize The minimum size
8852      */
8853     setMinimumSize : function(minSize){
8854         this.minSize = minSize;
8855     },
8856     
8857     /**
8858      * Gets the maximum size for the resizing element
8859      * @return {Number} The maximum size
8860      */
8861     getMaximumSize : function(){
8862         return this.maxSize;
8863     },
8864     
8865     /**
8866      * Sets the maximum size for the resizing element
8867      * @param {Number} maxSize The maximum size
8868      */
8869     setMaximumSize : function(maxSize){
8870         this.maxSize = maxSize;
8871     },
8872     
8873     /**
8874      * Sets the initialize size for the resizing element
8875      * @param {Number} size The initial size
8876      */
8877     setCurrentSize : function(size){
8878         var oldAnimate = this.animate;
8879         this.animate = false;
8880         this.adapter.setElementSize(this, size);
8881         this.animate = oldAnimate;
8882     },
8883     
8884     /**
8885      * Destroy this splitbar. 
8886      * @param {Boolean} removeEl True to remove the element
8887      */
8888     destroy : function(removeEl){
8889         if(this.shim){
8890             this.shim.remove();
8891         }
8892         this.dd.unreg();
8893         this.proxy.parentNode.removeChild(this.proxy);
8894         if(removeEl){
8895             this.el.remove();
8896         }
8897     }
8898 });
8899
8900 /**
8901  * @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.
8902  */
8903 Roo.SplitBar.createProxy = function(dir){
8904     var proxy = new Roo.Element(document.createElement("div"));
8905     proxy.unselectable();
8906     var cls = 'x-splitbar-proxy';
8907     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8908     document.body.appendChild(proxy.dom);
8909     return proxy.dom;
8910 };
8911
8912 /** 
8913  * @class Roo.SplitBar.BasicLayoutAdapter
8914  * Default Adapter. It assumes the splitter and resizing element are not positioned
8915  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8916  */
8917 Roo.SplitBar.BasicLayoutAdapter = function(){
8918 };
8919
8920 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8921     // do nothing for now
8922     init : function(s){
8923     
8924     },
8925     /**
8926      * Called before drag operations to get the current size of the resizing element. 
8927      * @param {Roo.SplitBar} s The SplitBar using this adapter
8928      */
8929      getElementSize : function(s){
8930         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8931             return s.resizingEl.getWidth();
8932         }else{
8933             return s.resizingEl.getHeight();
8934         }
8935     },
8936     
8937     /**
8938      * Called after drag operations to set the size of the resizing element.
8939      * @param {Roo.SplitBar} s The SplitBar using this adapter
8940      * @param {Number} newSize The new size to set
8941      * @param {Function} onComplete A function to be invoked when resizing is complete
8942      */
8943     setElementSize : function(s, newSize, onComplete){
8944         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8945             if(!s.animate){
8946                 s.resizingEl.setWidth(newSize);
8947                 if(onComplete){
8948                     onComplete(s, newSize);
8949                 }
8950             }else{
8951                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8952             }
8953         }else{
8954             
8955             if(!s.animate){
8956                 s.resizingEl.setHeight(newSize);
8957                 if(onComplete){
8958                     onComplete(s, newSize);
8959                 }
8960             }else{
8961                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
8962             }
8963         }
8964     }
8965 };
8966
8967 /** 
8968  *@class Roo.SplitBar.AbsoluteLayoutAdapter
8969  * @extends Roo.SplitBar.BasicLayoutAdapter
8970  * Adapter that  moves the splitter element to align with the resized sizing element. 
8971  * Used with an absolute positioned SplitBar.
8972  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
8973  * document.body, make sure you assign an id to the body element.
8974  */
8975 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
8976     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
8977     this.container = Roo.get(container);
8978 };
8979
8980 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
8981     init : function(s){
8982         this.basic.init(s);
8983     },
8984     
8985     getElementSize : function(s){
8986         return this.basic.getElementSize(s);
8987     },
8988     
8989     setElementSize : function(s, newSize, onComplete){
8990         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
8991     },
8992     
8993     moveSplitter : function(s){
8994         var yes = Roo.SplitBar;
8995         switch(s.placement){
8996             case yes.LEFT:
8997                 s.el.setX(s.resizingEl.getRight());
8998                 break;
8999             case yes.RIGHT:
9000                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
9001                 break;
9002             case yes.TOP:
9003                 s.el.setY(s.resizingEl.getBottom());
9004                 break;
9005             case yes.BOTTOM:
9006                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
9007                 break;
9008         }
9009     }
9010 };
9011
9012 /**
9013  * Orientation constant - Create a vertical SplitBar
9014  * @static
9015  * @type Number
9016  */
9017 Roo.SplitBar.VERTICAL = 1;
9018
9019 /**
9020  * Orientation constant - Create a horizontal SplitBar
9021  * @static
9022  * @type Number
9023  */
9024 Roo.SplitBar.HORIZONTAL = 2;
9025
9026 /**
9027  * Placement constant - The resizing element is to the left of the splitter element
9028  * @static
9029  * @type Number
9030  */
9031 Roo.SplitBar.LEFT = 1;
9032
9033 /**
9034  * Placement constant - The resizing element is to the right of the splitter element
9035  * @static
9036  * @type Number
9037  */
9038 Roo.SplitBar.RIGHT = 2;
9039
9040 /**
9041  * Placement constant - The resizing element is positioned above the splitter element
9042  * @static
9043  * @type Number
9044  */
9045 Roo.SplitBar.TOP = 3;
9046
9047 /**
9048  * Placement constant - The resizing element is positioned under splitter element
9049  * @static
9050  * @type Number
9051  */
9052 Roo.SplitBar.BOTTOM = 4;
9053 /*
9054  * Based on:
9055  * Ext JS Library 1.1.1
9056  * Copyright(c) 2006-2007, Ext JS, LLC.
9057  *
9058  * Originally Released Under LGPL - original licence link has changed is not relivant.
9059  *
9060  * Fork - LGPL
9061  * <script type="text/javascript">
9062  */
9063
9064 /**
9065  * @class Roo.View
9066  * @extends Roo.util.Observable
9067  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9068  * This class also supports single and multi selection modes. <br>
9069  * Create a data model bound view:
9070  <pre><code>
9071  var store = new Roo.data.Store(...);
9072
9073  var view = new Roo.View({
9074     el : "my-element",
9075     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9076  
9077     singleSelect: true,
9078     selectedClass: "ydataview-selected",
9079     store: store
9080  });
9081
9082  // listen for node click?
9083  view.on("click", function(vw, index, node, e){
9084  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9085  });
9086
9087  // load XML data
9088  dataModel.load("foobar.xml");
9089  </code></pre>
9090  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9091  * <br><br>
9092  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9093  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9094  * 
9095  * Note: old style constructor is still suported (container, template, config)
9096  * 
9097  * @constructor
9098  * Create a new View
9099  * @param {Object} config The config object
9100  * 
9101  */
9102 Roo.View = function(config, depreciated_tpl, depreciated_config){
9103     
9104     if (typeof(depreciated_tpl) == 'undefined') {
9105         // new way.. - universal constructor.
9106         Roo.apply(this, config);
9107         this.el  = Roo.get(this.el);
9108     } else {
9109         // old format..
9110         this.el  = Roo.get(config);
9111         this.tpl = depreciated_tpl;
9112         Roo.apply(this, depreciated_config);
9113     }
9114      
9115     
9116     if(typeof(this.tpl) == "string"){
9117         this.tpl = new Roo.Template(this.tpl);
9118     } else {
9119         // support xtype ctors..
9120         this.tpl = new Roo.factory(this.tpl, Roo);
9121     }
9122     
9123     
9124     this.tpl.compile();
9125    
9126
9127      
9128     /** @private */
9129     this.addEvents({
9130     /**
9131      * @event beforeclick
9132      * Fires before a click is processed. Returns false to cancel the default action.
9133      * @param {Roo.View} this
9134      * @param {Number} index The index of the target node
9135      * @param {HTMLElement} node The target node
9136      * @param {Roo.EventObject} e The raw event object
9137      */
9138         "beforeclick" : true,
9139     /**
9140      * @event click
9141      * Fires when a template node is clicked.
9142      * @param {Roo.View} this
9143      * @param {Number} index The index of the target node
9144      * @param {HTMLElement} node The target node
9145      * @param {Roo.EventObject} e The raw event object
9146      */
9147         "click" : true,
9148     /**
9149      * @event dblclick
9150      * Fires when a template node is double clicked.
9151      * @param {Roo.View} this
9152      * @param {Number} index The index of the target node
9153      * @param {HTMLElement} node The target node
9154      * @param {Roo.EventObject} e The raw event object
9155      */
9156         "dblclick" : true,
9157     /**
9158      * @event contextmenu
9159      * Fires when a template node is right clicked.
9160      * @param {Roo.View} this
9161      * @param {Number} index The index of the target node
9162      * @param {HTMLElement} node The target node
9163      * @param {Roo.EventObject} e The raw event object
9164      */
9165         "contextmenu" : true,
9166     /**
9167      * @event selectionchange
9168      * Fires when the selected nodes change.
9169      * @param {Roo.View} this
9170      * @param {Array} selections Array of the selected nodes
9171      */
9172         "selectionchange" : true,
9173
9174     /**
9175      * @event beforeselect
9176      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9177      * @param {Roo.View} this
9178      * @param {HTMLElement} node The node to be selected
9179      * @param {Array} selections Array of currently selected nodes
9180      */
9181         "beforeselect" : true
9182     });
9183
9184     this.el.on({
9185         "click": this.onClick,
9186         "dblclick": this.onDblClick,
9187         "contextmenu": this.onContextMenu,
9188         scope:this
9189     });
9190
9191     this.selections = [];
9192     this.nodes = [];
9193     this.cmp = new Roo.CompositeElementLite([]);
9194     if(this.store){
9195         this.store = Roo.factory(this.store, Roo.data);
9196         this.setStore(this.store, true);
9197     }
9198     Roo.View.superclass.constructor.call(this);
9199 };
9200
9201 Roo.extend(Roo.View, Roo.util.Observable, {
9202     
9203      /**
9204      * @cfg {Roo.data.Store} store Data store to load data from.
9205      */
9206     store : false,
9207     
9208     /**
9209      * @cfg {String|Roo.Element} el The container element.
9210      */
9211     el : '',
9212     
9213     /**
9214      * @cfg {String|Roo.Template} tpl The template used by this View 
9215      */
9216     tpl : false,
9217     
9218     /**
9219      * @cfg {String} selectedClass The css class to add to selected nodes
9220      */
9221     selectedClass : "x-view-selected",
9222      /**
9223      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9224      */
9225     emptyText : "",
9226     /**
9227      * @cfg {Boolean} multiSelect Allow multiple selection
9228      */
9229     
9230     multiSelect : false,
9231     /**
9232      * @cfg {Boolean} singleSelect Allow single selection
9233      */
9234     singleSelect:  false,
9235     
9236     /**
9237      * Returns the element this view is bound to.
9238      * @return {Roo.Element}
9239      */
9240     getEl : function(){
9241         return this.el;
9242     },
9243
9244     /**
9245      * Refreshes the view.
9246      */
9247     refresh : function(){
9248         var t = this.tpl;
9249         this.clearSelections();
9250         this.el.update("");
9251         var html = [];
9252         var records = this.store.getRange();
9253         if(records.length < 1){
9254             this.el.update(this.emptyText);
9255             return;
9256         }
9257         for(var i = 0, len = records.length; i < len; i++){
9258             var data = this.prepareData(records[i].data, i, records[i]);
9259             html[html.length] = t.apply(data);
9260         }
9261         this.el.update(html.join(""));
9262         this.nodes = this.el.dom.childNodes;
9263         this.updateIndexes(0);
9264     },
9265
9266     /**
9267      * Function to override to reformat the data that is sent to
9268      * the template for each node.
9269      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9270      * a JSON object for an UpdateManager bound view).
9271      */
9272     prepareData : function(data){
9273         return data;
9274     },
9275
9276     onUpdate : function(ds, record){
9277         this.clearSelections();
9278         var index = this.store.indexOf(record);
9279         var n = this.nodes[index];
9280         this.tpl.insertBefore(n, this.prepareData(record.data));
9281         n.parentNode.removeChild(n);
9282         this.updateIndexes(index, index);
9283     },
9284
9285     onAdd : function(ds, records, index){
9286         this.clearSelections();
9287         if(this.nodes.length == 0){
9288             this.refresh();
9289             return;
9290         }
9291         var n = this.nodes[index];
9292         for(var i = 0, len = records.length; i < len; i++){
9293             var d = this.prepareData(records[i].data);
9294             if(n){
9295                 this.tpl.insertBefore(n, d);
9296             }else{
9297                 this.tpl.append(this.el, d);
9298             }
9299         }
9300         this.updateIndexes(index);
9301     },
9302
9303     onRemove : function(ds, record, index){
9304         this.clearSelections();
9305         this.el.dom.removeChild(this.nodes[index]);
9306         this.updateIndexes(index);
9307     },
9308
9309     /**
9310      * Refresh an individual node.
9311      * @param {Number} index
9312      */
9313     refreshNode : function(index){
9314         this.onUpdate(this.store, this.store.getAt(index));
9315     },
9316
9317     updateIndexes : function(startIndex, endIndex){
9318         var ns = this.nodes;
9319         startIndex = startIndex || 0;
9320         endIndex = endIndex || ns.length - 1;
9321         for(var i = startIndex; i <= endIndex; i++){
9322             ns[i].nodeIndex = i;
9323         }
9324     },
9325
9326     /**
9327      * Changes the data store this view uses and refresh the view.
9328      * @param {Store} store
9329      */
9330     setStore : function(store, initial){
9331         if(!initial && this.store){
9332             this.store.un("datachanged", this.refresh);
9333             this.store.un("add", this.onAdd);
9334             this.store.un("remove", this.onRemove);
9335             this.store.un("update", this.onUpdate);
9336             this.store.un("clear", this.refresh);
9337         }
9338         if(store){
9339           
9340             store.on("datachanged", this.refresh, this);
9341             store.on("add", this.onAdd, this);
9342             store.on("remove", this.onRemove, this);
9343             store.on("update", this.onUpdate, this);
9344             store.on("clear", this.refresh, this);
9345         }
9346         
9347         if(store){
9348             this.refresh();
9349         }
9350     },
9351
9352     /**
9353      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9354      * @param {HTMLElement} node
9355      * @return {HTMLElement} The template node
9356      */
9357     findItemFromChild : function(node){
9358         var el = this.el.dom;
9359         if(!node || node.parentNode == el){
9360                     return node;
9361             }
9362             var p = node.parentNode;
9363             while(p && p != el){
9364             if(p.parentNode == el){
9365                 return p;
9366             }
9367             p = p.parentNode;
9368         }
9369             return null;
9370     },
9371
9372     /** @ignore */
9373     onClick : function(e){
9374         var item = this.findItemFromChild(e.getTarget());
9375         if(item){
9376             var index = this.indexOf(item);
9377             if(this.onItemClick(item, index, e) !== false){
9378                 this.fireEvent("click", this, index, item, e);
9379             }
9380         }else{
9381             this.clearSelections();
9382         }
9383     },
9384
9385     /** @ignore */
9386     onContextMenu : function(e){
9387         var item = this.findItemFromChild(e.getTarget());
9388         if(item){
9389             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9390         }
9391     },
9392
9393     /** @ignore */
9394     onDblClick : function(e){
9395         var item = this.findItemFromChild(e.getTarget());
9396         if(item){
9397             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9398         }
9399     },
9400
9401     onItemClick : function(item, index, e){
9402         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9403             return false;
9404         }
9405         if(this.multiSelect || this.singleSelect){
9406             if(this.multiSelect && e.shiftKey && this.lastSelection){
9407                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9408             }else{
9409                 this.select(item, this.multiSelect && e.ctrlKey);
9410                 this.lastSelection = item;
9411             }
9412             e.preventDefault();
9413         }
9414         return true;
9415     },
9416
9417     /**
9418      * Get the number of selected nodes.
9419      * @return {Number}
9420      */
9421     getSelectionCount : function(){
9422         return this.selections.length;
9423     },
9424
9425     /**
9426      * Get the currently selected nodes.
9427      * @return {Array} An array of HTMLElements
9428      */
9429     getSelectedNodes : function(){
9430         return this.selections;
9431     },
9432
9433     /**
9434      * Get the indexes of the selected nodes.
9435      * @return {Array}
9436      */
9437     getSelectedIndexes : function(){
9438         var indexes = [], s = this.selections;
9439         for(var i = 0, len = s.length; i < len; i++){
9440             indexes.push(s[i].nodeIndex);
9441         }
9442         return indexes;
9443     },
9444
9445     /**
9446      * Clear all selections
9447      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9448      */
9449     clearSelections : function(suppressEvent){
9450         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9451             this.cmp.elements = this.selections;
9452             this.cmp.removeClass(this.selectedClass);
9453             this.selections = [];
9454             if(!suppressEvent){
9455                 this.fireEvent("selectionchange", this, this.selections);
9456             }
9457         }
9458     },
9459
9460     /**
9461      * Returns true if the passed node is selected
9462      * @param {HTMLElement/Number} node The node or node index
9463      * @return {Boolean}
9464      */
9465     isSelected : function(node){
9466         var s = this.selections;
9467         if(s.length < 1){
9468             return false;
9469         }
9470         node = this.getNode(node);
9471         return s.indexOf(node) !== -1;
9472     },
9473
9474     /**
9475      * Selects nodes.
9476      * @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
9477      * @param {Boolean} keepExisting (optional) true to keep existing selections
9478      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9479      */
9480     select : function(nodeInfo, keepExisting, suppressEvent){
9481         if(nodeInfo instanceof Array){
9482             if(!keepExisting){
9483                 this.clearSelections(true);
9484             }
9485             for(var i = 0, len = nodeInfo.length; i < len; i++){
9486                 this.select(nodeInfo[i], true, true);
9487             }
9488         } else{
9489             var node = this.getNode(nodeInfo);
9490             if(node && !this.isSelected(node)){
9491                 if(!keepExisting){
9492                     this.clearSelections(true);
9493                 }
9494                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9495                     Roo.fly(node).addClass(this.selectedClass);
9496                     this.selections.push(node);
9497                     if(!suppressEvent){
9498                         this.fireEvent("selectionchange", this, this.selections);
9499                     }
9500                 }
9501             }
9502         }
9503     },
9504
9505     /**
9506      * Gets a template node.
9507      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9508      * @return {HTMLElement} The node or null if it wasn't found
9509      */
9510     getNode : function(nodeInfo){
9511         if(typeof nodeInfo == "string"){
9512             return document.getElementById(nodeInfo);
9513         }else if(typeof nodeInfo == "number"){
9514             return this.nodes[nodeInfo];
9515         }
9516         return nodeInfo;
9517     },
9518
9519     /**
9520      * Gets a range template nodes.
9521      * @param {Number} startIndex
9522      * @param {Number} endIndex
9523      * @return {Array} An array of nodes
9524      */
9525     getNodes : function(start, end){
9526         var ns = this.nodes;
9527         start = start || 0;
9528         end = typeof end == "undefined" ? ns.length - 1 : end;
9529         var nodes = [];
9530         if(start <= end){
9531             for(var i = start; i <= end; i++){
9532                 nodes.push(ns[i]);
9533             }
9534         } else{
9535             for(var i = start; i >= end; i--){
9536                 nodes.push(ns[i]);
9537             }
9538         }
9539         return nodes;
9540     },
9541
9542     /**
9543      * Finds the index of the passed node
9544      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9545      * @return {Number} The index of the node or -1
9546      */
9547     indexOf : function(node){
9548         node = this.getNode(node);
9549         if(typeof node.nodeIndex == "number"){
9550             return node.nodeIndex;
9551         }
9552         var ns = this.nodes;
9553         for(var i = 0, len = ns.length; i < len; i++){
9554             if(ns[i] == node){
9555                 return i;
9556             }
9557         }
9558         return -1;
9559     }
9560 });
9561 /*
9562  * Based on:
9563  * Ext JS Library 1.1.1
9564  * Copyright(c) 2006-2007, Ext JS, LLC.
9565  *
9566  * Originally Released Under LGPL - original licence link has changed is not relivant.
9567  *
9568  * Fork - LGPL
9569  * <script type="text/javascript">
9570  */
9571
9572 /**
9573  * @class Roo.JsonView
9574  * @extends Roo.View
9575  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9576 <pre><code>
9577 var view = new Roo.JsonView({
9578     container: "my-element",
9579     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9580     multiSelect: true, 
9581     jsonRoot: "data" 
9582 });
9583
9584 // listen for node click?
9585 view.on("click", function(vw, index, node, e){
9586     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9587 });
9588
9589 // direct load of JSON data
9590 view.load("foobar.php");
9591
9592 // Example from my blog list
9593 var tpl = new Roo.Template(
9594     '&lt;div class="entry"&gt;' +
9595     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9596     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9597     "&lt;/div&gt;&lt;hr /&gt;"
9598 );
9599
9600 var moreView = new Roo.JsonView({
9601     container :  "entry-list", 
9602     template : tpl,
9603     jsonRoot: "posts"
9604 });
9605 moreView.on("beforerender", this.sortEntries, this);
9606 moreView.load({
9607     url: "/blog/get-posts.php",
9608     params: "allposts=true",
9609     text: "Loading Blog Entries..."
9610 });
9611 </code></pre>
9612
9613 * Note: old code is supported with arguments : (container, template, config)
9614
9615
9616  * @constructor
9617  * Create a new JsonView
9618  * 
9619  * @param {Object} config The config object
9620  * 
9621  */
9622 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9623     
9624     
9625     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9626
9627     var um = this.el.getUpdateManager();
9628     um.setRenderer(this);
9629     um.on("update", this.onLoad, this);
9630     um.on("failure", this.onLoadException, this);
9631
9632     /**
9633      * @event beforerender
9634      * Fires before rendering of the downloaded JSON data.
9635      * @param {Roo.JsonView} this
9636      * @param {Object} data The JSON data loaded
9637      */
9638     /**
9639      * @event load
9640      * Fires when data is loaded.
9641      * @param {Roo.JsonView} this
9642      * @param {Object} data The JSON data loaded
9643      * @param {Object} response The raw Connect response object
9644      */
9645     /**
9646      * @event loadexception
9647      * Fires when loading fails.
9648      * @param {Roo.JsonView} this
9649      * @param {Object} response The raw Connect response object
9650      */
9651     this.addEvents({
9652         'beforerender' : true,
9653         'load' : true,
9654         'loadexception' : true
9655     });
9656 };
9657 Roo.extend(Roo.JsonView, Roo.View, {
9658     /**
9659      * @type {String} The root property in the loaded JSON object that contains the data
9660      */
9661     jsonRoot : "",
9662
9663     /**
9664      * Refreshes the view.
9665      */
9666     refresh : function(){
9667         this.clearSelections();
9668         this.el.update("");
9669         var html = [];
9670         var o = this.jsonData;
9671         if(o && o.length > 0){
9672             for(var i = 0, len = o.length; i < len; i++){
9673                 var data = this.prepareData(o[i], i, o);
9674                 html[html.length] = this.tpl.apply(data);
9675             }
9676         }else{
9677             html.push(this.emptyText);
9678         }
9679         this.el.update(html.join(""));
9680         this.nodes = this.el.dom.childNodes;
9681         this.updateIndexes(0);
9682     },
9683
9684     /**
9685      * 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.
9686      * @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:
9687      <pre><code>
9688      view.load({
9689          url: "your-url.php",
9690          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9691          callback: yourFunction,
9692          scope: yourObject, //(optional scope)
9693          discardUrl: false,
9694          nocache: false,
9695          text: "Loading...",
9696          timeout: 30,
9697          scripts: false
9698      });
9699      </code></pre>
9700      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9701      * 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.
9702      * @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}
9703      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9704      * @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.
9705      */
9706     load : function(){
9707         var um = this.el.getUpdateManager();
9708         um.update.apply(um, arguments);
9709     },
9710
9711     render : function(el, response){
9712         this.clearSelections();
9713         this.el.update("");
9714         var o;
9715         try{
9716             o = Roo.util.JSON.decode(response.responseText);
9717             if(this.jsonRoot){
9718                 
9719                 o = o[this.jsonRoot];
9720             }
9721         } catch(e){
9722         }
9723         /**
9724          * The current JSON data or null
9725          */
9726         this.jsonData = o;
9727         this.beforeRender();
9728         this.refresh();
9729     },
9730
9731 /**
9732  * Get the number of records in the current JSON dataset
9733  * @return {Number}
9734  */
9735     getCount : function(){
9736         return this.jsonData ? this.jsonData.length : 0;
9737     },
9738
9739 /**
9740  * Returns the JSON object for the specified node(s)
9741  * @param {HTMLElement/Array} node The node or an array of nodes
9742  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9743  * you get the JSON object for the node
9744  */
9745     getNodeData : function(node){
9746         if(node instanceof Array){
9747             var data = [];
9748             for(var i = 0, len = node.length; i < len; i++){
9749                 data.push(this.getNodeData(node[i]));
9750             }
9751             return data;
9752         }
9753         return this.jsonData[this.indexOf(node)] || null;
9754     },
9755
9756     beforeRender : function(){
9757         this.snapshot = this.jsonData;
9758         if(this.sortInfo){
9759             this.sort.apply(this, this.sortInfo);
9760         }
9761         this.fireEvent("beforerender", this, this.jsonData);
9762     },
9763
9764     onLoad : function(el, o){
9765         this.fireEvent("load", this, this.jsonData, o);
9766     },
9767
9768     onLoadException : function(el, o){
9769         this.fireEvent("loadexception", this, o);
9770     },
9771
9772 /**
9773  * Filter the data by a specific property.
9774  * @param {String} property A property on your JSON objects
9775  * @param {String/RegExp} value Either string that the property values
9776  * should start with, or a RegExp to test against the property
9777  */
9778     filter : function(property, value){
9779         if(this.jsonData){
9780             var data = [];
9781             var ss = this.snapshot;
9782             if(typeof value == "string"){
9783                 var vlen = value.length;
9784                 if(vlen == 0){
9785                     this.clearFilter();
9786                     return;
9787                 }
9788                 value = value.toLowerCase();
9789                 for(var i = 0, len = ss.length; i < len; i++){
9790                     var o = ss[i];
9791                     if(o[property].substr(0, vlen).toLowerCase() == value){
9792                         data.push(o);
9793                     }
9794                 }
9795             } else if(value.exec){ // regex?
9796                 for(var i = 0, len = ss.length; i < len; i++){
9797                     var o = ss[i];
9798                     if(value.test(o[property])){
9799                         data.push(o);
9800                     }
9801                 }
9802             } else{
9803                 return;
9804             }
9805             this.jsonData = data;
9806             this.refresh();
9807         }
9808     },
9809
9810 /**
9811  * Filter by a function. The passed function will be called with each
9812  * object in the current dataset. If the function returns true the value is kept,
9813  * otherwise it is filtered.
9814  * @param {Function} fn
9815  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9816  */
9817     filterBy : function(fn, scope){
9818         if(this.jsonData){
9819             var data = [];
9820             var ss = this.snapshot;
9821             for(var i = 0, len = ss.length; i < len; i++){
9822                 var o = ss[i];
9823                 if(fn.call(scope || this, o)){
9824                     data.push(o);
9825                 }
9826             }
9827             this.jsonData = data;
9828             this.refresh();
9829         }
9830     },
9831
9832 /**
9833  * Clears the current filter.
9834  */
9835     clearFilter : function(){
9836         if(this.snapshot && this.jsonData != this.snapshot){
9837             this.jsonData = this.snapshot;
9838             this.refresh();
9839         }
9840     },
9841
9842
9843 /**
9844  * Sorts the data for this view and refreshes it.
9845  * @param {String} property A property on your JSON objects to sort on
9846  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9847  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9848  */
9849     sort : function(property, dir, sortType){
9850         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9851         if(this.jsonData){
9852             var p = property;
9853             var dsc = dir && dir.toLowerCase() == "desc";
9854             var f = function(o1, o2){
9855                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9856                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9857                 ;
9858                 if(v1 < v2){
9859                     return dsc ? +1 : -1;
9860                 } else if(v1 > v2){
9861                     return dsc ? -1 : +1;
9862                 } else{
9863                     return 0;
9864                 }
9865             };
9866             this.jsonData.sort(f);
9867             this.refresh();
9868             if(this.jsonData != this.snapshot){
9869                 this.snapshot.sort(f);
9870             }
9871         }
9872     }
9873 });/*
9874  * Based on:
9875  * Ext JS Library 1.1.1
9876  * Copyright(c) 2006-2007, Ext JS, LLC.
9877  *
9878  * Originally Released Under LGPL - original licence link has changed is not relivant.
9879  *
9880  * Fork - LGPL
9881  * <script type="text/javascript">
9882  */
9883  
9884
9885 /**
9886  * @class Roo.ColorPalette
9887  * @extends Roo.Component
9888  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9889  * Here's an example of typical usage:
9890  * <pre><code>
9891 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9892 cp.render('my-div');
9893
9894 cp.on('select', function(palette, selColor){
9895     // do something with selColor
9896 });
9897 </code></pre>
9898  * @constructor
9899  * Create a new ColorPalette
9900  * @param {Object} config The config object
9901  */
9902 Roo.ColorPalette = function(config){
9903     Roo.ColorPalette.superclass.constructor.call(this, config);
9904     this.addEvents({
9905         /**
9906              * @event select
9907              * Fires when a color is selected
9908              * @param {ColorPalette} this
9909              * @param {String} color The 6-digit color hex code (without the # symbol)
9910              */
9911         select: true
9912     });
9913
9914     if(this.handler){
9915         this.on("select", this.handler, this.scope, true);
9916     }
9917 };
9918 Roo.extend(Roo.ColorPalette, Roo.Component, {
9919     /**
9920      * @cfg {String} itemCls
9921      * The CSS class to apply to the containing element (defaults to "x-color-palette")
9922      */
9923     itemCls : "x-color-palette",
9924     /**
9925      * @cfg {String} value
9926      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
9927      * the hex codes are case-sensitive.
9928      */
9929     value : null,
9930     clickEvent:'click',
9931     // private
9932     ctype: "Roo.ColorPalette",
9933
9934     /**
9935      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
9936      */
9937     allowReselect : false,
9938
9939     /**
9940      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
9941      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
9942      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
9943      * of colors with the width setting until the box is symmetrical.</p>
9944      * <p>You can override individual colors if needed:</p>
9945      * <pre><code>
9946 var cp = new Roo.ColorPalette();
9947 cp.colors[0] = "FF0000";  // change the first box to red
9948 </code></pre>
9949
9950 Or you can provide a custom array of your own for complete control:
9951 <pre><code>
9952 var cp = new Roo.ColorPalette();
9953 cp.colors = ["000000", "993300", "333300"];
9954 </code></pre>
9955      * @type Array
9956      */
9957     colors : [
9958         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
9959         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
9960         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
9961         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
9962         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
9963     ],
9964
9965     // private
9966     onRender : function(container, position){
9967         var t = new Roo.MasterTemplate(
9968             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
9969         );
9970         var c = this.colors;
9971         for(var i = 0, len = c.length; i < len; i++){
9972             t.add([c[i]]);
9973         }
9974         var el = document.createElement("div");
9975         el.className = this.itemCls;
9976         t.overwrite(el);
9977         container.dom.insertBefore(el, position);
9978         this.el = Roo.get(el);
9979         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
9980         if(this.clickEvent != 'click'){
9981             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
9982         }
9983     },
9984
9985     // private
9986     afterRender : function(){
9987         Roo.ColorPalette.superclass.afterRender.call(this);
9988         if(this.value){
9989             var s = this.value;
9990             this.value = null;
9991             this.select(s);
9992         }
9993     },
9994
9995     // private
9996     handleClick : function(e, t){
9997         e.preventDefault();
9998         if(!this.disabled){
9999             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
10000             this.select(c.toUpperCase());
10001         }
10002     },
10003
10004     /**
10005      * Selects the specified color in the palette (fires the select event)
10006      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10007      */
10008     select : function(color){
10009         color = color.replace("#", "");
10010         if(color != this.value || this.allowReselect){
10011             var el = this.el;
10012             if(this.value){
10013                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10014             }
10015             el.child("a.color-"+color).addClass("x-color-palette-sel");
10016             this.value = color;
10017             this.fireEvent("select", this, color);
10018         }
10019     }
10020 });/*
10021  * Based on:
10022  * Ext JS Library 1.1.1
10023  * Copyright(c) 2006-2007, Ext JS, LLC.
10024  *
10025  * Originally Released Under LGPL - original licence link has changed is not relivant.
10026  *
10027  * Fork - LGPL
10028  * <script type="text/javascript">
10029  */
10030  
10031 /**
10032  * @class Roo.DatePicker
10033  * @extends Roo.Component
10034  * Simple date picker class.
10035  * @constructor
10036  * Create a new DatePicker
10037  * @param {Object} config The config object
10038  */
10039 Roo.DatePicker = function(config){
10040     Roo.DatePicker.superclass.constructor.call(this, config);
10041
10042     this.value = config && config.value ?
10043                  config.value.clearTime() : new Date().clearTime();
10044
10045     this.addEvents({
10046         /**
10047              * @event select
10048              * Fires when a date is selected
10049              * @param {DatePicker} this
10050              * @param {Date} date The selected date
10051              */
10052         select: true
10053     });
10054
10055     if(this.handler){
10056         this.on("select", this.handler,  this.scope || this);
10057     }
10058     // build the disabledDatesRE
10059     if(!this.disabledDatesRE && this.disabledDates){
10060         var dd = this.disabledDates;
10061         var re = "(?:";
10062         for(var i = 0; i < dd.length; i++){
10063             re += dd[i];
10064             if(i != dd.length-1) re += "|";
10065         }
10066         this.disabledDatesRE = new RegExp(re + ")");
10067     }
10068 };
10069
10070 Roo.extend(Roo.DatePicker, Roo.Component, {
10071     /**
10072      * @cfg {String} todayText
10073      * The text to display on the button that selects the current date (defaults to "Today")
10074      */
10075     todayText : "Today",
10076     /**
10077      * @cfg {String} okText
10078      * The text to display on the ok button
10079      */
10080     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10081     /**
10082      * @cfg {String} cancelText
10083      * The text to display on the cancel button
10084      */
10085     cancelText : "Cancel",
10086     /**
10087      * @cfg {String} todayTip
10088      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10089      */
10090     todayTip : "{0} (Spacebar)",
10091     /**
10092      * @cfg {Date} minDate
10093      * Minimum allowable date (JavaScript date object, defaults to null)
10094      */
10095     minDate : null,
10096     /**
10097      * @cfg {Date} maxDate
10098      * Maximum allowable date (JavaScript date object, defaults to null)
10099      */
10100     maxDate : null,
10101     /**
10102      * @cfg {String} minText
10103      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10104      */
10105     minText : "This date is before the minimum date",
10106     /**
10107      * @cfg {String} maxText
10108      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10109      */
10110     maxText : "This date is after the maximum date",
10111     /**
10112      * @cfg {String} format
10113      * The default date format string which can be overriden for localization support.  The format must be
10114      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10115      */
10116     format : "m/d/y",
10117     /**
10118      * @cfg {Array} disabledDays
10119      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10120      */
10121     disabledDays : null,
10122     /**
10123      * @cfg {String} disabledDaysText
10124      * The tooltip to display when the date falls on a disabled day (defaults to "")
10125      */
10126     disabledDaysText : "",
10127     /**
10128      * @cfg {RegExp} disabledDatesRE
10129      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10130      */
10131     disabledDatesRE : null,
10132     /**
10133      * @cfg {String} disabledDatesText
10134      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10135      */
10136     disabledDatesText : "",
10137     /**
10138      * @cfg {Boolean} constrainToViewport
10139      * True to constrain the date picker to the viewport (defaults to true)
10140      */
10141     constrainToViewport : true,
10142     /**
10143      * @cfg {Array} monthNames
10144      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10145      */
10146     monthNames : Date.monthNames,
10147     /**
10148      * @cfg {Array} dayNames
10149      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10150      */
10151     dayNames : Date.dayNames,
10152     /**
10153      * @cfg {String} nextText
10154      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10155      */
10156     nextText: 'Next Month (Control+Right)',
10157     /**
10158      * @cfg {String} prevText
10159      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10160      */
10161     prevText: 'Previous Month (Control+Left)',
10162     /**
10163      * @cfg {String} monthYearText
10164      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10165      */
10166     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10167     /**
10168      * @cfg {Number} startDay
10169      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10170      */
10171     startDay : 0,
10172     /**
10173      * @cfg {Bool} showClear
10174      * Show a clear button (usefull for date form elements that can be blank.)
10175      */
10176     
10177     showClear: false,
10178     
10179     /**
10180      * Sets the value of the date field
10181      * @param {Date} value The date to set
10182      */
10183     setValue : function(value){
10184         var old = this.value;
10185         this.value = value.clearTime(true);
10186         if(this.el){
10187             this.update(this.value);
10188         }
10189     },
10190
10191     /**
10192      * Gets the current selected value of the date field
10193      * @return {Date} The selected date
10194      */
10195     getValue : function(){
10196         return this.value;
10197     },
10198
10199     // private
10200     focus : function(){
10201         if(this.el){
10202             this.update(this.activeDate);
10203         }
10204     },
10205
10206     // private
10207     onRender : function(container, position){
10208         var m = [
10209              '<table cellspacing="0">',
10210                 '<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>',
10211                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10212         var dn = this.dayNames;
10213         for(var i = 0; i < 7; i++){
10214             var d = this.startDay+i;
10215             if(d > 6){
10216                 d = d-7;
10217             }
10218             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10219         }
10220         m[m.length] = "</tr></thead><tbody><tr>";
10221         for(var i = 0; i < 42; i++) {
10222             if(i % 7 == 0 && i != 0){
10223                 m[m.length] = "</tr><tr>";
10224             }
10225             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10226         }
10227         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10228             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10229
10230         var el = document.createElement("div");
10231         el.className = "x-date-picker";
10232         el.innerHTML = m.join("");
10233
10234         container.dom.insertBefore(el, position);
10235
10236         this.el = Roo.get(el);
10237         this.eventEl = Roo.get(el.firstChild);
10238
10239         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10240             handler: this.showPrevMonth,
10241             scope: this,
10242             preventDefault:true,
10243             stopDefault:true
10244         });
10245
10246         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10247             handler: this.showNextMonth,
10248             scope: this,
10249             preventDefault:true,
10250             stopDefault:true
10251         });
10252
10253         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10254
10255         this.monthPicker = this.el.down('div.x-date-mp');
10256         this.monthPicker.enableDisplayMode('block');
10257         
10258         var kn = new Roo.KeyNav(this.eventEl, {
10259             "left" : function(e){
10260                 e.ctrlKey ?
10261                     this.showPrevMonth() :
10262                     this.update(this.activeDate.add("d", -1));
10263             },
10264
10265             "right" : function(e){
10266                 e.ctrlKey ?
10267                     this.showNextMonth() :
10268                     this.update(this.activeDate.add("d", 1));
10269             },
10270
10271             "up" : function(e){
10272                 e.ctrlKey ?
10273                     this.showNextYear() :
10274                     this.update(this.activeDate.add("d", -7));
10275             },
10276
10277             "down" : function(e){
10278                 e.ctrlKey ?
10279                     this.showPrevYear() :
10280                     this.update(this.activeDate.add("d", 7));
10281             },
10282
10283             "pageUp" : function(e){
10284                 this.showNextMonth();
10285             },
10286
10287             "pageDown" : function(e){
10288                 this.showPrevMonth();
10289             },
10290
10291             "enter" : function(e){
10292                 e.stopPropagation();
10293                 return true;
10294             },
10295
10296             scope : this
10297         });
10298
10299         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10300
10301         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10302
10303         this.el.unselectable();
10304         
10305         this.cells = this.el.select("table.x-date-inner tbody td");
10306         this.textNodes = this.el.query("table.x-date-inner tbody span");
10307
10308         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10309             text: "&#160;",
10310             tooltip: this.monthYearText
10311         });
10312
10313         this.mbtn.on('click', this.showMonthPicker, this);
10314         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10315
10316
10317         var today = (new Date()).dateFormat(this.format);
10318         
10319         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10320         if (this.showClear) {
10321             baseTb.add( new Roo.Toolbar.Fill());
10322         }
10323         baseTb.add({
10324             text: String.format(this.todayText, today),
10325             tooltip: String.format(this.todayTip, today),
10326             handler: this.selectToday,
10327             scope: this
10328         });
10329         
10330         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10331             
10332         //});
10333         if (this.showClear) {
10334             
10335             baseTb.add( new Roo.Toolbar.Fill());
10336             baseTb.add({
10337                 text: '&#160;',
10338                 cls: 'x-btn-icon x-btn-clear',
10339                 handler: function() {
10340                     //this.value = '';
10341                     this.fireEvent("select", this, '');
10342                 },
10343                 scope: this
10344             });
10345         }
10346         
10347         
10348         if(Roo.isIE){
10349             this.el.repaint();
10350         }
10351         this.update(this.value);
10352     },
10353
10354     createMonthPicker : function(){
10355         if(!this.monthPicker.dom.firstChild){
10356             var buf = ['<table border="0" cellspacing="0">'];
10357             for(var i = 0; i < 6; i++){
10358                 buf.push(
10359                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10360                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10361                     i == 0 ?
10362                     '<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>' :
10363                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10364                 );
10365             }
10366             buf.push(
10367                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10368                     this.okText,
10369                     '</button><button type="button" class="x-date-mp-cancel">',
10370                     this.cancelText,
10371                     '</button></td></tr>',
10372                 '</table>'
10373             );
10374             this.monthPicker.update(buf.join(''));
10375             this.monthPicker.on('click', this.onMonthClick, this);
10376             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10377
10378             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10379             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10380
10381             this.mpMonths.each(function(m, a, i){
10382                 i += 1;
10383                 if((i%2) == 0){
10384                     m.dom.xmonth = 5 + Math.round(i * .5);
10385                 }else{
10386                     m.dom.xmonth = Math.round((i-1) * .5);
10387                 }
10388             });
10389         }
10390     },
10391
10392     showMonthPicker : function(){
10393         this.createMonthPicker();
10394         var size = this.el.getSize();
10395         this.monthPicker.setSize(size);
10396         this.monthPicker.child('table').setSize(size);
10397
10398         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10399         this.updateMPMonth(this.mpSelMonth);
10400         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10401         this.updateMPYear(this.mpSelYear);
10402
10403         this.monthPicker.slideIn('t', {duration:.2});
10404     },
10405
10406     updateMPYear : function(y){
10407         this.mpyear = y;
10408         var ys = this.mpYears.elements;
10409         for(var i = 1; i <= 10; i++){
10410             var td = ys[i-1], y2;
10411             if((i%2) == 0){
10412                 y2 = y + Math.round(i * .5);
10413                 td.firstChild.innerHTML = y2;
10414                 td.xyear = y2;
10415             }else{
10416                 y2 = y - (5-Math.round(i * .5));
10417                 td.firstChild.innerHTML = y2;
10418                 td.xyear = y2;
10419             }
10420             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10421         }
10422     },
10423
10424     updateMPMonth : function(sm){
10425         this.mpMonths.each(function(m, a, i){
10426             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10427         });
10428     },
10429
10430     selectMPMonth: function(m){
10431         
10432     },
10433
10434     onMonthClick : function(e, t){
10435         e.stopEvent();
10436         var el = new Roo.Element(t), pn;
10437         if(el.is('button.x-date-mp-cancel')){
10438             this.hideMonthPicker();
10439         }
10440         else if(el.is('button.x-date-mp-ok')){
10441             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10442             this.hideMonthPicker();
10443         }
10444         else if(pn = el.up('td.x-date-mp-month', 2)){
10445             this.mpMonths.removeClass('x-date-mp-sel');
10446             pn.addClass('x-date-mp-sel');
10447             this.mpSelMonth = pn.dom.xmonth;
10448         }
10449         else if(pn = el.up('td.x-date-mp-year', 2)){
10450             this.mpYears.removeClass('x-date-mp-sel');
10451             pn.addClass('x-date-mp-sel');
10452             this.mpSelYear = pn.dom.xyear;
10453         }
10454         else if(el.is('a.x-date-mp-prev')){
10455             this.updateMPYear(this.mpyear-10);
10456         }
10457         else if(el.is('a.x-date-mp-next')){
10458             this.updateMPYear(this.mpyear+10);
10459         }
10460     },
10461
10462     onMonthDblClick : function(e, t){
10463         e.stopEvent();
10464         var el = new Roo.Element(t), pn;
10465         if(pn = el.up('td.x-date-mp-month', 2)){
10466             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10467             this.hideMonthPicker();
10468         }
10469         else if(pn = el.up('td.x-date-mp-year', 2)){
10470             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10471             this.hideMonthPicker();
10472         }
10473     },
10474
10475     hideMonthPicker : function(disableAnim){
10476         if(this.monthPicker){
10477             if(disableAnim === true){
10478                 this.monthPicker.hide();
10479             }else{
10480                 this.monthPicker.slideOut('t', {duration:.2});
10481             }
10482         }
10483     },
10484
10485     // private
10486     showPrevMonth : function(e){
10487         this.update(this.activeDate.add("mo", -1));
10488     },
10489
10490     // private
10491     showNextMonth : function(e){
10492         this.update(this.activeDate.add("mo", 1));
10493     },
10494
10495     // private
10496     showPrevYear : function(){
10497         this.update(this.activeDate.add("y", -1));
10498     },
10499
10500     // private
10501     showNextYear : function(){
10502         this.update(this.activeDate.add("y", 1));
10503     },
10504
10505     // private
10506     handleMouseWheel : function(e){
10507         var delta = e.getWheelDelta();
10508         if(delta > 0){
10509             this.showPrevMonth();
10510             e.stopEvent();
10511         } else if(delta < 0){
10512             this.showNextMonth();
10513             e.stopEvent();
10514         }
10515     },
10516
10517     // private
10518     handleDateClick : function(e, t){
10519         e.stopEvent();
10520         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10521             this.setValue(new Date(t.dateValue));
10522             this.fireEvent("select", this, this.value);
10523         }
10524     },
10525
10526     // private
10527     selectToday : function(){
10528         this.setValue(new Date().clearTime());
10529         this.fireEvent("select", this, this.value);
10530     },
10531
10532     // private
10533     update : function(date){
10534         var vd = this.activeDate;
10535         this.activeDate = date;
10536         if(vd && this.el){
10537             var t = date.getTime();
10538             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10539                 this.cells.removeClass("x-date-selected");
10540                 this.cells.each(function(c){
10541                    if(c.dom.firstChild.dateValue == t){
10542                        c.addClass("x-date-selected");
10543                        setTimeout(function(){
10544                             try{c.dom.firstChild.focus();}catch(e){}
10545                        }, 50);
10546                        return false;
10547                    }
10548                 });
10549                 return;
10550             }
10551         }
10552         var days = date.getDaysInMonth();
10553         var firstOfMonth = date.getFirstDateOfMonth();
10554         var startingPos = firstOfMonth.getDay()-this.startDay;
10555
10556         if(startingPos <= this.startDay){
10557             startingPos += 7;
10558         }
10559
10560         var pm = date.add("mo", -1);
10561         var prevStart = pm.getDaysInMonth()-startingPos;
10562
10563         var cells = this.cells.elements;
10564         var textEls = this.textNodes;
10565         days += startingPos;
10566
10567         // convert everything to numbers so it's fast
10568         var day = 86400000;
10569         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10570         var today = new Date().clearTime().getTime();
10571         var sel = date.clearTime().getTime();
10572         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10573         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10574         var ddMatch = this.disabledDatesRE;
10575         var ddText = this.disabledDatesText;
10576         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10577         var ddaysText = this.disabledDaysText;
10578         var format = this.format;
10579
10580         var setCellClass = function(cal, cell){
10581             cell.title = "";
10582             var t = d.getTime();
10583             cell.firstChild.dateValue = t;
10584             if(t == today){
10585                 cell.className += " x-date-today";
10586                 cell.title = cal.todayText;
10587             }
10588             if(t == sel){
10589                 cell.className += " x-date-selected";
10590                 setTimeout(function(){
10591                     try{cell.firstChild.focus();}catch(e){}
10592                 }, 50);
10593             }
10594             // disabling
10595             if(t < min) {
10596                 cell.className = " x-date-disabled";
10597                 cell.title = cal.minText;
10598                 return;
10599             }
10600             if(t > max) {
10601                 cell.className = " x-date-disabled";
10602                 cell.title = cal.maxText;
10603                 return;
10604             }
10605             if(ddays){
10606                 if(ddays.indexOf(d.getDay()) != -1){
10607                     cell.title = ddaysText;
10608                     cell.className = " x-date-disabled";
10609                 }
10610             }
10611             if(ddMatch && format){
10612                 var fvalue = d.dateFormat(format);
10613                 if(ddMatch.test(fvalue)){
10614                     cell.title = ddText.replace("%0", fvalue);
10615                     cell.className = " x-date-disabled";
10616                 }
10617             }
10618         };
10619
10620         var i = 0;
10621         for(; i < startingPos; i++) {
10622             textEls[i].innerHTML = (++prevStart);
10623             d.setDate(d.getDate()+1);
10624             cells[i].className = "x-date-prevday";
10625             setCellClass(this, cells[i]);
10626         }
10627         for(; i < days; i++){
10628             intDay = i - startingPos + 1;
10629             textEls[i].innerHTML = (intDay);
10630             d.setDate(d.getDate()+1);
10631             cells[i].className = "x-date-active";
10632             setCellClass(this, cells[i]);
10633         }
10634         var extraDays = 0;
10635         for(; i < 42; i++) {
10636              textEls[i].innerHTML = (++extraDays);
10637              d.setDate(d.getDate()+1);
10638              cells[i].className = "x-date-nextday";
10639              setCellClass(this, cells[i]);
10640         }
10641
10642         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10643
10644         if(!this.internalRender){
10645             var main = this.el.dom.firstChild;
10646             var w = main.offsetWidth;
10647             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10648             Roo.fly(main).setWidth(w);
10649             this.internalRender = true;
10650             // opera does not respect the auto grow header center column
10651             // then, after it gets a width opera refuses to recalculate
10652             // without a second pass
10653             if(Roo.isOpera && !this.secondPass){
10654                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10655                 this.secondPass = true;
10656                 this.update.defer(10, this, [date]);
10657             }
10658         }
10659     }
10660 });/*
10661  * Based on:
10662  * Ext JS Library 1.1.1
10663  * Copyright(c) 2006-2007, Ext JS, LLC.
10664  *
10665  * Originally Released Under LGPL - original licence link has changed is not relivant.
10666  *
10667  * Fork - LGPL
10668  * <script type="text/javascript">
10669  */
10670 /**
10671  * @class Roo.TabPanel
10672  * @extends Roo.util.Observable
10673  * A lightweight tab container.
10674  * <br><br>
10675  * Usage:
10676  * <pre><code>
10677 // basic tabs 1, built from existing content
10678 var tabs = new Roo.TabPanel("tabs1");
10679 tabs.addTab("script", "View Script");
10680 tabs.addTab("markup", "View Markup");
10681 tabs.activate("script");
10682
10683 // more advanced tabs, built from javascript
10684 var jtabs = new Roo.TabPanel("jtabs");
10685 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10686
10687 // set up the UpdateManager
10688 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10689 var updater = tab2.getUpdateManager();
10690 updater.setDefaultUrl("ajax1.htm");
10691 tab2.on('activate', updater.refresh, updater, true);
10692
10693 // Use setUrl for Ajax loading
10694 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10695 tab3.setUrl("ajax2.htm", null, true);
10696
10697 // Disabled tab
10698 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10699 tab4.disable();
10700
10701 jtabs.activate("jtabs-1");
10702  * </code></pre>
10703  * @constructor
10704  * Create a new TabPanel.
10705  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10706  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10707  */
10708 Roo.TabPanel = function(container, config){
10709     /**
10710     * The container element for this TabPanel.
10711     * @type Roo.Element
10712     */
10713     this.el = Roo.get(container, true);
10714     if(config){
10715         if(typeof config == "boolean"){
10716             this.tabPosition = config ? "bottom" : "top";
10717         }else{
10718             Roo.apply(this, config);
10719         }
10720     }
10721     if(this.tabPosition == "bottom"){
10722         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10723         this.el.addClass("x-tabs-bottom");
10724     }
10725     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10726     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10727     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10728     if(Roo.isIE){
10729         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10730     }
10731     if(this.tabPosition != "bottom"){
10732     /** The body element that contains {@link Roo.TabPanelItem} bodies.
10733      * @type Roo.Element
10734      */
10735       this.bodyEl = Roo.get(this.createBody(this.el.dom));
10736       this.el.addClass("x-tabs-top");
10737     }
10738     this.items = [];
10739
10740     this.bodyEl.setStyle("position", "relative");
10741
10742     this.active = null;
10743     this.activateDelegate = this.activate.createDelegate(this);
10744
10745     this.addEvents({
10746         /**
10747          * @event tabchange
10748          * Fires when the active tab changes
10749          * @param {Roo.TabPanel} this
10750          * @param {Roo.TabPanelItem} activePanel The new active tab
10751          */
10752         "tabchange": true,
10753         /**
10754          * @event beforetabchange
10755          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10756          * @param {Roo.TabPanel} this
10757          * @param {Object} e Set cancel to true on this object to cancel the tab change
10758          * @param {Roo.TabPanelItem} tab The tab being changed to
10759          */
10760         "beforetabchange" : true
10761     });
10762
10763     Roo.EventManager.onWindowResize(this.onResize, this);
10764     this.cpad = this.el.getPadding("lr");
10765     this.hiddenCount = 0;
10766
10767     Roo.TabPanel.superclass.constructor.call(this);
10768 };
10769
10770 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10771         /*
10772          *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10773          */
10774     tabPosition : "top",
10775         /*
10776          *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10777          */
10778     currentTabWidth : 0,
10779         /*
10780          *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10781          */
10782     minTabWidth : 40,
10783         /*
10784          *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10785          */
10786     maxTabWidth : 250,
10787         /*
10788          *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10789          */
10790     preferredTabWidth : 175,
10791         /*
10792          *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10793          */
10794     resizeTabs : false,
10795         /*
10796          *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10797          */
10798     monitorResize : true,
10799
10800     /**
10801      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10802      * @param {String} id The id of the div to use <b>or create</b>
10803      * @param {String} text The text for the tab
10804      * @param {String} content (optional) Content to put in the TabPanelItem body
10805      * @param {Boolean} closable (optional) True to create a close icon on the tab
10806      * @return {Roo.TabPanelItem} The created TabPanelItem
10807      */
10808     addTab : function(id, text, content, closable){
10809         var item = new Roo.TabPanelItem(this, id, text, closable);
10810         this.addTabItem(item);
10811         if(content){
10812             item.setContent(content);
10813         }
10814         return item;
10815     },
10816
10817     /**
10818      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10819      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10820      * @return {Roo.TabPanelItem}
10821      */
10822     getTab : function(id){
10823         return this.items[id];
10824     },
10825
10826     /**
10827      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10828      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10829      */
10830     hideTab : function(id){
10831         var t = this.items[id];
10832         if(!t.isHidden()){
10833            t.setHidden(true);
10834            this.hiddenCount++;
10835            this.autoSizeTabs();
10836         }
10837     },
10838
10839     /**
10840      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10841      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10842      */
10843     unhideTab : function(id){
10844         var t = this.items[id];
10845         if(t.isHidden()){
10846            t.setHidden(false);
10847            this.hiddenCount--;
10848            this.autoSizeTabs();
10849         }
10850     },
10851
10852     /**
10853      * Adds an existing {@link Roo.TabPanelItem}.
10854      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10855      */
10856     addTabItem : function(item){
10857         this.items[item.id] = item;
10858         this.items.push(item);
10859         if(this.resizeTabs){
10860            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10861            this.autoSizeTabs();
10862         }else{
10863             item.autoSize();
10864         }
10865     },
10866
10867     /**
10868      * Removes a {@link Roo.TabPanelItem}.
10869      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10870      */
10871     removeTab : function(id){
10872         var items = this.items;
10873         var tab = items[id];
10874         if(!tab) { return; }
10875         var index = items.indexOf(tab);
10876         if(this.active == tab && items.length > 1){
10877             var newTab = this.getNextAvailable(index);
10878             if(newTab) {
10879                 newTab.activate();
10880             }
10881         }
10882         this.stripEl.dom.removeChild(tab.pnode.dom);
10883         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
10884             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
10885         }
10886         items.splice(index, 1);
10887         delete this.items[tab.id];
10888         tab.fireEvent("close", tab);
10889         tab.purgeListeners();
10890         this.autoSizeTabs();
10891     },
10892
10893     getNextAvailable : function(start){
10894         var items = this.items;
10895         var index = start;
10896         // look for a next tab that will slide over to
10897         // replace the one being removed
10898         while(index < items.length){
10899             var item = items[++index];
10900             if(item && !item.isHidden()){
10901                 return item;
10902             }
10903         }
10904         // if one isn't found select the previous tab (on the left)
10905         index = start;
10906         while(index >= 0){
10907             var item = items[--index];
10908             if(item && !item.isHidden()){
10909                 return item;
10910             }
10911         }
10912         return null;
10913     },
10914
10915     /**
10916      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
10917      * @param {String/Number} id The id or index of the TabPanelItem to disable.
10918      */
10919     disableTab : function(id){
10920         var tab = this.items[id];
10921         if(tab && this.active != tab){
10922             tab.disable();
10923         }
10924     },
10925
10926     /**
10927      * Enables a {@link Roo.TabPanelItem} that is disabled.
10928      * @param {String/Number} id The id or index of the TabPanelItem to enable.
10929      */
10930     enableTab : function(id){
10931         var tab = this.items[id];
10932         tab.enable();
10933     },
10934
10935     /**
10936      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
10937      * @param {String/Number} id The id or index of the TabPanelItem to activate.
10938      * @return {Roo.TabPanelItem} The TabPanelItem.
10939      */
10940     activate : function(id){
10941         var tab = this.items[id];
10942         if(!tab){
10943             return null;
10944         }
10945         if(tab == this.active || tab.disabled){
10946             return tab;
10947         }
10948         var e = {};
10949         this.fireEvent("beforetabchange", this, e, tab);
10950         if(e.cancel !== true && !tab.disabled){
10951             if(this.active){
10952                 this.active.hide();
10953             }
10954             this.active = this.items[id];
10955             this.active.show();
10956             this.fireEvent("tabchange", this, this.active);
10957         }
10958         return tab;
10959     },
10960
10961     /**
10962      * Gets the active {@link Roo.TabPanelItem}.
10963      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
10964      */
10965     getActiveTab : function(){
10966         return this.active;
10967     },
10968
10969     /**
10970      * Updates the tab body element to fit the height of the container element
10971      * for overflow scrolling
10972      * @param {Number} targetHeight (optional) Override the starting height from the elements height
10973      */
10974     syncHeight : function(targetHeight){
10975         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
10976         var bm = this.bodyEl.getMargins();
10977         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
10978         this.bodyEl.setHeight(newHeight);
10979         return newHeight;
10980     },
10981
10982     onResize : function(){
10983         if(this.monitorResize){
10984             this.autoSizeTabs();
10985         }
10986     },
10987
10988     /**
10989      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
10990      */
10991     beginUpdate : function(){
10992         this.updating = true;
10993     },
10994
10995     /**
10996      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
10997      */
10998     endUpdate : function(){
10999         this.updating = false;
11000         this.autoSizeTabs();
11001     },
11002
11003     /**
11004      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11005      */
11006     autoSizeTabs : function(){
11007         var count = this.items.length;
11008         var vcount = count - this.hiddenCount;
11009         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11010         var w = Math.max(this.el.getWidth() - this.cpad, 10);
11011         var availWidth = Math.floor(w / vcount);
11012         var b = this.stripBody;
11013         if(b.getWidth() > w){
11014             var tabs = this.items;
11015             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11016             if(availWidth < this.minTabWidth){
11017                 /*if(!this.sleft){    // incomplete scrolling code
11018                     this.createScrollButtons();
11019                 }
11020                 this.showScroll();
11021                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11022             }
11023         }else{
11024             if(this.currentTabWidth < this.preferredTabWidth){
11025                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11026             }
11027         }
11028     },
11029
11030     /**
11031      * Returns the number of tabs in this TabPanel.
11032      * @return {Number}
11033      */
11034      getCount : function(){
11035          return this.items.length;
11036      },
11037
11038     /**
11039      * Resizes all the tabs to the passed width
11040      * @param {Number} The new width
11041      */
11042     setTabWidth : function(width){
11043         this.currentTabWidth = width;
11044         for(var i = 0, len = this.items.length; i < len; i++) {
11045                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11046         }
11047     },
11048
11049     /**
11050      * Destroys this TabPanel
11051      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11052      */
11053     destroy : function(removeEl){
11054         Roo.EventManager.removeResizeListener(this.onResize, this);
11055         for(var i = 0, len = this.items.length; i < len; i++){
11056             this.items[i].purgeListeners();
11057         }
11058         if(removeEl === true){
11059             this.el.update("");
11060             this.el.remove();
11061         }
11062     }
11063 });
11064
11065 /**
11066  * @class Roo.TabPanelItem
11067  * @extends Roo.util.Observable
11068  * Represents an individual item (tab plus body) in a TabPanel.
11069  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11070  * @param {String} id The id of this TabPanelItem
11071  * @param {String} text The text for the tab of this TabPanelItem
11072  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11073  */
11074 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11075     /**
11076      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11077      * @type Roo.TabPanel
11078      */
11079     this.tabPanel = tabPanel;
11080     /**
11081      * The id for this TabPanelItem
11082      * @type String
11083      */
11084     this.id = id;
11085     /** @private */
11086     this.disabled = false;
11087     /** @private */
11088     this.text = text;
11089     /** @private */
11090     this.loaded = false;
11091     this.closable = closable;
11092
11093     /**
11094      * The body element for this TabPanelItem.
11095      * @type Roo.Element
11096      */
11097     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11098     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11099     this.bodyEl.setStyle("display", "block");
11100     this.bodyEl.setStyle("zoom", "1");
11101     this.hideAction();
11102
11103     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11104     /** @private */
11105     this.el = Roo.get(els.el, true);
11106     this.inner = Roo.get(els.inner, true);
11107     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11108     this.pnode = Roo.get(els.el.parentNode, true);
11109     this.el.on("mousedown", this.onTabMouseDown, this);
11110     this.el.on("click", this.onTabClick, this);
11111     /** @private */
11112     if(closable){
11113         var c = Roo.get(els.close, true);
11114         c.dom.title = this.closeText;
11115         c.addClassOnOver("close-over");
11116         c.on("click", this.closeClick, this);
11117      }
11118
11119     this.addEvents({
11120          /**
11121          * @event activate
11122          * Fires when this tab becomes the active tab.
11123          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11124          * @param {Roo.TabPanelItem} this
11125          */
11126         "activate": true,
11127         /**
11128          * @event beforeclose
11129          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11130          * @param {Roo.TabPanelItem} this
11131          * @param {Object} e Set cancel to true on this object to cancel the close.
11132          */
11133         "beforeclose": true,
11134         /**
11135          * @event close
11136          * Fires when this tab is closed.
11137          * @param {Roo.TabPanelItem} this
11138          */
11139          "close": true,
11140         /**
11141          * @event deactivate
11142          * Fires when this tab is no longer the active tab.
11143          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11144          * @param {Roo.TabPanelItem} this
11145          */
11146          "deactivate" : true
11147     });
11148     this.hidden = false;
11149
11150     Roo.TabPanelItem.superclass.constructor.call(this);
11151 };
11152
11153 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11154     purgeListeners : function(){
11155        Roo.util.Observable.prototype.purgeListeners.call(this);
11156        this.el.removeAllListeners();
11157     },
11158     /**
11159      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11160      */
11161     show : function(){
11162         this.pnode.addClass("on");
11163         this.showAction();
11164         if(Roo.isOpera){
11165             this.tabPanel.stripWrap.repaint();
11166         }
11167         this.fireEvent("activate", this.tabPanel, this);
11168     },
11169
11170     /**
11171      * Returns true if this tab is the active tab.
11172      * @return {Boolean}
11173      */
11174     isActive : function(){
11175         return this.tabPanel.getActiveTab() == this;
11176     },
11177
11178     /**
11179      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11180      */
11181     hide : function(){
11182         this.pnode.removeClass("on");
11183         this.hideAction();
11184         this.fireEvent("deactivate", this.tabPanel, this);
11185     },
11186
11187     hideAction : function(){
11188         this.bodyEl.hide();
11189         this.bodyEl.setStyle("position", "absolute");
11190         this.bodyEl.setLeft("-20000px");
11191         this.bodyEl.setTop("-20000px");
11192     },
11193
11194     showAction : function(){
11195         this.bodyEl.setStyle("position", "relative");
11196         this.bodyEl.setTop("");
11197         this.bodyEl.setLeft("");
11198         this.bodyEl.show();
11199     },
11200
11201     /**
11202      * Set the tooltip for the tab.
11203      * @param {String} tooltip The tab's tooltip
11204      */
11205     setTooltip : function(text){
11206         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11207             this.textEl.dom.qtip = text;
11208             this.textEl.dom.removeAttribute('title');
11209         }else{
11210             this.textEl.dom.title = text;
11211         }
11212     },
11213
11214     onTabClick : function(e){
11215         e.preventDefault();
11216         this.tabPanel.activate(this.id);
11217     },
11218
11219     onTabMouseDown : function(e){
11220         e.preventDefault();
11221         this.tabPanel.activate(this.id);
11222     },
11223
11224     getWidth : function(){
11225         return this.inner.getWidth();
11226     },
11227
11228     setWidth : function(width){
11229         var iwidth = width - this.pnode.getPadding("lr");
11230         this.inner.setWidth(iwidth);
11231         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11232         this.pnode.setWidth(width);
11233     },
11234
11235     /**
11236      * Show or hide the tab
11237      * @param {Boolean} hidden True to hide or false to show.
11238      */
11239     setHidden : function(hidden){
11240         this.hidden = hidden;
11241         this.pnode.setStyle("display", hidden ? "none" : "");
11242     },
11243
11244     /**
11245      * Returns true if this tab is "hidden"
11246      * @return {Boolean}
11247      */
11248     isHidden : function(){
11249         return this.hidden;
11250     },
11251
11252     /**
11253      * Returns the text for this tab
11254      * @return {String}
11255      */
11256     getText : function(){
11257         return this.text;
11258     },
11259
11260     autoSize : function(){
11261         //this.el.beginMeasure();
11262         this.textEl.setWidth(1);
11263         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11264         //this.el.endMeasure();
11265     },
11266
11267     /**
11268      * Sets the text for the tab (Note: this also sets the tooltip text)
11269      * @param {String} text The tab's text and tooltip
11270      */
11271     setText : function(text){
11272         this.text = text;
11273         this.textEl.update(text);
11274         this.setTooltip(text);
11275         if(!this.tabPanel.resizeTabs){
11276             this.autoSize();
11277         }
11278     },
11279     /**
11280      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11281      */
11282     activate : function(){
11283         this.tabPanel.activate(this.id);
11284     },
11285
11286     /**
11287      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11288      */
11289     disable : function(){
11290         if(this.tabPanel.active != this){
11291             this.disabled = true;
11292             this.pnode.addClass("disabled");
11293         }
11294     },
11295
11296     /**
11297      * Enables this TabPanelItem if it was previously disabled.
11298      */
11299     enable : function(){
11300         this.disabled = false;
11301         this.pnode.removeClass("disabled");
11302     },
11303
11304     /**
11305      * Sets the content for this TabPanelItem.
11306      * @param {String} content The content
11307      * @param {Boolean} loadScripts true to look for and load scripts
11308      */
11309     setContent : function(content, loadScripts){
11310         this.bodyEl.update(content, loadScripts);
11311     },
11312
11313     /**
11314      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11315      * @return {Roo.UpdateManager} The UpdateManager
11316      */
11317     getUpdateManager : function(){
11318         return this.bodyEl.getUpdateManager();
11319     },
11320
11321     /**
11322      * Set a URL to be used to load the content for this TabPanelItem.
11323      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11324      * @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)
11325      * @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)
11326      * @return {Roo.UpdateManager} The UpdateManager
11327      */
11328     setUrl : function(url, params, loadOnce){
11329         if(this.refreshDelegate){
11330             this.un('activate', this.refreshDelegate);
11331         }
11332         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11333         this.on("activate", this.refreshDelegate);
11334         return this.bodyEl.getUpdateManager();
11335     },
11336
11337     /** @private */
11338     _handleRefresh : function(url, params, loadOnce){
11339         if(!loadOnce || !this.loaded){
11340             var updater = this.bodyEl.getUpdateManager();
11341             updater.update(url, params, this._setLoaded.createDelegate(this));
11342         }
11343     },
11344
11345     /**
11346      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11347      *   Will fail silently if the setUrl method has not been called.
11348      *   This does not activate the panel, just updates its content.
11349      */
11350     refresh : function(){
11351         if(this.refreshDelegate){
11352            this.loaded = false;
11353            this.refreshDelegate();
11354         }
11355     },
11356
11357     /** @private */
11358     _setLoaded : function(){
11359         this.loaded = true;
11360     },
11361
11362     /** @private */
11363     closeClick : function(e){
11364         var o = {};
11365         e.stopEvent();
11366         this.fireEvent("beforeclose", this, o);
11367         if(o.cancel !== true){
11368             this.tabPanel.removeTab(this.id);
11369         }
11370     },
11371     /**
11372      * The text displayed in the tooltip for the close icon.
11373      * @type String
11374      */
11375     closeText : "Close this tab"
11376 });
11377
11378 /** @private */
11379 Roo.TabPanel.prototype.createStrip = function(container){
11380     var strip = document.createElement("div");
11381     strip.className = "x-tabs-wrap";
11382     container.appendChild(strip);
11383     return strip;
11384 };
11385 /** @private */
11386 Roo.TabPanel.prototype.createStripList = function(strip){
11387     // div wrapper for retard IE
11388     strip.innerHTML = '<div class="x-tabs-strip-wrap"><table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr></tr></tbody></table></div>';
11389     return strip.firstChild.firstChild.firstChild.firstChild;
11390 };
11391 /** @private */
11392 Roo.TabPanel.prototype.createBody = function(container){
11393     var body = document.createElement("div");
11394     Roo.id(body, "tab-body");
11395     Roo.fly(body).addClass("x-tabs-body");
11396     container.appendChild(body);
11397     return body;
11398 };
11399 /** @private */
11400 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11401     var body = Roo.getDom(id);
11402     if(!body){
11403         body = document.createElement("div");
11404         body.id = id;
11405     }
11406     Roo.fly(body).addClass("x-tabs-item-body");
11407     bodyEl.insertBefore(body, bodyEl.firstChild);
11408     return body;
11409 };
11410 /** @private */
11411 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11412     var td = document.createElement("td");
11413     stripEl.appendChild(td);
11414     if(closable){
11415         td.className = "x-tabs-closable";
11416         if(!this.closeTpl){
11417             this.closeTpl = new Roo.Template(
11418                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11419                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11420                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11421             );
11422         }
11423         var el = this.closeTpl.overwrite(td, {"text": text});
11424         var close = el.getElementsByTagName("div")[0];
11425         var inner = el.getElementsByTagName("em")[0];
11426         return {"el": el, "close": close, "inner": inner};
11427     } else {
11428         if(!this.tabTpl){
11429             this.tabTpl = new Roo.Template(
11430                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11431                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11432             );
11433         }
11434         var el = this.tabTpl.overwrite(td, {"text": text});
11435         var inner = el.getElementsByTagName("em")[0];
11436         return {"el": el, "inner": inner};
11437     }
11438 };/*
11439  * Based on:
11440  * Ext JS Library 1.1.1
11441  * Copyright(c) 2006-2007, Ext JS, LLC.
11442  *
11443  * Originally Released Under LGPL - original licence link has changed is not relivant.
11444  *
11445  * Fork - LGPL
11446  * <script type="text/javascript">
11447  */
11448
11449 /**
11450  * @class Roo.Button
11451  * @extends Roo.util.Observable
11452  * Simple Button class
11453  * @cfg {String} text The button text
11454  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11455  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11456  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11457  * @cfg {Object} scope The scope of the handler
11458  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11459  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11460  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11461  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11462  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11463  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11464    applies if enableToggle = true)
11465  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11466  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11467   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11468  * @constructor
11469  * Create a new button
11470  * @param {Object} config The config object
11471  */
11472 Roo.Button = function(renderTo, config)
11473 {
11474     if (!config) {
11475         config = renderTo;
11476         renderTo = config.renderTo || false;
11477     }
11478     
11479     Roo.apply(this, config);
11480     this.addEvents({
11481         /**
11482              * @event click
11483              * Fires when this button is clicked
11484              * @param {Button} this
11485              * @param {EventObject} e The click event
11486              */
11487             "click" : true,
11488         /**
11489              * @event toggle
11490              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11491              * @param {Button} this
11492              * @param {Boolean} pressed
11493              */
11494             "toggle" : true,
11495         /**
11496              * @event mouseover
11497              * Fires when the mouse hovers over the button
11498              * @param {Button} this
11499              * @param {Event} e The event object
11500              */
11501         'mouseover' : true,
11502         /**
11503              * @event mouseout
11504              * Fires when the mouse exits the button
11505              * @param {Button} this
11506              * @param {Event} e The event object
11507              */
11508         'mouseout': true,
11509          /**
11510              * @event render
11511              * Fires when the button is rendered
11512              * @param {Button} this
11513              */
11514         'render': true
11515     });
11516     if(this.menu){
11517         this.menu = Roo.menu.MenuMgr.get(this.menu);
11518     }
11519     // register listeners first!!  - so render can be captured..
11520     Roo.util.Observable.call(this);
11521     if(renderTo){
11522         this.render(renderTo);
11523     }
11524     
11525   
11526 };
11527
11528 Roo.extend(Roo.Button, Roo.util.Observable, {
11529     /**
11530      * 
11531      */
11532     
11533     /**
11534      * Read-only. True if this button is hidden
11535      * @type Boolean
11536      */
11537     hidden : false,
11538     /**
11539      * Read-only. True if this button is disabled
11540      * @type Boolean
11541      */
11542     disabled : false,
11543     /**
11544      * Read-only. True if this button is pressed (only if enableToggle = true)
11545      * @type Boolean
11546      */
11547     pressed : false,
11548
11549     /**
11550      * @cfg {Number} tabIndex 
11551      * The DOM tabIndex for this button (defaults to undefined)
11552      */
11553     tabIndex : undefined,
11554
11555     /**
11556      * @cfg {Boolean} enableToggle
11557      * True to enable pressed/not pressed toggling (defaults to false)
11558      */
11559     enableToggle: false,
11560     /**
11561      * @cfg {Mixed} menu
11562      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11563      */
11564     menu : undefined,
11565     /**
11566      * @cfg {String} menuAlign
11567      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11568      */
11569     menuAlign : "tl-bl?",
11570
11571     /**
11572      * @cfg {String} iconCls
11573      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11574      */
11575     iconCls : undefined,
11576     /**
11577      * @cfg {String} type
11578      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11579      */
11580     type : 'button',
11581
11582     // private
11583     menuClassTarget: 'tr',
11584
11585     /**
11586      * @cfg {String} clickEvent
11587      * The type of event to map to the button's event handler (defaults to 'click')
11588      */
11589     clickEvent : 'click',
11590
11591     /**
11592      * @cfg {Boolean} handleMouseEvents
11593      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11594      */
11595     handleMouseEvents : true,
11596
11597     /**
11598      * @cfg {String} tooltipType
11599      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11600      */
11601     tooltipType : 'qtip',
11602
11603     /**
11604      * @cfg {String} cls
11605      * A CSS class to apply to the button's main element.
11606      */
11607     
11608     /**
11609      * @cfg {Roo.Template} template (Optional)
11610      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11611      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11612      * require code modifications if required elements (e.g. a button) aren't present.
11613      */
11614
11615     // private
11616     render : function(renderTo){
11617         var btn;
11618         if(this.hideParent){
11619             this.parentEl = Roo.get(renderTo);
11620         }
11621         if(!this.dhconfig){
11622             if(!this.template){
11623                 if(!Roo.Button.buttonTemplate){
11624                     // hideous table template
11625                     Roo.Button.buttonTemplate = new Roo.Template(
11626                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11627                         '<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>',
11628                         "</tr></tbody></table>");
11629                 }
11630                 this.template = Roo.Button.buttonTemplate;
11631             }
11632             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11633             var btnEl = btn.child("button:first");
11634             btnEl.on('focus', this.onFocus, this);
11635             btnEl.on('blur', this.onBlur, this);
11636             if(this.cls){
11637                 btn.addClass(this.cls);
11638             }
11639             if(this.icon){
11640                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11641             }
11642             if(this.iconCls){
11643                 btnEl.addClass(this.iconCls);
11644                 if(!this.cls){
11645                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11646                 }
11647             }
11648             if(this.tabIndex !== undefined){
11649                 btnEl.dom.tabIndex = this.tabIndex;
11650             }
11651             if(this.tooltip){
11652                 if(typeof this.tooltip == 'object'){
11653                     Roo.QuickTips.tips(Roo.apply({
11654                           target: btnEl.id
11655                     }, this.tooltip));
11656                 } else {
11657                     btnEl.dom[this.tooltipType] = this.tooltip;
11658                 }
11659             }
11660         }else{
11661             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11662         }
11663         this.el = btn;
11664         if(this.id){
11665             this.el.dom.id = this.el.id = this.id;
11666         }
11667         if(this.menu){
11668             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11669             this.menu.on("show", this.onMenuShow, this);
11670             this.menu.on("hide", this.onMenuHide, this);
11671         }
11672         btn.addClass("x-btn");
11673         if(Roo.isIE && !Roo.isIE7){
11674             this.autoWidth.defer(1, this);
11675         }else{
11676             this.autoWidth();
11677         }
11678         if(this.handleMouseEvents){
11679             btn.on("mouseover", this.onMouseOver, this);
11680             btn.on("mouseout", this.onMouseOut, this);
11681             btn.on("mousedown", this.onMouseDown, this);
11682         }
11683         btn.on(this.clickEvent, this.onClick, this);
11684         //btn.on("mouseup", this.onMouseUp, this);
11685         if(this.hidden){
11686             this.hide();
11687         }
11688         if(this.disabled){
11689             this.disable();
11690         }
11691         Roo.ButtonToggleMgr.register(this);
11692         if(this.pressed){
11693             this.el.addClass("x-btn-pressed");
11694         }
11695         if(this.repeat){
11696             var repeater = new Roo.util.ClickRepeater(btn,
11697                 typeof this.repeat == "object" ? this.repeat : {}
11698             );
11699             repeater.on("click", this.onClick,  this);
11700         }
11701         
11702         this.fireEvent('render', this);
11703         
11704     },
11705     /**
11706      * Returns the button's underlying element
11707      * @return {Roo.Element} The element
11708      */
11709     getEl : function(){
11710         return this.el;  
11711     },
11712     
11713     /**
11714      * Destroys this Button and removes any listeners.
11715      */
11716     destroy : function(){
11717         Roo.ButtonToggleMgr.unregister(this);
11718         this.el.removeAllListeners();
11719         this.purgeListeners();
11720         this.el.remove();
11721     },
11722
11723     // private
11724     autoWidth : function(){
11725         if(this.el){
11726             this.el.setWidth("auto");
11727             if(Roo.isIE7 && Roo.isStrict){
11728                 var ib = this.el.child('button');
11729                 if(ib && ib.getWidth() > 20){
11730                     ib.clip();
11731                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11732                 }
11733             }
11734             if(this.minWidth){
11735                 if(this.hidden){
11736                     this.el.beginMeasure();
11737                 }
11738                 if(this.el.getWidth() < this.minWidth){
11739                     this.el.setWidth(this.minWidth);
11740                 }
11741                 if(this.hidden){
11742                     this.el.endMeasure();
11743                 }
11744             }
11745         }
11746     },
11747
11748     /**
11749      * Assigns this button's click handler
11750      * @param {Function} handler The function to call when the button is clicked
11751      * @param {Object} scope (optional) Scope for the function passed in
11752      */
11753     setHandler : function(handler, scope){
11754         this.handler = handler;
11755         this.scope = scope;  
11756     },
11757     
11758     /**
11759      * Sets this button's text
11760      * @param {String} text The button text
11761      */
11762     setText : function(text){
11763         this.text = text;
11764         if(this.el){
11765             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11766         }
11767         this.autoWidth();
11768     },
11769     
11770     /**
11771      * Gets the text for this button
11772      * @return {String} The button text
11773      */
11774     getText : function(){
11775         return this.text;  
11776     },
11777     
11778     /**
11779      * Show this button
11780      */
11781     show: function(){
11782         this.hidden = false;
11783         if(this.el){
11784             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11785         }
11786     },
11787     
11788     /**
11789      * Hide this button
11790      */
11791     hide: function(){
11792         this.hidden = true;
11793         if(this.el){
11794             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11795         }
11796     },
11797     
11798     /**
11799      * Convenience function for boolean show/hide
11800      * @param {Boolean} visible True to show, false to hide
11801      */
11802     setVisible: function(visible){
11803         if(visible) {
11804             this.show();
11805         }else{
11806             this.hide();
11807         }
11808     },
11809     
11810     /**
11811      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11812      * @param {Boolean} state (optional) Force a particular state
11813      */
11814     toggle : function(state){
11815         state = state === undefined ? !this.pressed : state;
11816         if(state != this.pressed){
11817             if(state){
11818                 this.el.addClass("x-btn-pressed");
11819                 this.pressed = true;
11820                 this.fireEvent("toggle", this, true);
11821             }else{
11822                 this.el.removeClass("x-btn-pressed");
11823                 this.pressed = false;
11824                 this.fireEvent("toggle", this, false);
11825             }
11826             if(this.toggleHandler){
11827                 this.toggleHandler.call(this.scope || this, this, state);
11828             }
11829         }
11830     },
11831     
11832     /**
11833      * Focus the button
11834      */
11835     focus : function(){
11836         this.el.child('button:first').focus();
11837     },
11838     
11839     /**
11840      * Disable this button
11841      */
11842     disable : function(){
11843         if(this.el){
11844             this.el.addClass("x-btn-disabled");
11845         }
11846         this.disabled = true;
11847     },
11848     
11849     /**
11850      * Enable this button
11851      */
11852     enable : function(){
11853         if(this.el){
11854             this.el.removeClass("x-btn-disabled");
11855         }
11856         this.disabled = false;
11857     },
11858
11859     /**
11860      * Convenience function for boolean enable/disable
11861      * @param {Boolean} enabled True to enable, false to disable
11862      */
11863     setDisabled : function(v){
11864         this[v !== true ? "enable" : "disable"]();
11865     },
11866
11867     // private
11868     onClick : function(e){
11869         if(e){
11870             e.preventDefault();
11871         }
11872         if(e.button != 0){
11873             return;
11874         }
11875         if(!this.disabled){
11876             if(this.enableToggle){
11877                 this.toggle();
11878             }
11879             if(this.menu && !this.menu.isVisible()){
11880                 this.menu.show(this.el, this.menuAlign);
11881             }
11882             this.fireEvent("click", this, e);
11883             if(this.handler){
11884                 this.el.removeClass("x-btn-over");
11885                 this.handler.call(this.scope || this, this, e);
11886             }
11887         }
11888     },
11889     // private
11890     onMouseOver : function(e){
11891         if(!this.disabled){
11892             this.el.addClass("x-btn-over");
11893             this.fireEvent('mouseover', this, e);
11894         }
11895     },
11896     // private
11897     onMouseOut : function(e){
11898         if(!e.within(this.el,  true)){
11899             this.el.removeClass("x-btn-over");
11900             this.fireEvent('mouseout', this, e);
11901         }
11902     },
11903     // private
11904     onFocus : function(e){
11905         if(!this.disabled){
11906             this.el.addClass("x-btn-focus");
11907         }
11908     },
11909     // private
11910     onBlur : function(e){
11911         this.el.removeClass("x-btn-focus");
11912     },
11913     // private
11914     onMouseDown : function(e){
11915         if(!this.disabled && e.button == 0){
11916             this.el.addClass("x-btn-click");
11917             Roo.get(document).on('mouseup', this.onMouseUp, this);
11918         }
11919     },
11920     // private
11921     onMouseUp : function(e){
11922         if(e.button == 0){
11923             this.el.removeClass("x-btn-click");
11924             Roo.get(document).un('mouseup', this.onMouseUp, this);
11925         }
11926     },
11927     // private
11928     onMenuShow : function(e){
11929         this.el.addClass("x-btn-menu-active");
11930     },
11931     // private
11932     onMenuHide : function(e){
11933         this.el.removeClass("x-btn-menu-active");
11934     }   
11935 });
11936
11937 // Private utility class used by Button
11938 Roo.ButtonToggleMgr = function(){
11939    var groups = {};
11940    
11941    function toggleGroup(btn, state){
11942        if(state){
11943            var g = groups[btn.toggleGroup];
11944            for(var i = 0, l = g.length; i < l; i++){
11945                if(g[i] != btn){
11946                    g[i].toggle(false);
11947                }
11948            }
11949        }
11950    }
11951    
11952    return {
11953        register : function(btn){
11954            if(!btn.toggleGroup){
11955                return;
11956            }
11957            var g = groups[btn.toggleGroup];
11958            if(!g){
11959                g = groups[btn.toggleGroup] = [];
11960            }
11961            g.push(btn);
11962            btn.on("toggle", toggleGroup);
11963        },
11964        
11965        unregister : function(btn){
11966            if(!btn.toggleGroup){
11967                return;
11968            }
11969            var g = groups[btn.toggleGroup];
11970            if(g){
11971                g.remove(btn);
11972                btn.un("toggle", toggleGroup);
11973            }
11974        }
11975    };
11976 }();/*
11977  * Based on:
11978  * Ext JS Library 1.1.1
11979  * Copyright(c) 2006-2007, Ext JS, LLC.
11980  *
11981  * Originally Released Under LGPL - original licence link has changed is not relivant.
11982  *
11983  * Fork - LGPL
11984  * <script type="text/javascript">
11985  */
11986  
11987 /**
11988  * @class Roo.SplitButton
11989  * @extends Roo.Button
11990  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
11991  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
11992  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
11993  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
11994  * @cfg {String} arrowTooltip The title attribute of the arrow
11995  * @constructor
11996  * Create a new menu button
11997  * @param {String/HTMLElement/Element} renderTo The element to append the button to
11998  * @param {Object} config The config object
11999  */
12000 Roo.SplitButton = function(renderTo, config){
12001     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12002     /**
12003      * @event arrowclick
12004      * Fires when this button's arrow is clicked
12005      * @param {SplitButton} this
12006      * @param {EventObject} e The click event
12007      */
12008     this.addEvents({"arrowclick":true});
12009 };
12010
12011 Roo.extend(Roo.SplitButton, Roo.Button, {
12012     render : function(renderTo){
12013         // this is one sweet looking template!
12014         var tpl = new Roo.Template(
12015             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12016             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12017             '<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>',
12018             "</tbody></table></td><td>",
12019             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12020             '<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>',
12021             "</tbody></table></td></tr></table>"
12022         );
12023         var btn = tpl.append(renderTo, [this.text, this.type], true);
12024         var btnEl = btn.child("button");
12025         if(this.cls){
12026             btn.addClass(this.cls);
12027         }
12028         if(this.icon){
12029             btnEl.setStyle('background-image', 'url(' +this.icon +')');
12030         }
12031         if(this.iconCls){
12032             btnEl.addClass(this.iconCls);
12033             if(!this.cls){
12034                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12035             }
12036         }
12037         this.el = btn;
12038         if(this.handleMouseEvents){
12039             btn.on("mouseover", this.onMouseOver, this);
12040             btn.on("mouseout", this.onMouseOut, this);
12041             btn.on("mousedown", this.onMouseDown, this);
12042             btn.on("mouseup", this.onMouseUp, this);
12043         }
12044         btn.on(this.clickEvent, this.onClick, this);
12045         if(this.tooltip){
12046             if(typeof this.tooltip == 'object'){
12047                 Roo.QuickTips.tips(Roo.apply({
12048                       target: btnEl.id
12049                 }, this.tooltip));
12050             } else {
12051                 btnEl.dom[this.tooltipType] = this.tooltip;
12052             }
12053         }
12054         if(this.arrowTooltip){
12055             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12056         }
12057         if(this.hidden){
12058             this.hide();
12059         }
12060         if(this.disabled){
12061             this.disable();
12062         }
12063         if(this.pressed){
12064             this.el.addClass("x-btn-pressed");
12065         }
12066         if(Roo.isIE && !Roo.isIE7){
12067             this.autoWidth.defer(1, this);
12068         }else{
12069             this.autoWidth();
12070         }
12071         if(this.menu){
12072             this.menu.on("show", this.onMenuShow, this);
12073             this.menu.on("hide", this.onMenuHide, this);
12074         }
12075         this.fireEvent('render', this);
12076     },
12077
12078     // private
12079     autoWidth : function(){
12080         if(this.el){
12081             var tbl = this.el.child("table:first");
12082             var tbl2 = this.el.child("table:last");
12083             this.el.setWidth("auto");
12084             tbl.setWidth("auto");
12085             if(Roo.isIE7 && Roo.isStrict){
12086                 var ib = this.el.child('button:first');
12087                 if(ib && ib.getWidth() > 20){
12088                     ib.clip();
12089                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12090                 }
12091             }
12092             if(this.minWidth){
12093                 if(this.hidden){
12094                     this.el.beginMeasure();
12095                 }
12096                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12097                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12098                 }
12099                 if(this.hidden){
12100                     this.el.endMeasure();
12101                 }
12102             }
12103             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12104         } 
12105     },
12106     /**
12107      * Sets this button's click handler
12108      * @param {Function} handler The function to call when the button is clicked
12109      * @param {Object} scope (optional) Scope for the function passed above
12110      */
12111     setHandler : function(handler, scope){
12112         this.handler = handler;
12113         this.scope = scope;  
12114     },
12115     
12116     /**
12117      * Sets this button's arrow click handler
12118      * @param {Function} handler The function to call when the arrow is clicked
12119      * @param {Object} scope (optional) Scope for the function passed above
12120      */
12121     setArrowHandler : function(handler, scope){
12122         this.arrowHandler = handler;
12123         this.scope = scope;  
12124     },
12125     
12126     /**
12127      * Focus the button
12128      */
12129     focus : function(){
12130         if(this.el){
12131             this.el.child("button:first").focus();
12132         }
12133     },
12134
12135     // private
12136     onClick : function(e){
12137         e.preventDefault();
12138         if(!this.disabled){
12139             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12140                 if(this.menu && !this.menu.isVisible()){
12141                     this.menu.show(this.el, this.menuAlign);
12142                 }
12143                 this.fireEvent("arrowclick", this, e);
12144                 if(this.arrowHandler){
12145                     this.arrowHandler.call(this.scope || this, this, e);
12146                 }
12147             }else{
12148                 this.fireEvent("click", this, e);
12149                 if(this.handler){
12150                     this.handler.call(this.scope || this, this, e);
12151                 }
12152             }
12153         }
12154     },
12155     // private
12156     onMouseDown : function(e){
12157         if(!this.disabled){
12158             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12159         }
12160     },
12161     // private
12162     onMouseUp : function(e){
12163         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12164     }   
12165 });
12166
12167
12168 // backwards compat
12169 Roo.MenuButton = Roo.SplitButton;/*
12170  * Based on:
12171  * Ext JS Library 1.1.1
12172  * Copyright(c) 2006-2007, Ext JS, LLC.
12173  *
12174  * Originally Released Under LGPL - original licence link has changed is not relivant.
12175  *
12176  * Fork - LGPL
12177  * <script type="text/javascript">
12178  */
12179
12180 /**
12181  * @class Roo.Toolbar
12182  * Basic Toolbar class.
12183  * @constructor
12184  * Creates a new Toolbar
12185  * @param {Object} config The config object
12186  */ 
12187 Roo.Toolbar = function(container, buttons, config)
12188 {
12189     /// old consturctor format still supported..
12190     if(container instanceof Array){ // omit the container for later rendering
12191         buttons = container;
12192         config = buttons;
12193         container = null;
12194     }
12195     if (typeof(container) == 'object' && container.xtype) {
12196         config = container;
12197         container = config.container;
12198         buttons = config.buttons; // not really - use items!!
12199     }
12200     var xitems = [];
12201     if (config && config.items) {
12202         xitems = config.items;
12203         delete config.items;
12204     }
12205     Roo.apply(this, config);
12206     this.buttons = buttons;
12207     
12208     if(container){
12209         this.render(container);
12210     }
12211     Roo.each(xitems, function(b) {
12212         this.add(b);
12213     }, this);
12214     
12215 };
12216
12217 Roo.Toolbar.prototype = {
12218     /**
12219      * @cfg {Roo.data.Store} items
12220      * array of button configs or elements to add
12221      */
12222     
12223     /**
12224      * @cfg {String/HTMLElement/Element} container
12225      * The id or element that will contain the toolbar
12226      */
12227     // private
12228     render : function(ct){
12229         this.el = Roo.get(ct);
12230         if(this.cls){
12231             this.el.addClass(this.cls);
12232         }
12233         // using a table allows for vertical alignment
12234         // 100% width is needed by Safari...
12235         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12236         this.tr = this.el.child("tr", true);
12237         var autoId = 0;
12238         this.items = new Roo.util.MixedCollection(false, function(o){
12239             return o.id || ("item" + (++autoId));
12240         });
12241         if(this.buttons){
12242             this.add.apply(this, this.buttons);
12243             delete this.buttons;
12244         }
12245     },
12246
12247     /**
12248      * Adds element(s) to the toolbar -- this function takes a variable number of 
12249      * arguments of mixed type and adds them to the toolbar.
12250      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12251      * <ul>
12252      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12253      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12254      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12255      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12256      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12257      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12258      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12259      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12260      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12261      * </ul>
12262      * @param {Mixed} arg2
12263      * @param {Mixed} etc.
12264      */
12265     add : function(){
12266         var a = arguments, l = a.length;
12267         for(var i = 0; i < l; i++){
12268             this._add(a[i]);
12269         }
12270     },
12271     // private..
12272     _add : function(el) {
12273         
12274         if (el.xtype) {
12275             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12276         }
12277         
12278         if (el.applyTo){ // some kind of form field
12279             return this.addField(el);
12280         } 
12281         if (el.render){ // some kind of Toolbar.Item
12282             return this.addItem(el);
12283         }
12284         if (typeof el == "string"){ // string
12285             if(el == "separator" || el == "-"){
12286                 return this.addSeparator();
12287             }
12288             if (el == " "){
12289                 return this.addSpacer();
12290             }
12291             if(el == "->"){
12292                 return this.addFill();
12293             }
12294             return this.addText(el);
12295             
12296         }
12297         if(el.tagName){ // element
12298             return this.addElement(el);
12299         }
12300         if(typeof el == "object"){ // must be button config?
12301             return this.addButton(el);
12302         }
12303         // and now what?!?!
12304         return false;
12305         
12306     },
12307     
12308     /**
12309      * Add an Xtype element
12310      * @param {Object} xtype Xtype Object
12311      * @return {Object} created Object
12312      */
12313     addxtype : function(e){
12314         return this.add(e);  
12315     },
12316     
12317     /**
12318      * Returns the Element for this toolbar.
12319      * @return {Roo.Element}
12320      */
12321     getEl : function(){
12322         return this.el;  
12323     },
12324     
12325     /**
12326      * Adds a separator
12327      * @return {Roo.Toolbar.Item} The separator item
12328      */
12329     addSeparator : function(){
12330         return this.addItem(new Roo.Toolbar.Separator());
12331     },
12332
12333     /**
12334      * Adds a spacer element
12335      * @return {Roo.Toolbar.Spacer} The spacer item
12336      */
12337     addSpacer : function(){
12338         return this.addItem(new Roo.Toolbar.Spacer());
12339     },
12340
12341     /**
12342      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12343      * @return {Roo.Toolbar.Fill} The fill item
12344      */
12345     addFill : function(){
12346         return this.addItem(new Roo.Toolbar.Fill());
12347     },
12348
12349     /**
12350      * Adds any standard HTML element to the toolbar
12351      * @param {String/HTMLElement/Element} el The element or id of the element to add
12352      * @return {Roo.Toolbar.Item} The element's item
12353      */
12354     addElement : function(el){
12355         return this.addItem(new Roo.Toolbar.Item(el));
12356     },
12357     /**
12358      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12359      * @type Roo.util.MixedCollection  
12360      */
12361     items : false,
12362      
12363     /**
12364      * Adds any Toolbar.Item or subclass
12365      * @param {Roo.Toolbar.Item} item
12366      * @return {Roo.Toolbar.Item} The item
12367      */
12368     addItem : function(item){
12369         var td = this.nextBlock();
12370         item.render(td);
12371         this.items.add(item);
12372         return item;
12373     },
12374     
12375     /**
12376      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12377      * @param {Object/Array} config A button config or array of configs
12378      * @return {Roo.Toolbar.Button/Array}
12379      */
12380     addButton : function(config){
12381         if(config instanceof Array){
12382             var buttons = [];
12383             for(var i = 0, len = config.length; i < len; i++) {
12384                 buttons.push(this.addButton(config[i]));
12385             }
12386             return buttons;
12387         }
12388         var b = config;
12389         if(!(config instanceof Roo.Toolbar.Button)){
12390             b = config.split ?
12391                 new Roo.Toolbar.SplitButton(config) :
12392                 new Roo.Toolbar.Button(config);
12393         }
12394         var td = this.nextBlock();
12395         b.render(td);
12396         this.items.add(b);
12397         return b;
12398     },
12399     
12400     /**
12401      * Adds text to the toolbar
12402      * @param {String} text The text to add
12403      * @return {Roo.Toolbar.Item} The element's item
12404      */
12405     addText : function(text){
12406         return this.addItem(new Roo.Toolbar.TextItem(text));
12407     },
12408     
12409     /**
12410      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12411      * @param {Number} index The index where the item is to be inserted
12412      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12413      * @return {Roo.Toolbar.Button/Item}
12414      */
12415     insertButton : function(index, item){
12416         if(item instanceof Array){
12417             var buttons = [];
12418             for(var i = 0, len = item.length; i < len; i++) {
12419                buttons.push(this.insertButton(index + i, item[i]));
12420             }
12421             return buttons;
12422         }
12423         if (!(item instanceof Roo.Toolbar.Button)){
12424            item = new Roo.Toolbar.Button(item);
12425         }
12426         var td = document.createElement("td");
12427         this.tr.insertBefore(td, this.tr.childNodes[index]);
12428         item.render(td);
12429         this.items.insert(index, item);
12430         return item;
12431     },
12432     
12433     /**
12434      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12435      * @param {Object} config
12436      * @return {Roo.Toolbar.Item} The element's item
12437      */
12438     addDom : function(config, returnEl){
12439         var td = this.nextBlock();
12440         Roo.DomHelper.overwrite(td, config);
12441         var ti = new Roo.Toolbar.Item(td.firstChild);
12442         ti.render(td);
12443         this.items.add(ti);
12444         return ti;
12445     },
12446
12447     /**
12448      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12449      * @type Roo.util.MixedCollection  
12450      */
12451     fields : false,
12452     
12453     /**
12454      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
12455      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
12456      * @param {Roo.form.Field} field
12457      * @return {Roo.ToolbarItem}
12458      */
12459      
12460       
12461     addField : function(field) {
12462         if (!this.fields) {
12463             var autoId = 0;
12464             this.fields = new Roo.util.MixedCollection(false, function(o){
12465                 return o.id || ("item" + (++autoId));
12466             });
12467
12468         }
12469         
12470         var td = this.nextBlock();
12471         field.render(td);
12472         var ti = new Roo.Toolbar.Item(td.firstChild);
12473         ti.render(td);
12474         this.items.add(ti);
12475         this.fields.add(field);
12476         return ti;
12477     },
12478     /**
12479      * Hide the toolbar
12480      * @method hide
12481      */
12482      
12483       
12484     hide : function()
12485     {
12486         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12487         this.el.child('div').hide();
12488     },
12489     /**
12490      * Show the toolbar
12491      * @method show
12492      */
12493     show : function()
12494     {
12495         this.el.child('div').show();
12496     },
12497       
12498     // private
12499     nextBlock : function(){
12500         var td = document.createElement("td");
12501         this.tr.appendChild(td);
12502         return td;
12503     },
12504
12505     // private
12506     destroy : function(){
12507         if(this.items){ // rendered?
12508             Roo.destroy.apply(Roo, this.items.items);
12509         }
12510         if(this.fields){ // rendered?
12511             Roo.destroy.apply(Roo, this.fields.items);
12512         }
12513         Roo.Element.uncache(this.el, this.tr);
12514     }
12515 };
12516
12517 /**
12518  * @class Roo.Toolbar.Item
12519  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12520  * @constructor
12521  * Creates a new Item
12522  * @param {HTMLElement} el 
12523  */
12524 Roo.Toolbar.Item = function(el){
12525     this.el = Roo.getDom(el);
12526     this.id = Roo.id(this.el);
12527     this.hidden = false;
12528 };
12529
12530 Roo.Toolbar.Item.prototype = {
12531     
12532     /**
12533      * Get this item's HTML Element
12534      * @return {HTMLElement}
12535      */
12536     getEl : function(){
12537        return this.el;  
12538     },
12539
12540     // private
12541     render : function(td){
12542         this.td = td;
12543         td.appendChild(this.el);
12544     },
12545     
12546     /**
12547      * Removes and destroys this item.
12548      */
12549     destroy : function(){
12550         this.td.parentNode.removeChild(this.td);
12551     },
12552     
12553     /**
12554      * Shows this item.
12555      */
12556     show: function(){
12557         this.hidden = false;
12558         this.td.style.display = "";
12559     },
12560     
12561     /**
12562      * Hides this item.
12563      */
12564     hide: function(){
12565         this.hidden = true;
12566         this.td.style.display = "none";
12567     },
12568     
12569     /**
12570      * Convenience function for boolean show/hide.
12571      * @param {Boolean} visible true to show/false to hide
12572      */
12573     setVisible: function(visible){
12574         if(visible) {
12575             this.show();
12576         }else{
12577             this.hide();
12578         }
12579     },
12580     
12581     /**
12582      * Try to focus this item.
12583      */
12584     focus : function(){
12585         Roo.fly(this.el).focus();
12586     },
12587     
12588     /**
12589      * Disables this item.
12590      */
12591     disable : function(){
12592         Roo.fly(this.td).addClass("x-item-disabled");
12593         this.disabled = true;
12594         this.el.disabled = true;
12595     },
12596     
12597     /**
12598      * Enables this item.
12599      */
12600     enable : function(){
12601         Roo.fly(this.td).removeClass("x-item-disabled");
12602         this.disabled = false;
12603         this.el.disabled = false;
12604     }
12605 };
12606
12607
12608 /**
12609  * @class Roo.Toolbar.Separator
12610  * @extends Roo.Toolbar.Item
12611  * A simple toolbar separator class
12612  * @constructor
12613  * Creates a new Separator
12614  */
12615 Roo.Toolbar.Separator = function(){
12616     var s = document.createElement("span");
12617     s.className = "ytb-sep";
12618     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12619 };
12620 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12621     enable:Roo.emptyFn,
12622     disable:Roo.emptyFn,
12623     focus:Roo.emptyFn
12624 });
12625
12626 /**
12627  * @class Roo.Toolbar.Spacer
12628  * @extends Roo.Toolbar.Item
12629  * A simple element that adds extra horizontal space to a toolbar.
12630  * @constructor
12631  * Creates a new Spacer
12632  */
12633 Roo.Toolbar.Spacer = function(){
12634     var s = document.createElement("div");
12635     s.className = "ytb-spacer";
12636     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12637 };
12638 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12639     enable:Roo.emptyFn,
12640     disable:Roo.emptyFn,
12641     focus:Roo.emptyFn
12642 });
12643
12644 /**
12645  * @class Roo.Toolbar.Fill
12646  * @extends Roo.Toolbar.Spacer
12647  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12648  * @constructor
12649  * Creates a new Spacer
12650  */
12651 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12652     // private
12653     render : function(td){
12654         td.style.width = '100%';
12655         Roo.Toolbar.Fill.superclass.render.call(this, td);
12656     }
12657 });
12658
12659 /**
12660  * @class Roo.Toolbar.TextItem
12661  * @extends Roo.Toolbar.Item
12662  * A simple class that renders text directly into a toolbar.
12663  * @constructor
12664  * Creates a new TextItem
12665  * @param {String} text
12666  */
12667 Roo.Toolbar.TextItem = function(text){
12668     if (typeof(text) == 'object') {
12669         text = text.text;
12670     }
12671     var s = document.createElement("span");
12672     s.className = "ytb-text";
12673     s.innerHTML = text;
12674     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12675 };
12676 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12677     enable:Roo.emptyFn,
12678     disable:Roo.emptyFn,
12679     focus:Roo.emptyFn
12680 });
12681
12682 /**
12683  * @class Roo.Toolbar.Button
12684  * @extends Roo.Button
12685  * A button that renders into a toolbar.
12686  * @constructor
12687  * Creates a new Button
12688  * @param {Object} config A standard {@link Roo.Button} config object
12689  */
12690 Roo.Toolbar.Button = function(config){
12691     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12692 };
12693 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12694     render : function(td){
12695         this.td = td;
12696         Roo.Toolbar.Button.superclass.render.call(this, td);
12697     },
12698     
12699     /**
12700      * Removes and destroys this button
12701      */
12702     destroy : function(){
12703         Roo.Toolbar.Button.superclass.destroy.call(this);
12704         this.td.parentNode.removeChild(this.td);
12705     },
12706     
12707     /**
12708      * Shows this button
12709      */
12710     show: function(){
12711         this.hidden = false;
12712         this.td.style.display = "";
12713     },
12714     
12715     /**
12716      * Hides this button
12717      */
12718     hide: function(){
12719         this.hidden = true;
12720         this.td.style.display = "none";
12721     },
12722
12723     /**
12724      * Disables this item
12725      */
12726     disable : function(){
12727         Roo.fly(this.td).addClass("x-item-disabled");
12728         this.disabled = true;
12729     },
12730
12731     /**
12732      * Enables this item
12733      */
12734     enable : function(){
12735         Roo.fly(this.td).removeClass("x-item-disabled");
12736         this.disabled = false;
12737     }
12738 });
12739 // backwards compat
12740 Roo.ToolbarButton = Roo.Toolbar.Button;
12741
12742 /**
12743  * @class Roo.Toolbar.SplitButton
12744  * @extends Roo.SplitButton
12745  * A menu button that renders into a toolbar.
12746  * @constructor
12747  * Creates a new SplitButton
12748  * @param {Object} config A standard {@link Roo.SplitButton} config object
12749  */
12750 Roo.Toolbar.SplitButton = function(config){
12751     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12752 };
12753 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12754     render : function(td){
12755         this.td = td;
12756         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12757     },
12758     
12759     /**
12760      * Removes and destroys this button
12761      */
12762     destroy : function(){
12763         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12764         this.td.parentNode.removeChild(this.td);
12765     },
12766     
12767     /**
12768      * Shows this button
12769      */
12770     show: function(){
12771         this.hidden = false;
12772         this.td.style.display = "";
12773     },
12774     
12775     /**
12776      * Hides this button
12777      */
12778     hide: function(){
12779         this.hidden = true;
12780         this.td.style.display = "none";
12781     }
12782 });
12783
12784 // backwards compat
12785 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12786  * Based on:
12787  * Ext JS Library 1.1.1
12788  * Copyright(c) 2006-2007, Ext JS, LLC.
12789  *
12790  * Originally Released Under LGPL - original licence link has changed is not relivant.
12791  *
12792  * Fork - LGPL
12793  * <script type="text/javascript">
12794  */
12795  
12796 /**
12797  * @class Roo.PagingToolbar
12798  * @extends Roo.Toolbar
12799  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12800  * @constructor
12801  * Create a new PagingToolbar
12802  * @param {Object} config The config object
12803  */
12804 Roo.PagingToolbar = function(el, ds, config)
12805 {
12806     // old args format still supported... - xtype is prefered..
12807     if (typeof(el) == 'object' && el.xtype) {
12808         // created from xtype...
12809         config = el;
12810         ds = el.dataSource;
12811         el = config.container;
12812     }
12813     var items = [];
12814     if (config.items) {
12815         items = config.items;
12816         config.items = [];
12817     }
12818     
12819     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12820     this.ds = ds;
12821     this.cursor = 0;
12822     this.renderButtons(this.el);
12823     this.bind(ds);
12824     
12825     // supprot items array.
12826    
12827     Roo.each(items, function(e) {
12828         this.add(Roo.factory(e));
12829     },this);
12830     
12831 };
12832
12833 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12834     /**
12835      * @cfg {Roo.data.Store} dataSource
12836      * The underlying data store providing the paged data
12837      */
12838     /**
12839      * @cfg {String/HTMLElement/Element} container
12840      * container The id or element that will contain the toolbar
12841      */
12842     /**
12843      * @cfg {Boolean} displayInfo
12844      * True to display the displayMsg (defaults to false)
12845      */
12846     /**
12847      * @cfg {Number} pageSize
12848      * The number of records to display per page (defaults to 20)
12849      */
12850     pageSize: 20,
12851     /**
12852      * @cfg {String} displayMsg
12853      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12854      */
12855     displayMsg : 'Displaying {0} - {1} of {2}',
12856     /**
12857      * @cfg {String} emptyMsg
12858      * The message to display when no records are found (defaults to "No data to display")
12859      */
12860     emptyMsg : 'No data to display',
12861     /**
12862      * Customizable piece of the default paging text (defaults to "Page")
12863      * @type String
12864      */
12865     beforePageText : "Page",
12866     /**
12867      * Customizable piece of the default paging text (defaults to "of %0")
12868      * @type String
12869      */
12870     afterPageText : "of {0}",
12871     /**
12872      * Customizable piece of the default paging text (defaults to "First Page")
12873      * @type String
12874      */
12875     firstText : "First Page",
12876     /**
12877      * Customizable piece of the default paging text (defaults to "Previous Page")
12878      * @type String
12879      */
12880     prevText : "Previous Page",
12881     /**
12882      * Customizable piece of the default paging text (defaults to "Next Page")
12883      * @type String
12884      */
12885     nextText : "Next Page",
12886     /**
12887      * Customizable piece of the default paging text (defaults to "Last Page")
12888      * @type String
12889      */
12890     lastText : "Last Page",
12891     /**
12892      * Customizable piece of the default paging text (defaults to "Refresh")
12893      * @type String
12894      */
12895     refreshText : "Refresh",
12896
12897     // private
12898     renderButtons : function(el){
12899         Roo.PagingToolbar.superclass.render.call(this, el);
12900         this.first = this.addButton({
12901             tooltip: this.firstText,
12902             cls: "x-btn-icon x-grid-page-first",
12903             disabled: true,
12904             handler: this.onClick.createDelegate(this, ["first"])
12905         });
12906         this.prev = this.addButton({
12907             tooltip: this.prevText,
12908             cls: "x-btn-icon x-grid-page-prev",
12909             disabled: true,
12910             handler: this.onClick.createDelegate(this, ["prev"])
12911         });
12912         //this.addSeparator();
12913         this.add(this.beforePageText);
12914         this.field = Roo.get(this.addDom({
12915            tag: "input",
12916            type: "text",
12917            size: "3",
12918            value: "1",
12919            cls: "x-grid-page-number"
12920         }).el);
12921         this.field.on("keydown", this.onPagingKeydown, this);
12922         this.field.on("focus", function(){this.dom.select();});
12923         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12924         this.field.setHeight(18);
12925         //this.addSeparator();
12926         this.next = this.addButton({
12927             tooltip: this.nextText,
12928             cls: "x-btn-icon x-grid-page-next",
12929             disabled: true,
12930             handler: this.onClick.createDelegate(this, ["next"])
12931         });
12932         this.last = this.addButton({
12933             tooltip: this.lastText,
12934             cls: "x-btn-icon x-grid-page-last",
12935             disabled: true,
12936             handler: this.onClick.createDelegate(this, ["last"])
12937         });
12938         //this.addSeparator();
12939         this.loading = this.addButton({
12940             tooltip: this.refreshText,
12941             cls: "x-btn-icon x-grid-loading",
12942             handler: this.onClick.createDelegate(this, ["refresh"])
12943         });
12944
12945         if(this.displayInfo){
12946             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
12947         }
12948     },
12949
12950     // private
12951     updateInfo : function(){
12952         if(this.displayEl){
12953             var count = this.ds.getCount();
12954             var msg = count == 0 ?
12955                 this.emptyMsg :
12956                 String.format(
12957                     this.displayMsg,
12958                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
12959                 );
12960             this.displayEl.update(msg);
12961         }
12962     },
12963
12964     // private
12965     onLoad : function(ds, r, o){
12966        this.cursor = o.params ? o.params.start : 0;
12967        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
12968
12969        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
12970        this.field.dom.value = ap;
12971        this.first.setDisabled(ap == 1);
12972        this.prev.setDisabled(ap == 1);
12973        this.next.setDisabled(ap == ps);
12974        this.last.setDisabled(ap == ps);
12975        this.loading.enable();
12976        this.updateInfo();
12977     },
12978
12979     // private
12980     getPageData : function(){
12981         var total = this.ds.getTotalCount();
12982         return {
12983             total : total,
12984             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
12985             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
12986         };
12987     },
12988
12989     // private
12990     onLoadError : function(){
12991         this.loading.enable();
12992     },
12993
12994     // private
12995     onPagingKeydown : function(e){
12996         var k = e.getKey();
12997         var d = this.getPageData();
12998         if(k == e.RETURN){
12999             var v = this.field.dom.value, pageNum;
13000             if(!v || isNaN(pageNum = parseInt(v, 10))){
13001                 this.field.dom.value = d.activePage;
13002                 return;
13003             }
13004             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13005             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13006             e.stopEvent();
13007         }
13008         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))
13009         {
13010           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13011           this.field.dom.value = pageNum;
13012           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13013           e.stopEvent();
13014         }
13015         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13016         {
13017           var v = this.field.dom.value, pageNum; 
13018           var increment = (e.shiftKey) ? 10 : 1;
13019           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13020             increment *= -1;
13021           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13022             this.field.dom.value = d.activePage;
13023             return;
13024           }
13025           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13026           {
13027             this.field.dom.value = parseInt(v, 10) + increment;
13028             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13029             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13030           }
13031           e.stopEvent();
13032         }
13033     },
13034
13035     // private
13036     beforeLoad : function(){
13037         if(this.loading){
13038             this.loading.disable();
13039         }
13040     },
13041
13042     // private
13043     onClick : function(which){
13044         var ds = this.ds;
13045         switch(which){
13046             case "first":
13047                 ds.load({params:{start: 0, limit: this.pageSize}});
13048             break;
13049             case "prev":
13050                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13051             break;
13052             case "next":
13053                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13054             break;
13055             case "last":
13056                 var total = ds.getTotalCount();
13057                 var extra = total % this.pageSize;
13058                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13059                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13060             break;
13061             case "refresh":
13062                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13063             break;
13064         }
13065     },
13066
13067     /**
13068      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13069      * @param {Roo.data.Store} store The data store to unbind
13070      */
13071     unbind : function(ds){
13072         ds.un("beforeload", this.beforeLoad, this);
13073         ds.un("load", this.onLoad, this);
13074         ds.un("loadexception", this.onLoadError, this);
13075         ds.un("remove", this.updateInfo, this);
13076         ds.un("add", this.updateInfo, this);
13077         this.ds = undefined;
13078     },
13079
13080     /**
13081      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13082      * @param {Roo.data.Store} store The data store to bind
13083      */
13084     bind : function(ds){
13085         ds.on("beforeload", this.beforeLoad, this);
13086         ds.on("load", this.onLoad, this);
13087         ds.on("loadexception", this.onLoadError, this);
13088         ds.on("remove", this.updateInfo, this);
13089         ds.on("add", this.updateInfo, this);
13090         this.ds = ds;
13091     }
13092 });/*
13093  * Based on:
13094  * Ext JS Library 1.1.1
13095  * Copyright(c) 2006-2007, Ext JS, LLC.
13096  *
13097  * Originally Released Under LGPL - original licence link has changed is not relivant.
13098  *
13099  * Fork - LGPL
13100  * <script type="text/javascript">
13101  */
13102
13103 /**
13104  * @class Roo.Resizable
13105  * @extends Roo.util.Observable
13106  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13107  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13108  * 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
13109  * the element will be wrapped for you automatically.</p>
13110  * <p>Here is the list of valid resize handles:</p>
13111  * <pre>
13112 Value   Description
13113 ------  -------------------
13114  'n'     north
13115  's'     south
13116  'e'     east
13117  'w'     west
13118  'nw'    northwest
13119  'sw'    southwest
13120  'se'    southeast
13121  'ne'    northeast
13122  'hd'    horizontal drag
13123  'all'   all
13124 </pre>
13125  * <p>Here's an example showing the creation of a typical Resizable:</p>
13126  * <pre><code>
13127 var resizer = new Roo.Resizable("element-id", {
13128     handles: 'all',
13129     minWidth: 200,
13130     minHeight: 100,
13131     maxWidth: 500,
13132     maxHeight: 400,
13133     pinned: true
13134 });
13135 resizer.on("resize", myHandler);
13136 </code></pre>
13137  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13138  * resizer.east.setDisplayed(false);</p>
13139  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13140  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13141  * resize operation's new size (defaults to [0, 0])
13142  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13143  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13144  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13145  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13146  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13147  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13148  * @cfg {Number} width The width of the element in pixels (defaults to null)
13149  * @cfg {Number} height The height of the element in pixels (defaults to null)
13150  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13151  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13152  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13153  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13154  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13155  * in favor of the handles config option (defaults to false)
13156  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13157  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13158  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13159  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13160  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13161  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13162  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13163  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13164  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13165  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13166  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13167  * @constructor
13168  * Create a new resizable component
13169  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13170  * @param {Object} config configuration options
13171   */
13172 Roo.Resizable = function(el, config)
13173 {
13174     this.el = Roo.get(el);
13175
13176     if(config && config.wrap){
13177         config.resizeChild = this.el;
13178         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13179         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13180         this.el.setStyle("overflow", "hidden");
13181         this.el.setPositioning(config.resizeChild.getPositioning());
13182         config.resizeChild.clearPositioning();
13183         if(!config.width || !config.height){
13184             var csize = config.resizeChild.getSize();
13185             this.el.setSize(csize.width, csize.height);
13186         }
13187         if(config.pinned && !config.adjustments){
13188             config.adjustments = "auto";
13189         }
13190     }
13191
13192     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13193     this.proxy.unselectable();
13194     this.proxy.enableDisplayMode('block');
13195
13196     Roo.apply(this, config);
13197
13198     if(this.pinned){
13199         this.disableTrackOver = true;
13200         this.el.addClass("x-resizable-pinned");
13201     }
13202     // if the element isn't positioned, make it relative
13203     var position = this.el.getStyle("position");
13204     if(position != "absolute" && position != "fixed"){
13205         this.el.setStyle("position", "relative");
13206     }
13207     if(!this.handles){ // no handles passed, must be legacy style
13208         this.handles = 's,e,se';
13209         if(this.multiDirectional){
13210             this.handles += ',n,w';
13211         }
13212     }
13213     if(this.handles == "all"){
13214         this.handles = "n s e w ne nw se sw";
13215     }
13216     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13217     var ps = Roo.Resizable.positions;
13218     for(var i = 0, len = hs.length; i < len; i++){
13219         if(hs[i] && ps[hs[i]]){
13220             var pos = ps[hs[i]];
13221             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13222         }
13223     }
13224     // legacy
13225     this.corner = this.southeast;
13226     
13227     // updateBox = the box can move..
13228     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13229         this.updateBox = true;
13230     }
13231
13232     this.activeHandle = null;
13233
13234     if(this.resizeChild){
13235         if(typeof this.resizeChild == "boolean"){
13236             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13237         }else{
13238             this.resizeChild = Roo.get(this.resizeChild, true);
13239         }
13240     }
13241     
13242     if(this.adjustments == "auto"){
13243         var rc = this.resizeChild;
13244         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13245         if(rc && (hw || hn)){
13246             rc.position("relative");
13247             rc.setLeft(hw ? hw.el.getWidth() : 0);
13248             rc.setTop(hn ? hn.el.getHeight() : 0);
13249         }
13250         this.adjustments = [
13251             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13252             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13253         ];
13254     }
13255
13256     if(this.draggable){
13257         this.dd = this.dynamic ?
13258             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13259         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13260     }
13261
13262     // public events
13263     this.addEvents({
13264         /**
13265          * @event beforeresize
13266          * Fired before resize is allowed. Set enabled to false to cancel resize.
13267          * @param {Roo.Resizable} this
13268          * @param {Roo.EventObject} e The mousedown event
13269          */
13270         "beforeresize" : true,
13271         /**
13272          * @event resize
13273          * Fired after a resize.
13274          * @param {Roo.Resizable} this
13275          * @param {Number} width The new width
13276          * @param {Number} height The new height
13277          * @param {Roo.EventObject} e The mouseup event
13278          */
13279         "resize" : true
13280     });
13281
13282     if(this.width !== null && this.height !== null){
13283         this.resizeTo(this.width, this.height);
13284     }else{
13285         this.updateChildSize();
13286     }
13287     if(Roo.isIE){
13288         this.el.dom.style.zoom = 1;
13289     }
13290     Roo.Resizable.superclass.constructor.call(this);
13291 };
13292
13293 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13294         resizeChild : false,
13295         adjustments : [0, 0],
13296         minWidth : 5,
13297         minHeight : 5,
13298         maxWidth : 10000,
13299         maxHeight : 10000,
13300         enabled : true,
13301         animate : false,
13302         duration : .35,
13303         dynamic : false,
13304         handles : false,
13305         multiDirectional : false,
13306         disableTrackOver : false,
13307         easing : 'easeOutStrong',
13308         widthIncrement : 0,
13309         heightIncrement : 0,
13310         pinned : false,
13311         width : null,
13312         height : null,
13313         preserveRatio : false,
13314         transparent: false,
13315         minX: 0,
13316         minY: 0,
13317         draggable: false,
13318
13319         /**
13320          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13321          */
13322         constrainTo: undefined,
13323         /**
13324          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13325          */
13326         resizeRegion: undefined,
13327
13328
13329     /**
13330      * Perform a manual resize
13331      * @param {Number} width
13332      * @param {Number} height
13333      */
13334     resizeTo : function(width, height){
13335         this.el.setSize(width, height);
13336         this.updateChildSize();
13337         this.fireEvent("resize", this, width, height, null);
13338     },
13339
13340     // private
13341     startSizing : function(e, handle){
13342         this.fireEvent("beforeresize", this, e);
13343         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13344
13345             if(!this.overlay){
13346                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13347                 this.overlay.unselectable();
13348                 this.overlay.enableDisplayMode("block");
13349                 this.overlay.on("mousemove", this.onMouseMove, this);
13350                 this.overlay.on("mouseup", this.onMouseUp, this);
13351             }
13352             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13353
13354             this.resizing = true;
13355             this.startBox = this.el.getBox();
13356             this.startPoint = e.getXY();
13357             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13358                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13359
13360             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13361             this.overlay.show();
13362
13363             if(this.constrainTo) {
13364                 var ct = Roo.get(this.constrainTo);
13365                 this.resizeRegion = ct.getRegion().adjust(
13366                     ct.getFrameWidth('t'),
13367                     ct.getFrameWidth('l'),
13368                     -ct.getFrameWidth('b'),
13369                     -ct.getFrameWidth('r')
13370                 );
13371             }
13372
13373             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13374             this.proxy.show();
13375             this.proxy.setBox(this.startBox);
13376             if(!this.dynamic){
13377                 this.proxy.setStyle('visibility', 'visible');
13378             }
13379         }
13380     },
13381
13382     // private
13383     onMouseDown : function(handle, e){
13384         if(this.enabled){
13385             e.stopEvent();
13386             this.activeHandle = handle;
13387             this.startSizing(e, handle);
13388         }
13389     },
13390
13391     // private
13392     onMouseUp : function(e){
13393         var size = this.resizeElement();
13394         this.resizing = false;
13395         this.handleOut();
13396         this.overlay.hide();
13397         this.proxy.hide();
13398         this.fireEvent("resize", this, size.width, size.height, e);
13399     },
13400
13401     // private
13402     updateChildSize : function(){
13403         if(this.resizeChild){
13404             var el = this.el;
13405             var child = this.resizeChild;
13406             var adj = this.adjustments;
13407             if(el.dom.offsetWidth){
13408                 var b = el.getSize(true);
13409                 child.setSize(b.width+adj[0], b.height+adj[1]);
13410             }
13411             // Second call here for IE
13412             // The first call enables instant resizing and
13413             // the second call corrects scroll bars if they
13414             // exist
13415             if(Roo.isIE){
13416                 setTimeout(function(){
13417                     if(el.dom.offsetWidth){
13418                         var b = el.getSize(true);
13419                         child.setSize(b.width+adj[0], b.height+adj[1]);
13420                     }
13421                 }, 10);
13422             }
13423         }
13424     },
13425
13426     // private
13427     snap : function(value, inc, min){
13428         if(!inc || !value) return value;
13429         var newValue = value;
13430         var m = value % inc;
13431         if(m > 0){
13432             if(m > (inc/2)){
13433                 newValue = value + (inc-m);
13434             }else{
13435                 newValue = value - m;
13436             }
13437         }
13438         return Math.max(min, newValue);
13439     },
13440
13441     // private
13442     resizeElement : function(){
13443         var box = this.proxy.getBox();
13444         if(this.updateBox){
13445             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13446         }else{
13447             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13448         }
13449         this.updateChildSize();
13450         if(!this.dynamic){
13451             this.proxy.hide();
13452         }
13453         return box;
13454     },
13455
13456     // private
13457     constrain : function(v, diff, m, mx){
13458         if(v - diff < m){
13459             diff = v - m;
13460         }else if(v - diff > mx){
13461             diff = mx - v;
13462         }
13463         return diff;
13464     },
13465
13466     // private
13467     onMouseMove : function(e){
13468         if(this.enabled){
13469             try{// try catch so if something goes wrong the user doesn't get hung
13470
13471             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13472                 return;
13473             }
13474
13475             //var curXY = this.startPoint;
13476             var curSize = this.curSize || this.startBox;
13477             var x = this.startBox.x, y = this.startBox.y;
13478             var ox = x, oy = y;
13479             var w = curSize.width, h = curSize.height;
13480             var ow = w, oh = h;
13481             var mw = this.minWidth, mh = this.minHeight;
13482             var mxw = this.maxWidth, mxh = this.maxHeight;
13483             var wi = this.widthIncrement;
13484             var hi = this.heightIncrement;
13485
13486             var eventXY = e.getXY();
13487             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13488             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13489
13490             var pos = this.activeHandle.position;
13491
13492             switch(pos){
13493                 case "east":
13494                     w += diffX;
13495                     w = Math.min(Math.max(mw, w), mxw);
13496                     break;
13497              
13498                 case "south":
13499                     h += diffY;
13500                     h = Math.min(Math.max(mh, h), mxh);
13501                     break;
13502                 case "southeast":
13503                     w += diffX;
13504                     h += diffY;
13505                     w = Math.min(Math.max(mw, w), mxw);
13506                     h = Math.min(Math.max(mh, h), mxh);
13507                     break;
13508                 case "north":
13509                     diffY = this.constrain(h, diffY, mh, mxh);
13510                     y += diffY;
13511                     h -= diffY;
13512                     break;
13513                 case "hdrag":
13514                     
13515                     if (wi) {
13516                         var adiffX = Math.abs(diffX);
13517                         var sub = (adiffX % wi); // how much 
13518                         if (sub > (wi/2)) { // far enough to snap
13519                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13520                         } else {
13521                             // remove difference.. 
13522                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13523                         }
13524                     }
13525                     x += diffX;
13526                     x = Math.max(this.minX, x);
13527                     break;
13528                 case "west":
13529                     diffX = this.constrain(w, diffX, mw, mxw);
13530                     x += diffX;
13531                     w -= diffX;
13532                     break;
13533                 case "northeast":
13534                     w += diffX;
13535                     w = Math.min(Math.max(mw, w), mxw);
13536                     diffY = this.constrain(h, diffY, mh, mxh);
13537                     y += diffY;
13538                     h -= diffY;
13539                     break;
13540                 case "northwest":
13541                     diffX = this.constrain(w, diffX, mw, mxw);
13542                     diffY = this.constrain(h, diffY, mh, mxh);
13543                     y += diffY;
13544                     h -= diffY;
13545                     x += diffX;
13546                     w -= diffX;
13547                     break;
13548                case "southwest":
13549                     diffX = this.constrain(w, diffX, mw, mxw);
13550                     h += diffY;
13551                     h = Math.min(Math.max(mh, h), mxh);
13552                     x += diffX;
13553                     w -= diffX;
13554                     break;
13555             }
13556
13557             var sw = this.snap(w, wi, mw);
13558             var sh = this.snap(h, hi, mh);
13559             if(sw != w || sh != h){
13560                 switch(pos){
13561                     case "northeast":
13562                         y -= sh - h;
13563                     break;
13564                     case "north":
13565                         y -= sh - h;
13566                         break;
13567                     case "southwest":
13568                         x -= sw - w;
13569                     break;
13570                     case "west":
13571                         x -= sw - w;
13572                         break;
13573                     case "northwest":
13574                         x -= sw - w;
13575                         y -= sh - h;
13576                     break;
13577                 }
13578                 w = sw;
13579                 h = sh;
13580             }
13581
13582             if(this.preserveRatio){
13583                 switch(pos){
13584                     case "southeast":
13585                     case "east":
13586                         h = oh * (w/ow);
13587                         h = Math.min(Math.max(mh, h), mxh);
13588                         w = ow * (h/oh);
13589                        break;
13590                     case "south":
13591                         w = ow * (h/oh);
13592                         w = Math.min(Math.max(mw, w), mxw);
13593                         h = oh * (w/ow);
13594                         break;
13595                     case "northeast":
13596                         w = ow * (h/oh);
13597                         w = Math.min(Math.max(mw, w), mxw);
13598                         h = oh * (w/ow);
13599                     break;
13600                     case "north":
13601                         var tw = w;
13602                         w = ow * (h/oh);
13603                         w = Math.min(Math.max(mw, w), mxw);
13604                         h = oh * (w/ow);
13605                         x += (tw - w) / 2;
13606                         break;
13607                     case "southwest":
13608                         h = oh * (w/ow);
13609                         h = Math.min(Math.max(mh, h), mxh);
13610                         var tw = w;
13611                         w = ow * (h/oh);
13612                         x += tw - w;
13613                         break;
13614                     case "west":
13615                         var th = h;
13616                         h = oh * (w/ow);
13617                         h = Math.min(Math.max(mh, h), mxh);
13618                         y += (th - h) / 2;
13619                         var tw = w;
13620                         w = ow * (h/oh);
13621                         x += tw - w;
13622                        break;
13623                     case "northwest":
13624                         var tw = w;
13625                         var th = h;
13626                         h = oh * (w/ow);
13627                         h = Math.min(Math.max(mh, h), mxh);
13628                         w = ow * (h/oh);
13629                         y += th - h;
13630                         x += tw - w;
13631                        break;
13632
13633                 }
13634             }
13635             if (pos == 'hdrag') {
13636                 w = ow;
13637             }
13638             this.proxy.setBounds(x, y, w, h);
13639             if(this.dynamic){
13640                 this.resizeElement();
13641             }
13642             }catch(e){}
13643         }
13644     },
13645
13646     // private
13647     handleOver : function(){
13648         if(this.enabled){
13649             this.el.addClass("x-resizable-over");
13650         }
13651     },
13652
13653     // private
13654     handleOut : function(){
13655         if(!this.resizing){
13656             this.el.removeClass("x-resizable-over");
13657         }
13658     },
13659
13660     /**
13661      * Returns the element this component is bound to.
13662      * @return {Roo.Element}
13663      */
13664     getEl : function(){
13665         return this.el;
13666     },
13667
13668     /**
13669      * Returns the resizeChild element (or null).
13670      * @return {Roo.Element}
13671      */
13672     getResizeChild : function(){
13673         return this.resizeChild;
13674     },
13675
13676     /**
13677      * Destroys this resizable. If the element was wrapped and
13678      * removeEl is not true then the element remains.
13679      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13680      */
13681     destroy : function(removeEl){
13682         this.proxy.remove();
13683         if(this.overlay){
13684             this.overlay.removeAllListeners();
13685             this.overlay.remove();
13686         }
13687         var ps = Roo.Resizable.positions;
13688         for(var k in ps){
13689             if(typeof ps[k] != "function" && this[ps[k]]){
13690                 var h = this[ps[k]];
13691                 h.el.removeAllListeners();
13692                 h.el.remove();
13693             }
13694         }
13695         if(removeEl){
13696             this.el.update("");
13697             this.el.remove();
13698         }
13699     }
13700 });
13701
13702 // private
13703 // hash to map config positions to true positions
13704 Roo.Resizable.positions = {
13705     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13706     hd: "hdrag"
13707 };
13708
13709 // private
13710 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13711     if(!this.tpl){
13712         // only initialize the template if resizable is used
13713         var tpl = Roo.DomHelper.createTemplate(
13714             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13715         );
13716         tpl.compile();
13717         Roo.Resizable.Handle.prototype.tpl = tpl;
13718     }
13719     this.position = pos;
13720     this.rz = rz;
13721     // show north drag fro topdra
13722     var handlepos = pos == 'hdrag' ? 'north' : pos;
13723     
13724     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13725     if (pos == 'hdrag') {
13726         this.el.setStyle('cursor', 'pointer');
13727     }
13728     this.el.unselectable();
13729     if(transparent){
13730         this.el.setOpacity(0);
13731     }
13732     this.el.on("mousedown", this.onMouseDown, this);
13733     if(!disableTrackOver){
13734         this.el.on("mouseover", this.onMouseOver, this);
13735         this.el.on("mouseout", this.onMouseOut, this);
13736     }
13737 };
13738
13739 // private
13740 Roo.Resizable.Handle.prototype = {
13741     afterResize : function(rz){
13742         // do nothing
13743     },
13744     // private
13745     onMouseDown : function(e){
13746         this.rz.onMouseDown(this, e);
13747     },
13748     // private
13749     onMouseOver : function(e){
13750         this.rz.handleOver(this, e);
13751     },
13752     // private
13753     onMouseOut : function(e){
13754         this.rz.handleOut(this, e);
13755     }
13756 };/*
13757  * Based on:
13758  * Ext JS Library 1.1.1
13759  * Copyright(c) 2006-2007, Ext JS, LLC.
13760  *
13761  * Originally Released Under LGPL - original licence link has changed is not relivant.
13762  *
13763  * Fork - LGPL
13764  * <script type="text/javascript">
13765  */
13766
13767 /**
13768  * @class Roo.Editor
13769  * @extends Roo.Component
13770  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13771  * @constructor
13772  * Create a new Editor
13773  * @param {Roo.form.Field} field The Field object (or descendant)
13774  * @param {Object} config The config object
13775  */
13776 Roo.Editor = function(field, config){
13777     Roo.Editor.superclass.constructor.call(this, config);
13778     this.field = field;
13779     this.addEvents({
13780         /**
13781              * @event beforestartedit
13782              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13783              * false from the handler of this event.
13784              * @param {Editor} this
13785              * @param {Roo.Element} boundEl The underlying element bound to this editor
13786              * @param {Mixed} value The field value being set
13787              */
13788         "beforestartedit" : true,
13789         /**
13790              * @event startedit
13791              * Fires when this editor is displayed
13792              * @param {Roo.Element} boundEl The underlying element bound to this editor
13793              * @param {Mixed} value The starting field value
13794              */
13795         "startedit" : true,
13796         /**
13797              * @event beforecomplete
13798              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13799              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13800              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13801              * event will not fire since no edit actually occurred.
13802              * @param {Editor} this
13803              * @param {Mixed} value The current field value
13804              * @param {Mixed} startValue The original field value
13805              */
13806         "beforecomplete" : true,
13807         /**
13808              * @event complete
13809              * Fires after editing is complete and any changed value has been written to the underlying field.
13810              * @param {Editor} this
13811              * @param {Mixed} value The current field value
13812              * @param {Mixed} startValue The original field value
13813              */
13814         "complete" : true,
13815         /**
13816          * @event specialkey
13817          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13818          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13819          * @param {Roo.form.Field} this
13820          * @param {Roo.EventObject} e The event object
13821          */
13822         "specialkey" : true
13823     });
13824 };
13825
13826 Roo.extend(Roo.Editor, Roo.Component, {
13827     /**
13828      * @cfg {Boolean/String} autosize
13829      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13830      * or "height" to adopt the height only (defaults to false)
13831      */
13832     /**
13833      * @cfg {Boolean} revertInvalid
13834      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13835      * validation fails (defaults to true)
13836      */
13837     /**
13838      * @cfg {Boolean} ignoreNoChange
13839      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13840      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13841      * will never be ignored.
13842      */
13843     /**
13844      * @cfg {Boolean} hideEl
13845      * False to keep the bound element visible while the editor is displayed (defaults to true)
13846      */
13847     /**
13848      * @cfg {Mixed} value
13849      * The data value of the underlying field (defaults to "")
13850      */
13851     value : "",
13852     /**
13853      * @cfg {String} alignment
13854      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13855      */
13856     alignment: "c-c?",
13857     /**
13858      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13859      * for bottom-right shadow (defaults to "frame")
13860      */
13861     shadow : "frame",
13862     /**
13863      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13864      */
13865     constrain : false,
13866     /**
13867      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13868      */
13869     completeOnEnter : false,
13870     /**
13871      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
13872      */
13873     cancelOnEsc : false,
13874     /**
13875      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
13876      */
13877     updateEl : false,
13878
13879     // private
13880     onRender : function(ct, position){
13881         this.el = new Roo.Layer({
13882             shadow: this.shadow,
13883             cls: "x-editor",
13884             parentEl : ct,
13885             shim : this.shim,
13886             shadowOffset:4,
13887             id: this.id,
13888             constrain: this.constrain
13889         });
13890         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
13891         if(this.field.msgTarget != 'title'){
13892             this.field.msgTarget = 'qtip';
13893         }
13894         this.field.render(this.el);
13895         if(Roo.isGecko){
13896             this.field.el.dom.setAttribute('autocomplete', 'off');
13897         }
13898         this.field.on("specialkey", this.onSpecialKey, this);
13899         if(this.swallowKeys){
13900             this.field.el.swallowEvent(['keydown','keypress']);
13901         }
13902         this.field.show();
13903         this.field.on("blur", this.onBlur, this);
13904         if(this.field.grow){
13905             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
13906         }
13907     },
13908
13909     onSpecialKey : function(field, e){
13910         //Roo.log('editor onSpecialKey');
13911         if(this.completeOnEnter && e.getKey() == e.ENTER){
13912             e.stopEvent();
13913             this.completeEdit();
13914         }else if(this.cancelOnEsc && e.getKey() == e.ESC){
13915             this.cancelEdit();
13916         }else{
13917             this.fireEvent('specialkey', field, e);
13918         }
13919     },
13920
13921     /**
13922      * Starts the editing process and shows the editor.
13923      * @param {String/HTMLElement/Element} el The element to edit
13924      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
13925       * to the innerHTML of el.
13926      */
13927     startEdit : function(el, value){
13928         if(this.editing){
13929             this.completeEdit();
13930         }
13931         this.boundEl = Roo.get(el);
13932         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
13933         if(!this.rendered){
13934             this.render(this.parentEl || document.body);
13935         }
13936         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
13937             return;
13938         }
13939         this.startValue = v;
13940         this.field.setValue(v);
13941         if(this.autoSize){
13942             var sz = this.boundEl.getSize();
13943             switch(this.autoSize){
13944                 case "width":
13945                 this.setSize(sz.width,  "");
13946                 break;
13947                 case "height":
13948                 this.setSize("",  sz.height);
13949                 break;
13950                 default:
13951                 this.setSize(sz.width,  sz.height);
13952             }
13953         }
13954         this.el.alignTo(this.boundEl, this.alignment);
13955         this.editing = true;
13956         if(Roo.QuickTips){
13957             Roo.QuickTips.disable();
13958         }
13959         this.show();
13960     },
13961
13962     /**
13963      * Sets the height and width of this editor.
13964      * @param {Number} width The new width
13965      * @param {Number} height The new height
13966      */
13967     setSize : function(w, h){
13968         this.field.setSize(w, h);
13969         if(this.el){
13970             this.el.sync();
13971         }
13972     },
13973
13974     /**
13975      * Realigns the editor to the bound field based on the current alignment config value.
13976      */
13977     realign : function(){
13978         this.el.alignTo(this.boundEl, this.alignment);
13979     },
13980
13981     /**
13982      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
13983      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
13984      */
13985     completeEdit : function(remainVisible){
13986         if(!this.editing){
13987             return;
13988         }
13989         var v = this.getValue();
13990         if(this.revertInvalid !== false && !this.field.isValid()){
13991             v = this.startValue;
13992             this.cancelEdit(true);
13993         }
13994         if(String(v) === String(this.startValue) && this.ignoreNoChange){
13995             this.editing = false;
13996             this.hide();
13997             return;
13998         }
13999         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
14000             this.editing = false;
14001             if(this.updateEl && this.boundEl){
14002                 this.boundEl.update(v);
14003             }
14004             if(remainVisible !== true){
14005                 this.hide();
14006             }
14007             this.fireEvent("complete", this, v, this.startValue);
14008         }
14009     },
14010
14011     // private
14012     onShow : function(){
14013         this.el.show();
14014         if(this.hideEl !== false){
14015             this.boundEl.hide();
14016         }
14017         this.field.show();
14018         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14019             this.fixIEFocus = true;
14020             this.deferredFocus.defer(50, this);
14021         }else{
14022             this.field.focus();
14023         }
14024         this.fireEvent("startedit", this.boundEl, this.startValue);
14025     },
14026
14027     deferredFocus : function(){
14028         if(this.editing){
14029             this.field.focus();
14030         }
14031     },
14032
14033     /**
14034      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14035      * reverted to the original starting value.
14036      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14037      * cancel (defaults to false)
14038      */
14039     cancelEdit : function(remainVisible){
14040         if(this.editing){
14041             this.setValue(this.startValue);
14042             if(remainVisible !== true){
14043                 this.hide();
14044             }
14045         }
14046     },
14047
14048     // private
14049     onBlur : function(){
14050         if(this.allowBlur !== true && this.editing){
14051             this.completeEdit();
14052         }
14053     },
14054
14055     // private
14056     onHide : function(){
14057         if(this.editing){
14058             this.completeEdit();
14059             return;
14060         }
14061         this.field.blur();
14062         if(this.field.collapse){
14063             this.field.collapse();
14064         }
14065         this.el.hide();
14066         if(this.hideEl !== false){
14067             this.boundEl.show();
14068         }
14069         if(Roo.QuickTips){
14070             Roo.QuickTips.enable();
14071         }
14072     },
14073
14074     /**
14075      * Sets the data value of the editor
14076      * @param {Mixed} value Any valid value supported by the underlying field
14077      */
14078     setValue : function(v){
14079         this.field.setValue(v);
14080     },
14081
14082     /**
14083      * Gets the data value of the editor
14084      * @return {Mixed} The data value
14085      */
14086     getValue : function(){
14087         return this.field.getValue();
14088     }
14089 });/*
14090  * Based on:
14091  * Ext JS Library 1.1.1
14092  * Copyright(c) 2006-2007, Ext JS, LLC.
14093  *
14094  * Originally Released Under LGPL - original licence link has changed is not relivant.
14095  *
14096  * Fork - LGPL
14097  * <script type="text/javascript">
14098  */
14099  
14100 /**
14101  * @class Roo.BasicDialog
14102  * @extends Roo.util.Observable
14103  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14104  * <pre><code>
14105 var dlg = new Roo.BasicDialog("my-dlg", {
14106     height: 200,
14107     width: 300,
14108     minHeight: 100,
14109     minWidth: 150,
14110     modal: true,
14111     proxyDrag: true,
14112     shadow: true
14113 });
14114 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14115 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14116 dlg.addButton('Cancel', dlg.hide, dlg);
14117 dlg.show();
14118 </code></pre>
14119   <b>A Dialog should always be a direct child of the body element.</b>
14120  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14121  * @cfg {String} title Default text to display in the title bar (defaults to null)
14122  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14123  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14124  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14125  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14126  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14127  * (defaults to null with no animation)
14128  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14129  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14130  * property for valid values (defaults to 'all')
14131  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14132  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14133  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14134  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14135  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14136  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14137  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14138  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14139  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14140  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14141  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14142  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14143  * draggable = true (defaults to false)
14144  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14145  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14146  * shadow (defaults to false)
14147  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14148  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14149  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14150  * @cfg {Array} buttons Array of buttons
14151  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14152  * @constructor
14153  * Create a new BasicDialog.
14154  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14155  * @param {Object} config Configuration options
14156  */
14157 Roo.BasicDialog = function(el, config){
14158     this.el = Roo.get(el);
14159     var dh = Roo.DomHelper;
14160     if(!this.el && config && config.autoCreate){
14161         if(typeof config.autoCreate == "object"){
14162             if(!config.autoCreate.id){
14163                 config.autoCreate.id = el;
14164             }
14165             this.el = dh.append(document.body,
14166                         config.autoCreate, true);
14167         }else{
14168             this.el = dh.append(document.body,
14169                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14170         }
14171     }
14172     el = this.el;
14173     el.setDisplayed(true);
14174     el.hide = this.hideAction;
14175     this.id = el.id;
14176     el.addClass("x-dlg");
14177
14178     Roo.apply(this, config);
14179
14180     this.proxy = el.createProxy("x-dlg-proxy");
14181     this.proxy.hide = this.hideAction;
14182     this.proxy.setOpacity(.5);
14183     this.proxy.hide();
14184
14185     if(config.width){
14186         el.setWidth(config.width);
14187     }
14188     if(config.height){
14189         el.setHeight(config.height);
14190     }
14191     this.size = el.getSize();
14192     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14193         this.xy = [config.x,config.y];
14194     }else{
14195         this.xy = el.getCenterXY(true);
14196     }
14197     /** The header element @type Roo.Element */
14198     this.header = el.child("> .x-dlg-hd");
14199     /** The body element @type Roo.Element */
14200     this.body = el.child("> .x-dlg-bd");
14201     /** The footer element @type Roo.Element */
14202     this.footer = el.child("> .x-dlg-ft");
14203
14204     if(!this.header){
14205         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14206     }
14207     if(!this.body){
14208         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14209     }
14210
14211     this.header.unselectable();
14212     if(this.title){
14213         this.header.update(this.title);
14214     }
14215     // this element allows the dialog to be focused for keyboard event
14216     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14217     this.focusEl.swallowEvent("click", true);
14218
14219     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14220
14221     // wrap the body and footer for special rendering
14222     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14223     if(this.footer){
14224         this.bwrap.dom.appendChild(this.footer.dom);
14225     }
14226
14227     this.bg = this.el.createChild({
14228         tag: "div", cls:"x-dlg-bg",
14229         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14230     });
14231     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14232
14233
14234     if(this.autoScroll !== false && !this.autoTabs){
14235         this.body.setStyle("overflow", "auto");
14236     }
14237
14238     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14239
14240     if(this.closable !== false){
14241         this.el.addClass("x-dlg-closable");
14242         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14243         this.close.on("click", this.closeClick, this);
14244         this.close.addClassOnOver("x-dlg-close-over");
14245     }
14246     if(this.collapsible !== false){
14247         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14248         this.collapseBtn.on("click", this.collapseClick, this);
14249         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14250         this.header.on("dblclick", this.collapseClick, this);
14251     }
14252     if(this.resizable !== false){
14253         this.el.addClass("x-dlg-resizable");
14254         this.resizer = new Roo.Resizable(el, {
14255             minWidth: this.minWidth || 80,
14256             minHeight:this.minHeight || 80,
14257             handles: this.resizeHandles || "all",
14258             pinned: true
14259         });
14260         this.resizer.on("beforeresize", this.beforeResize, this);
14261         this.resizer.on("resize", this.onResize, this);
14262     }
14263     if(this.draggable !== false){
14264         el.addClass("x-dlg-draggable");
14265         if (!this.proxyDrag) {
14266             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14267         }
14268         else {
14269             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14270         }
14271         dd.setHandleElId(this.header.id);
14272         dd.endDrag = this.endMove.createDelegate(this);
14273         dd.startDrag = this.startMove.createDelegate(this);
14274         dd.onDrag = this.onDrag.createDelegate(this);
14275         dd.scroll = false;
14276         this.dd = dd;
14277     }
14278     if(this.modal){
14279         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14280         this.mask.enableDisplayMode("block");
14281         this.mask.hide();
14282         this.el.addClass("x-dlg-modal");
14283     }
14284     if(this.shadow){
14285         this.shadow = new Roo.Shadow({
14286             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14287             offset : this.shadowOffset
14288         });
14289     }else{
14290         this.shadowOffset = 0;
14291     }
14292     if(Roo.useShims && this.shim !== false){
14293         this.shim = this.el.createShim();
14294         this.shim.hide = this.hideAction;
14295         this.shim.hide();
14296     }else{
14297         this.shim = false;
14298     }
14299     if(this.autoTabs){
14300         this.initTabs();
14301     }
14302     if (this.buttons) { 
14303         var bts= this.buttons;
14304         this.buttons = [];
14305         Roo.each(bts, function(b) {
14306             this.addButton(b);
14307         }, this);
14308     }
14309     
14310     
14311     this.addEvents({
14312         /**
14313          * @event keydown
14314          * Fires when a key is pressed
14315          * @param {Roo.BasicDialog} this
14316          * @param {Roo.EventObject} e
14317          */
14318         "keydown" : true,
14319         /**
14320          * @event move
14321          * Fires when this dialog is moved by the user.
14322          * @param {Roo.BasicDialog} this
14323          * @param {Number} x The new page X
14324          * @param {Number} y The new page Y
14325          */
14326         "move" : true,
14327         /**
14328          * @event resize
14329          * Fires when this dialog is resized by the user.
14330          * @param {Roo.BasicDialog} this
14331          * @param {Number} width The new width
14332          * @param {Number} height The new height
14333          */
14334         "resize" : true,
14335         /**
14336          * @event beforehide
14337          * Fires before this dialog is hidden.
14338          * @param {Roo.BasicDialog} this
14339          */
14340         "beforehide" : true,
14341         /**
14342          * @event hide
14343          * Fires when this dialog is hidden.
14344          * @param {Roo.BasicDialog} this
14345          */
14346         "hide" : true,
14347         /**
14348          * @event beforeshow
14349          * Fires before this dialog is shown.
14350          * @param {Roo.BasicDialog} this
14351          */
14352         "beforeshow" : true,
14353         /**
14354          * @event show
14355          * Fires when this dialog is shown.
14356          * @param {Roo.BasicDialog} this
14357          */
14358         "show" : true
14359     });
14360     el.on("keydown", this.onKeyDown, this);
14361     el.on("mousedown", this.toFront, this);
14362     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14363     this.el.hide();
14364     Roo.DialogManager.register(this);
14365     Roo.BasicDialog.superclass.constructor.call(this);
14366 };
14367
14368 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14369     shadowOffset: Roo.isIE ? 6 : 5,
14370     minHeight: 80,
14371     minWidth: 200,
14372     minButtonWidth: 75,
14373     defaultButton: null,
14374     buttonAlign: "right",
14375     tabTag: 'div',
14376     firstShow: true,
14377
14378     /**
14379      * Sets the dialog title text
14380      * @param {String} text The title text to display
14381      * @return {Roo.BasicDialog} this
14382      */
14383     setTitle : function(text){
14384         this.header.update(text);
14385         return this;
14386     },
14387
14388     // private
14389     closeClick : function(){
14390         this.hide();
14391     },
14392
14393     // private
14394     collapseClick : function(){
14395         this[this.collapsed ? "expand" : "collapse"]();
14396     },
14397
14398     /**
14399      * Collapses the dialog to its minimized state (only the title bar is visible).
14400      * Equivalent to the user clicking the collapse dialog button.
14401      */
14402     collapse : function(){
14403         if(!this.collapsed){
14404             this.collapsed = true;
14405             this.el.addClass("x-dlg-collapsed");
14406             this.restoreHeight = this.el.getHeight();
14407             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14408         }
14409     },
14410
14411     /**
14412      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14413      * clicking the expand dialog button.
14414      */
14415     expand : function(){
14416         if(this.collapsed){
14417             this.collapsed = false;
14418             this.el.removeClass("x-dlg-collapsed");
14419             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14420         }
14421     },
14422
14423     /**
14424      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14425      * @return {Roo.TabPanel} The tabs component
14426      */
14427     initTabs : function(){
14428         var tabs = this.getTabs();
14429         while(tabs.getTab(0)){
14430             tabs.removeTab(0);
14431         }
14432         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14433             var dom = el.dom;
14434             tabs.addTab(Roo.id(dom), dom.title);
14435             dom.title = "";
14436         });
14437         tabs.activate(0);
14438         return tabs;
14439     },
14440
14441     // private
14442     beforeResize : function(){
14443         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14444     },
14445
14446     // private
14447     onResize : function(){
14448         this.refreshSize();
14449         this.syncBodyHeight();
14450         this.adjustAssets();
14451         this.focus();
14452         this.fireEvent("resize", this, this.size.width, this.size.height);
14453     },
14454
14455     // private
14456     onKeyDown : function(e){
14457         if(this.isVisible()){
14458             this.fireEvent("keydown", this, e);
14459         }
14460     },
14461
14462     /**
14463      * Resizes the dialog.
14464      * @param {Number} width
14465      * @param {Number} height
14466      * @return {Roo.BasicDialog} this
14467      */
14468     resizeTo : function(width, height){
14469         this.el.setSize(width, height);
14470         this.size = {width: width, height: height};
14471         this.syncBodyHeight();
14472         if(this.fixedcenter){
14473             this.center();
14474         }
14475         if(this.isVisible()){
14476             this.constrainXY();
14477             this.adjustAssets();
14478         }
14479         this.fireEvent("resize", this, width, height);
14480         return this;
14481     },
14482
14483
14484     /**
14485      * Resizes the dialog to fit the specified content size.
14486      * @param {Number} width
14487      * @param {Number} height
14488      * @return {Roo.BasicDialog} this
14489      */
14490     setContentSize : function(w, h){
14491         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14492         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14493         //if(!this.el.isBorderBox()){
14494             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14495             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14496         //}
14497         if(this.tabs){
14498             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14499             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14500         }
14501         this.resizeTo(w, h);
14502         return this;
14503     },
14504
14505     /**
14506      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14507      * executed in response to a particular key being pressed while the dialog is active.
14508      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14509      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14510      * @param {Function} fn The function to call
14511      * @param {Object} scope (optional) The scope of the function
14512      * @return {Roo.BasicDialog} this
14513      */
14514     addKeyListener : function(key, fn, scope){
14515         var keyCode, shift, ctrl, alt;
14516         if(typeof key == "object" && !(key instanceof Array)){
14517             keyCode = key["key"];
14518             shift = key["shift"];
14519             ctrl = key["ctrl"];
14520             alt = key["alt"];
14521         }else{
14522             keyCode = key;
14523         }
14524         var handler = function(dlg, e){
14525             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14526                 var k = e.getKey();
14527                 if(keyCode instanceof Array){
14528                     for(var i = 0, len = keyCode.length; i < len; i++){
14529                         if(keyCode[i] == k){
14530                           fn.call(scope || window, dlg, k, e);
14531                           return;
14532                         }
14533                     }
14534                 }else{
14535                     if(k == keyCode){
14536                         fn.call(scope || window, dlg, k, e);
14537                     }
14538                 }
14539             }
14540         };
14541         this.on("keydown", handler);
14542         return this;
14543     },
14544
14545     /**
14546      * Returns the TabPanel component (creates it if it doesn't exist).
14547      * Note: If you wish to simply check for the existence of tabs without creating them,
14548      * check for a null 'tabs' property.
14549      * @return {Roo.TabPanel} The tabs component
14550      */
14551     getTabs : function(){
14552         if(!this.tabs){
14553             this.el.addClass("x-dlg-auto-tabs");
14554             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14555             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14556         }
14557         return this.tabs;
14558     },
14559
14560     /**
14561      * Adds a button to the footer section of the dialog.
14562      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14563      * object or a valid Roo.DomHelper element config
14564      * @param {Function} handler The function called when the button is clicked
14565      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14566      * @return {Roo.Button} The new button
14567      */
14568     addButton : function(config, handler, scope){
14569         var dh = Roo.DomHelper;
14570         if(!this.footer){
14571             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14572         }
14573         if(!this.btnContainer){
14574             var tb = this.footer.createChild({
14575
14576                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14577                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14578             }, null, true);
14579             this.btnContainer = tb.firstChild.firstChild.firstChild;
14580         }
14581         var bconfig = {
14582             handler: handler,
14583             scope: scope,
14584             minWidth: this.minButtonWidth,
14585             hideParent:true
14586         };
14587         if(typeof config == "string"){
14588             bconfig.text = config;
14589         }else{
14590             if(config.tag){
14591                 bconfig.dhconfig = config;
14592             }else{
14593                 Roo.apply(bconfig, config);
14594             }
14595         }
14596         var fc = false;
14597         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14598             bconfig.position = Math.max(0, bconfig.position);
14599             fc = this.btnContainer.childNodes[bconfig.position];
14600         }
14601          
14602         var btn = new Roo.Button(
14603             fc ? 
14604                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14605                 : this.btnContainer.appendChild(document.createElement("td")),
14606             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14607             bconfig
14608         );
14609         this.syncBodyHeight();
14610         if(!this.buttons){
14611             /**
14612              * Array of all the buttons that have been added to this dialog via addButton
14613              * @type Array
14614              */
14615             this.buttons = [];
14616         }
14617         this.buttons.push(btn);
14618         return btn;
14619     },
14620
14621     /**
14622      * Sets the default button to be focused when the dialog is displayed.
14623      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14624      * @return {Roo.BasicDialog} this
14625      */
14626     setDefaultButton : function(btn){
14627         this.defaultButton = btn;
14628         return this;
14629     },
14630
14631     // private
14632     getHeaderFooterHeight : function(safe){
14633         var height = 0;
14634         if(this.header){
14635            height += this.header.getHeight();
14636         }
14637         if(this.footer){
14638            var fm = this.footer.getMargins();
14639             height += (this.footer.getHeight()+fm.top+fm.bottom);
14640         }
14641         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14642         height += this.centerBg.getPadding("tb");
14643         return height;
14644     },
14645
14646     // private
14647     syncBodyHeight : function(){
14648         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
14649         var height = this.size.height - this.getHeaderFooterHeight(false);
14650         bd.setHeight(height-bd.getMargins("tb"));
14651         var hh = this.header.getHeight();
14652         var h = this.size.height-hh;
14653         cb.setHeight(h);
14654         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14655         bw.setHeight(h-cb.getPadding("tb"));
14656         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14657         bd.setWidth(bw.getWidth(true));
14658         if(this.tabs){
14659             this.tabs.syncHeight();
14660             if(Roo.isIE){
14661                 this.tabs.el.repaint();
14662             }
14663         }
14664     },
14665
14666     /**
14667      * Restores the previous state of the dialog if Roo.state is configured.
14668      * @return {Roo.BasicDialog} this
14669      */
14670     restoreState : function(){
14671         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14672         if(box && box.width){
14673             this.xy = [box.x, box.y];
14674             this.resizeTo(box.width, box.height);
14675         }
14676         return this;
14677     },
14678
14679     // private
14680     beforeShow : function(){
14681         this.expand();
14682         if(this.fixedcenter){
14683             this.xy = this.el.getCenterXY(true);
14684         }
14685         if(this.modal){
14686             Roo.get(document.body).addClass("x-body-masked");
14687             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14688             this.mask.show();
14689         }
14690         this.constrainXY();
14691     },
14692
14693     // private
14694     animShow : function(){
14695         var b = Roo.get(this.animateTarget).getBox();
14696         this.proxy.setSize(b.width, b.height);
14697         this.proxy.setLocation(b.x, b.y);
14698         this.proxy.show();
14699         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14700                     true, .35, this.showEl.createDelegate(this));
14701     },
14702
14703     /**
14704      * Shows the dialog.
14705      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14706      * @return {Roo.BasicDialog} this
14707      */
14708     show : function(animateTarget){
14709         if (this.fireEvent("beforeshow", this) === false){
14710             return;
14711         }
14712         if(this.syncHeightBeforeShow){
14713             this.syncBodyHeight();
14714         }else if(this.firstShow){
14715             this.firstShow = false;
14716             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14717         }
14718         this.animateTarget = animateTarget || this.animateTarget;
14719         if(!this.el.isVisible()){
14720             this.beforeShow();
14721             if(this.animateTarget && Roo.get(this.animateTarget)){
14722                 this.animShow();
14723             }else{
14724                 this.showEl();
14725             }
14726         }
14727         return this;
14728     },
14729
14730     // private
14731     showEl : function(){
14732         this.proxy.hide();
14733         this.el.setXY(this.xy);
14734         this.el.show();
14735         this.adjustAssets(true);
14736         this.toFront();
14737         this.focus();
14738         // IE peekaboo bug - fix found by Dave Fenwick
14739         if(Roo.isIE){
14740             this.el.repaint();
14741         }
14742         this.fireEvent("show", this);
14743     },
14744
14745     /**
14746      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14747      * dialog itself will receive focus.
14748      */
14749     focus : function(){
14750         if(this.defaultButton){
14751             this.defaultButton.focus();
14752         }else{
14753             this.focusEl.focus();
14754         }
14755     },
14756
14757     // private
14758     constrainXY : function(){
14759         if(this.constraintoviewport !== false){
14760             if(!this.viewSize){
14761                 if(this.container){
14762                     var s = this.container.getSize();
14763                     this.viewSize = [s.width, s.height];
14764                 }else{
14765                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14766                 }
14767             }
14768             var s = Roo.get(this.container||document).getScroll();
14769
14770             var x = this.xy[0], y = this.xy[1];
14771             var w = this.size.width, h = this.size.height;
14772             var vw = this.viewSize[0], vh = this.viewSize[1];
14773             // only move it if it needs it
14774             var moved = false;
14775             // first validate right/bottom
14776             if(x + w > vw+s.left){
14777                 x = vw - w;
14778                 moved = true;
14779             }
14780             if(y + h > vh+s.top){
14781                 y = vh - h;
14782                 moved = true;
14783             }
14784             // then make sure top/left isn't negative
14785             if(x < s.left){
14786                 x = s.left;
14787                 moved = true;
14788             }
14789             if(y < s.top){
14790                 y = s.top;
14791                 moved = true;
14792             }
14793             if(moved){
14794                 // cache xy
14795                 this.xy = [x, y];
14796                 if(this.isVisible()){
14797                     this.el.setLocation(x, y);
14798                     this.adjustAssets();
14799                 }
14800             }
14801         }
14802     },
14803
14804     // private
14805     onDrag : function(){
14806         if(!this.proxyDrag){
14807             this.xy = this.el.getXY();
14808             this.adjustAssets();
14809         }
14810     },
14811
14812     // private
14813     adjustAssets : function(doShow){
14814         var x = this.xy[0], y = this.xy[1];
14815         var w = this.size.width, h = this.size.height;
14816         if(doShow === true){
14817             if(this.shadow){
14818                 this.shadow.show(this.el);
14819             }
14820             if(this.shim){
14821                 this.shim.show();
14822             }
14823         }
14824         if(this.shadow && this.shadow.isVisible()){
14825             this.shadow.show(this.el);
14826         }
14827         if(this.shim && this.shim.isVisible()){
14828             this.shim.setBounds(x, y, w, h);
14829         }
14830     },
14831
14832     // private
14833     adjustViewport : function(w, h){
14834         if(!w || !h){
14835             w = Roo.lib.Dom.getViewWidth();
14836             h = Roo.lib.Dom.getViewHeight();
14837         }
14838         // cache the size
14839         this.viewSize = [w, h];
14840         if(this.modal && this.mask.isVisible()){
14841             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14842             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14843         }
14844         if(this.isVisible()){
14845             this.constrainXY();
14846         }
14847     },
14848
14849     /**
14850      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14851      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14852      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14853      */
14854     destroy : function(removeEl){
14855         if(this.isVisible()){
14856             this.animateTarget = null;
14857             this.hide();
14858         }
14859         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14860         if(this.tabs){
14861             this.tabs.destroy(removeEl);
14862         }
14863         Roo.destroy(
14864              this.shim,
14865              this.proxy,
14866              this.resizer,
14867              this.close,
14868              this.mask
14869         );
14870         if(this.dd){
14871             this.dd.unreg();
14872         }
14873         if(this.buttons){
14874            for(var i = 0, len = this.buttons.length; i < len; i++){
14875                this.buttons[i].destroy();
14876            }
14877         }
14878         this.el.removeAllListeners();
14879         if(removeEl === true){
14880             this.el.update("");
14881             this.el.remove();
14882         }
14883         Roo.DialogManager.unregister(this);
14884     },
14885
14886     // private
14887     startMove : function(){
14888         if(this.proxyDrag){
14889             this.proxy.show();
14890         }
14891         if(this.constraintoviewport !== false){
14892             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
14893         }
14894     },
14895
14896     // private
14897     endMove : function(){
14898         if(!this.proxyDrag){
14899             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
14900         }else{
14901             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
14902             this.proxy.hide();
14903         }
14904         this.refreshSize();
14905         this.adjustAssets();
14906         this.focus();
14907         this.fireEvent("move", this, this.xy[0], this.xy[1]);
14908     },
14909
14910     /**
14911      * Brings this dialog to the front of any other visible dialogs
14912      * @return {Roo.BasicDialog} this
14913      */
14914     toFront : function(){
14915         Roo.DialogManager.bringToFront(this);
14916         return this;
14917     },
14918
14919     /**
14920      * Sends this dialog to the back (under) of any other visible dialogs
14921      * @return {Roo.BasicDialog} this
14922      */
14923     toBack : function(){
14924         Roo.DialogManager.sendToBack(this);
14925         return this;
14926     },
14927
14928     /**
14929      * Centers this dialog in the viewport
14930      * @return {Roo.BasicDialog} this
14931      */
14932     center : function(){
14933         var xy = this.el.getCenterXY(true);
14934         this.moveTo(xy[0], xy[1]);
14935         return this;
14936     },
14937
14938     /**
14939      * Moves the dialog's top-left corner to the specified point
14940      * @param {Number} x
14941      * @param {Number} y
14942      * @return {Roo.BasicDialog} this
14943      */
14944     moveTo : function(x, y){
14945         this.xy = [x,y];
14946         if(this.isVisible()){
14947             this.el.setXY(this.xy);
14948             this.adjustAssets();
14949         }
14950         return this;
14951     },
14952
14953     /**
14954      * Aligns the dialog to the specified element
14955      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14956      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
14957      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14958      * @return {Roo.BasicDialog} this
14959      */
14960     alignTo : function(element, position, offsets){
14961         this.xy = this.el.getAlignToXY(element, position, offsets);
14962         if(this.isVisible()){
14963             this.el.setXY(this.xy);
14964             this.adjustAssets();
14965         }
14966         return this;
14967     },
14968
14969     /**
14970      * Anchors an element to another element and realigns it when the window is resized.
14971      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14972      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
14973      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14974      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
14975      * is a number, it is used as the buffer delay (defaults to 50ms).
14976      * @return {Roo.BasicDialog} this
14977      */
14978     anchorTo : function(el, alignment, offsets, monitorScroll){
14979         var action = function(){
14980             this.alignTo(el, alignment, offsets);
14981         };
14982         Roo.EventManager.onWindowResize(action, this);
14983         var tm = typeof monitorScroll;
14984         if(tm != 'undefined'){
14985             Roo.EventManager.on(window, 'scroll', action, this,
14986                 {buffer: tm == 'number' ? monitorScroll : 50});
14987         }
14988         action.call(this);
14989         return this;
14990     },
14991
14992     /**
14993      * Returns true if the dialog is visible
14994      * @return {Boolean}
14995      */
14996     isVisible : function(){
14997         return this.el.isVisible();
14998     },
14999
15000     // private
15001     animHide : function(callback){
15002         var b = Roo.get(this.animateTarget).getBox();
15003         this.proxy.show();
15004         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15005         this.el.hide();
15006         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15007                     this.hideEl.createDelegate(this, [callback]));
15008     },
15009
15010     /**
15011      * Hides the dialog.
15012      * @param {Function} callback (optional) Function to call when the dialog is hidden
15013      * @return {Roo.BasicDialog} this
15014      */
15015     hide : function(callback){
15016         if (this.fireEvent("beforehide", this) === false){
15017             return;
15018         }
15019         if(this.shadow){
15020             this.shadow.hide();
15021         }
15022         if(this.shim) {
15023           this.shim.hide();
15024         }
15025         // sometimes animateTarget seems to get set.. causing problems...
15026         // this just double checks..
15027         if(this.animateTarget && Roo.get(this.animateTarget)) {
15028            this.animHide(callback);
15029         }else{
15030             this.el.hide();
15031             this.hideEl(callback);
15032         }
15033         return this;
15034     },
15035
15036     // private
15037     hideEl : function(callback){
15038         this.proxy.hide();
15039         if(this.modal){
15040             this.mask.hide();
15041             Roo.get(document.body).removeClass("x-body-masked");
15042         }
15043         this.fireEvent("hide", this);
15044         if(typeof callback == "function"){
15045             callback();
15046         }
15047     },
15048
15049     // private
15050     hideAction : function(){
15051         this.setLeft("-10000px");
15052         this.setTop("-10000px");
15053         this.setStyle("visibility", "hidden");
15054     },
15055
15056     // private
15057     refreshSize : function(){
15058         this.size = this.el.getSize();
15059         this.xy = this.el.getXY();
15060         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15061     },
15062
15063     // private
15064     // z-index is managed by the DialogManager and may be overwritten at any time
15065     setZIndex : function(index){
15066         if(this.modal){
15067             this.mask.setStyle("z-index", index);
15068         }
15069         if(this.shim){
15070             this.shim.setStyle("z-index", ++index);
15071         }
15072         if(this.shadow){
15073             this.shadow.setZIndex(++index);
15074         }
15075         this.el.setStyle("z-index", ++index);
15076         if(this.proxy){
15077             this.proxy.setStyle("z-index", ++index);
15078         }
15079         if(this.resizer){
15080             this.resizer.proxy.setStyle("z-index", ++index);
15081         }
15082
15083         this.lastZIndex = index;
15084     },
15085
15086     /**
15087      * Returns the element for this dialog
15088      * @return {Roo.Element} The underlying dialog Element
15089      */
15090     getEl : function(){
15091         return this.el;
15092     }
15093 });
15094
15095 /**
15096  * @class Roo.DialogManager
15097  * Provides global access to BasicDialogs that have been created and
15098  * support for z-indexing (layering) multiple open dialogs.
15099  */
15100 Roo.DialogManager = function(){
15101     var list = {};
15102     var accessList = [];
15103     var front = null;
15104
15105     // private
15106     var sortDialogs = function(d1, d2){
15107         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15108     };
15109
15110     // private
15111     var orderDialogs = function(){
15112         accessList.sort(sortDialogs);
15113         var seed = Roo.DialogManager.zseed;
15114         for(var i = 0, len = accessList.length; i < len; i++){
15115             var dlg = accessList[i];
15116             if(dlg){
15117                 dlg.setZIndex(seed + (i*10));
15118             }
15119         }
15120     };
15121
15122     return {
15123         /**
15124          * The starting z-index for BasicDialogs (defaults to 9000)
15125          * @type Number The z-index value
15126          */
15127         zseed : 9000,
15128
15129         // private
15130         register : function(dlg){
15131             list[dlg.id] = dlg;
15132             accessList.push(dlg);
15133         },
15134
15135         // private
15136         unregister : function(dlg){
15137             delete list[dlg.id];
15138             var i=0;
15139             var len=0;
15140             if(!accessList.indexOf){
15141                 for(  i = 0, len = accessList.length; i < len; i++){
15142                     if(accessList[i] == dlg){
15143                         accessList.splice(i, 1);
15144                         return;
15145                     }
15146                 }
15147             }else{
15148                  i = accessList.indexOf(dlg);
15149                 if(i != -1){
15150                     accessList.splice(i, 1);
15151                 }
15152             }
15153         },
15154
15155         /**
15156          * Gets a registered dialog by id
15157          * @param {String/Object} id The id of the dialog or a dialog
15158          * @return {Roo.BasicDialog} this
15159          */
15160         get : function(id){
15161             return typeof id == "object" ? id : list[id];
15162         },
15163
15164         /**
15165          * Brings the specified dialog to the front
15166          * @param {String/Object} dlg The id of the dialog or a dialog
15167          * @return {Roo.BasicDialog} this
15168          */
15169         bringToFront : function(dlg){
15170             dlg = this.get(dlg);
15171             if(dlg != front){
15172                 front = dlg;
15173                 dlg._lastAccess = new Date().getTime();
15174                 orderDialogs();
15175             }
15176             return dlg;
15177         },
15178
15179         /**
15180          * Sends the specified dialog to the back
15181          * @param {String/Object} dlg The id of the dialog or a dialog
15182          * @return {Roo.BasicDialog} this
15183          */
15184         sendToBack : function(dlg){
15185             dlg = this.get(dlg);
15186             dlg._lastAccess = -(new Date().getTime());
15187             orderDialogs();
15188             return dlg;
15189         },
15190
15191         /**
15192          * Hides all dialogs
15193          */
15194         hideAll : function(){
15195             for(var id in list){
15196                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15197                     list[id].hide();
15198                 }
15199             }
15200         }
15201     };
15202 }();
15203
15204 /**
15205  * @class Roo.LayoutDialog
15206  * @extends Roo.BasicDialog
15207  * Dialog which provides adjustments for working with a layout in a Dialog.
15208  * Add your necessary layout config options to the dialog's config.<br>
15209  * Example usage (including a nested layout):
15210  * <pre><code>
15211 if(!dialog){
15212     dialog = new Roo.LayoutDialog("download-dlg", {
15213         modal: true,
15214         width:600,
15215         height:450,
15216         shadow:true,
15217         minWidth:500,
15218         minHeight:350,
15219         autoTabs:true,
15220         proxyDrag:true,
15221         // layout config merges with the dialog config
15222         center:{
15223             tabPosition: "top",
15224             alwaysShowTabs: true
15225         }
15226     });
15227     dialog.addKeyListener(27, dialog.hide, dialog);
15228     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15229     dialog.addButton("Build It!", this.getDownload, this);
15230
15231     // we can even add nested layouts
15232     var innerLayout = new Roo.BorderLayout("dl-inner", {
15233         east: {
15234             initialSize: 200,
15235             autoScroll:true,
15236             split:true
15237         },
15238         center: {
15239             autoScroll:true
15240         }
15241     });
15242     innerLayout.beginUpdate();
15243     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15244     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15245     innerLayout.endUpdate(true);
15246
15247     var layout = dialog.getLayout();
15248     layout.beginUpdate();
15249     layout.add("center", new Roo.ContentPanel("standard-panel",
15250                         {title: "Download the Source", fitToFrame:true}));
15251     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15252                {title: "Build your own roo.js"}));
15253     layout.getRegion("center").showPanel(sp);
15254     layout.endUpdate();
15255 }
15256 </code></pre>
15257     * @constructor
15258     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15259     * @param {Object} config configuration options
15260   */
15261 Roo.LayoutDialog = function(el, cfg){
15262     
15263     var config=  cfg;
15264     if (typeof(cfg) == 'undefined') {
15265         config = Roo.apply({}, el);
15266         // not sure why we use documentElement here.. - it should always be body.
15267         // IE7 borks horribly if we use documentElement.
15268         el = Roo.get( Roo.isIE ? (document.body || document.documentElement) : (document.documentElement || document.body) ).createChild();
15269         //config.autoCreate = true;
15270     }
15271     
15272     
15273     config.autoTabs = false;
15274     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15275     this.body.setStyle({overflow:"hidden", position:"relative"});
15276     this.layout = new Roo.BorderLayout(this.body.dom, config);
15277     this.layout.monitorWindowResize = false;
15278     this.el.addClass("x-dlg-auto-layout");
15279     // fix case when center region overwrites center function
15280     this.center = Roo.BasicDialog.prototype.center;
15281     this.on("show", this.layout.layout, this.layout, true);
15282     if (config.items) {
15283         var xitems = config.items;
15284         delete config.items;
15285         Roo.each(xitems, this.addxtype, this);
15286     }
15287     
15288     
15289 };
15290 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15291     /**
15292      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15293      * @deprecated
15294      */
15295     endUpdate : function(){
15296         this.layout.endUpdate();
15297     },
15298
15299     /**
15300      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15301      *  @deprecated
15302      */
15303     beginUpdate : function(){
15304         this.layout.beginUpdate();
15305     },
15306
15307     /**
15308      * Get the BorderLayout for this dialog
15309      * @return {Roo.BorderLayout}
15310      */
15311     getLayout : function(){
15312         return this.layout;
15313     },
15314
15315     showEl : function(){
15316         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15317         if(Roo.isIE7){
15318             this.layout.layout();
15319         }
15320     },
15321
15322     // private
15323     // Use the syncHeightBeforeShow config option to control this automatically
15324     syncBodyHeight : function(){
15325         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15326         if(this.layout){this.layout.layout();}
15327     },
15328     
15329       /**
15330      * Add an xtype element (actually adds to the layout.)
15331      * @return {Object} xdata xtype object data.
15332      */
15333     
15334     addxtype : function(c) {
15335         return this.layout.addxtype(c);
15336     }
15337 });/*
15338  * Based on:
15339  * Ext JS Library 1.1.1
15340  * Copyright(c) 2006-2007, Ext JS, LLC.
15341  *
15342  * Originally Released Under LGPL - original licence link has changed is not relivant.
15343  *
15344  * Fork - LGPL
15345  * <script type="text/javascript">
15346  */
15347  
15348 /**
15349  * @class Roo.MessageBox
15350  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15351  * Example usage:
15352  *<pre><code>
15353 // Basic alert:
15354 Roo.Msg.alert('Status', 'Changes saved successfully.');
15355
15356 // Prompt for user data:
15357 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15358     if (btn == 'ok'){
15359         // process text value...
15360     }
15361 });
15362
15363 // Show a dialog using config options:
15364 Roo.Msg.show({
15365    title:'Save Changes?',
15366    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15367    buttons: Roo.Msg.YESNOCANCEL,
15368    fn: processResult,
15369    animEl: 'elId'
15370 });
15371 </code></pre>
15372  * @singleton
15373  */
15374 Roo.MessageBox = function(){
15375     var dlg, opt, mask, waitTimer;
15376     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15377     var buttons, activeTextEl, bwidth;
15378
15379     // private
15380     var handleButton = function(button){
15381         dlg.hide();
15382         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15383     };
15384
15385     // private
15386     var handleHide = function(){
15387         if(opt && opt.cls){
15388             dlg.el.removeClass(opt.cls);
15389         }
15390         if(waitTimer){
15391             Roo.TaskMgr.stop(waitTimer);
15392             waitTimer = null;
15393         }
15394     };
15395
15396     // private
15397     var updateButtons = function(b){
15398         var width = 0;
15399         if(!b){
15400             buttons["ok"].hide();
15401             buttons["cancel"].hide();
15402             buttons["yes"].hide();
15403             buttons["no"].hide();
15404             dlg.footer.dom.style.display = 'none';
15405             return width;
15406         }
15407         dlg.footer.dom.style.display = '';
15408         for(var k in buttons){
15409             if(typeof buttons[k] != "function"){
15410                 if(b[k]){
15411                     buttons[k].show();
15412                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15413                     width += buttons[k].el.getWidth()+15;
15414                 }else{
15415                     buttons[k].hide();
15416                 }
15417             }
15418         }
15419         return width;
15420     };
15421
15422     // private
15423     var handleEsc = function(d, k, e){
15424         if(opt && opt.closable !== false){
15425             dlg.hide();
15426         }
15427         if(e){
15428             e.stopEvent();
15429         }
15430     };
15431
15432     return {
15433         /**
15434          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15435          * @return {Roo.BasicDialog} The BasicDialog element
15436          */
15437         getDialog : function(){
15438            if(!dlg){
15439                 dlg = new Roo.BasicDialog("x-msg-box", {
15440                     autoCreate : true,
15441                     shadow: true,
15442                     draggable: true,
15443                     resizable:false,
15444                     constraintoviewport:false,
15445                     fixedcenter:true,
15446                     collapsible : false,
15447                     shim:true,
15448                     modal: true,
15449                     width:400, height:100,
15450                     buttonAlign:"center",
15451                     closeClick : function(){
15452                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15453                             handleButton("no");
15454                         }else{
15455                             handleButton("cancel");
15456                         }
15457                     }
15458                 });
15459                 dlg.on("hide", handleHide);
15460                 mask = dlg.mask;
15461                 dlg.addKeyListener(27, handleEsc);
15462                 buttons = {};
15463                 var bt = this.buttonText;
15464                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15465                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15466                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15467                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15468                 bodyEl = dlg.body.createChild({
15469
15470                     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>'
15471                 });
15472                 msgEl = bodyEl.dom.firstChild;
15473                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15474                 textboxEl.enableDisplayMode();
15475                 textboxEl.addKeyListener([10,13], function(){
15476                     if(dlg.isVisible() && opt && opt.buttons){
15477                         if(opt.buttons.ok){
15478                             handleButton("ok");
15479                         }else if(opt.buttons.yes){
15480                             handleButton("yes");
15481                         }
15482                     }
15483                 });
15484                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15485                 textareaEl.enableDisplayMode();
15486                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15487                 progressEl.enableDisplayMode();
15488                 var pf = progressEl.dom.firstChild;
15489                 if (pf) {
15490                     pp = Roo.get(pf.firstChild);
15491                     pp.setHeight(pf.offsetHeight);
15492                 }
15493                 
15494             }
15495             return dlg;
15496         },
15497
15498         /**
15499          * Updates the message box body text
15500          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15501          * the XHTML-compliant non-breaking space character '&amp;#160;')
15502          * @return {Roo.MessageBox} This message box
15503          */
15504         updateText : function(text){
15505             if(!dlg.isVisible() && !opt.width){
15506                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15507             }
15508             msgEl.innerHTML = text || '&#160;';
15509             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
15510                         Math.max(opt.minWidth || this.minWidth, bwidth));
15511             if(opt.prompt){
15512                 activeTextEl.setWidth(w);
15513             }
15514             if(dlg.isVisible()){
15515                 dlg.fixedcenter = false;
15516             }
15517             dlg.setContentSize(w, bodyEl.getHeight());
15518             if(dlg.isVisible()){
15519                 dlg.fixedcenter = true;
15520             }
15521             return this;
15522         },
15523
15524         /**
15525          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15526          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15527          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15528          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15529          * @return {Roo.MessageBox} This message box
15530          */
15531         updateProgress : function(value, text){
15532             if(text){
15533                 this.updateText(text);
15534             }
15535             if (pp) { // weird bug on my firefox - for some reason this is not defined
15536                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15537             }
15538             return this;
15539         },        
15540
15541         /**
15542          * Returns true if the message box is currently displayed
15543          * @return {Boolean} True if the message box is visible, else false
15544          */
15545         isVisible : function(){
15546             return dlg && dlg.isVisible();  
15547         },
15548
15549         /**
15550          * Hides the message box if it is displayed
15551          */
15552         hide : function(){
15553             if(this.isVisible()){
15554                 dlg.hide();
15555             }  
15556         },
15557
15558         /**
15559          * Displays a new message box, or reinitializes an existing message box, based on the config options
15560          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15561          * The following config object properties are supported:
15562          * <pre>
15563 Property    Type             Description
15564 ----------  ---------------  ------------------------------------------------------------------------------------
15565 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15566                                    closes (defaults to undefined)
15567 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15568                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15569 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15570                                    progress and wait dialogs will ignore this property and always hide the
15571                                    close button as they can only be closed programmatically.
15572 cls               String           A custom CSS class to apply to the message box element
15573 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15574                                    displayed (defaults to 75)
15575 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15576                                    function will be btn (the name of the button that was clicked, if applicable,
15577                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15578                                    Progress and wait dialogs will ignore this option since they do not respond to
15579                                    user actions and can only be closed programmatically, so any required function
15580                                    should be called by the same code after it closes the dialog.
15581 icon              String           A CSS class that provides a background image to be used as an icon for
15582                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15583 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15584 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15585 modal             Boolean          False to allow user interaction with the page while the message box is
15586                                    displayed (defaults to true)
15587 msg               String           A string that will replace the existing message box body text (defaults
15588                                    to the XHTML-compliant non-breaking space character '&#160;')
15589 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15590 progress          Boolean          True to display a progress bar (defaults to false)
15591 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15592 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15593 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15594 title             String           The title text
15595 value             String           The string value to set into the active textbox element if displayed
15596 wait              Boolean          True to display a progress bar (defaults to false)
15597 width             Number           The width of the dialog in pixels
15598 </pre>
15599          *
15600          * Example usage:
15601          * <pre><code>
15602 Roo.Msg.show({
15603    title: 'Address',
15604    msg: 'Please enter your address:',
15605    width: 300,
15606    buttons: Roo.MessageBox.OKCANCEL,
15607    multiline: true,
15608    fn: saveAddress,
15609    animEl: 'addAddressBtn'
15610 });
15611 </code></pre>
15612          * @param {Object} config Configuration options
15613          * @return {Roo.MessageBox} This message box
15614          */
15615         show : function(options){
15616             if(this.isVisible()){
15617                 this.hide();
15618             }
15619             var d = this.getDialog();
15620             opt = options;
15621             d.setTitle(opt.title || "&#160;");
15622             d.close.setDisplayed(opt.closable !== false);
15623             activeTextEl = textboxEl;
15624             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15625             if(opt.prompt){
15626                 if(opt.multiline){
15627                     textboxEl.hide();
15628                     textareaEl.show();
15629                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15630                         opt.multiline : this.defaultTextHeight);
15631                     activeTextEl = textareaEl;
15632                 }else{
15633                     textboxEl.show();
15634                     textareaEl.hide();
15635                 }
15636             }else{
15637                 textboxEl.hide();
15638                 textareaEl.hide();
15639             }
15640             progressEl.setDisplayed(opt.progress === true);
15641             this.updateProgress(0);
15642             activeTextEl.dom.value = opt.value || "";
15643             if(opt.prompt){
15644                 dlg.setDefaultButton(activeTextEl);
15645             }else{
15646                 var bs = opt.buttons;
15647                 var db = null;
15648                 if(bs && bs.ok){
15649                     db = buttons["ok"];
15650                 }else if(bs && bs.yes){
15651                     db = buttons["yes"];
15652                 }
15653                 dlg.setDefaultButton(db);
15654             }
15655             bwidth = updateButtons(opt.buttons);
15656             this.updateText(opt.msg);
15657             if(opt.cls){
15658                 d.el.addClass(opt.cls);
15659             }
15660             d.proxyDrag = opt.proxyDrag === true;
15661             d.modal = opt.modal !== false;
15662             d.mask = opt.modal !== false ? mask : false;
15663             if(!d.isVisible()){
15664                 // force it to the end of the z-index stack so it gets a cursor in FF
15665                 document.body.appendChild(dlg.el.dom);
15666                 d.animateTarget = null;
15667                 d.show(options.animEl);
15668             }
15669             return this;
15670         },
15671
15672         /**
15673          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15674          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15675          * and closing the message box when the process is complete.
15676          * @param {String} title The title bar text
15677          * @param {String} msg The message box body text
15678          * @return {Roo.MessageBox} This message box
15679          */
15680         progress : function(title, msg){
15681             this.show({
15682                 title : title,
15683                 msg : msg,
15684                 buttons: false,
15685                 progress:true,
15686                 closable:false,
15687                 minWidth: this.minProgressWidth,
15688                 modal : true
15689             });
15690             return this;
15691         },
15692
15693         /**
15694          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15695          * If a callback function is passed it will be called after the user clicks the button, and the
15696          * id of the button that was clicked will be passed as the only parameter to the callback
15697          * (could also be the top-right close button).
15698          * @param {String} title The title bar text
15699          * @param {String} msg The message box body text
15700          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15701          * @param {Object} scope (optional) The scope of the callback function
15702          * @return {Roo.MessageBox} This message box
15703          */
15704         alert : function(title, msg, fn, scope){
15705             this.show({
15706                 title : title,
15707                 msg : msg,
15708                 buttons: this.OK,
15709                 fn: fn,
15710                 scope : scope,
15711                 modal : true
15712             });
15713             return this;
15714         },
15715
15716         /**
15717          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15718          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15719          * You are responsible for closing the message box when the process is complete.
15720          * @param {String} msg The message box body text
15721          * @param {String} title (optional) The title bar text
15722          * @return {Roo.MessageBox} This message box
15723          */
15724         wait : function(msg, title){
15725             this.show({
15726                 title : title,
15727                 msg : msg,
15728                 buttons: false,
15729                 closable:false,
15730                 progress:true,
15731                 modal:true,
15732                 width:300,
15733                 wait:true
15734             });
15735             waitTimer = Roo.TaskMgr.start({
15736                 run: function(i){
15737                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15738                 },
15739                 interval: 1000
15740             });
15741             return this;
15742         },
15743
15744         /**
15745          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15746          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15747          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15748          * @param {String} title The title bar text
15749          * @param {String} msg The message box body text
15750          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15751          * @param {Object} scope (optional) The scope of the callback function
15752          * @return {Roo.MessageBox} This message box
15753          */
15754         confirm : function(title, msg, fn, scope){
15755             this.show({
15756                 title : title,
15757                 msg : msg,
15758                 buttons: this.YESNO,
15759                 fn: fn,
15760                 scope : scope,
15761                 modal : true
15762             });
15763             return this;
15764         },
15765
15766         /**
15767          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15768          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15769          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15770          * (could also be the top-right close button) and the text that was entered will be passed as the two
15771          * parameters to the callback.
15772          * @param {String} title The title bar text
15773          * @param {String} msg The message box body text
15774          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15775          * @param {Object} scope (optional) The scope of the callback function
15776          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15777          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15778          * @return {Roo.MessageBox} This message box
15779          */
15780         prompt : function(title, msg, fn, scope, multiline){
15781             this.show({
15782                 title : title,
15783                 msg : msg,
15784                 buttons: this.OKCANCEL,
15785                 fn: fn,
15786                 minWidth:250,
15787                 scope : scope,
15788                 prompt:true,
15789                 multiline: multiline,
15790                 modal : true
15791             });
15792             return this;
15793         },
15794
15795         /**
15796          * Button config that displays a single OK button
15797          * @type Object
15798          */
15799         OK : {ok:true},
15800         /**
15801          * Button config that displays Yes and No buttons
15802          * @type Object
15803          */
15804         YESNO : {yes:true, no:true},
15805         /**
15806          * Button config that displays OK and Cancel buttons
15807          * @type Object
15808          */
15809         OKCANCEL : {ok:true, cancel:true},
15810         /**
15811          * Button config that displays Yes, No and Cancel buttons
15812          * @type Object
15813          */
15814         YESNOCANCEL : {yes:true, no:true, cancel:true},
15815
15816         /**
15817          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15818          * @type Number
15819          */
15820         defaultTextHeight : 75,
15821         /**
15822          * The maximum width in pixels of the message box (defaults to 600)
15823          * @type Number
15824          */
15825         maxWidth : 600,
15826         /**
15827          * The minimum width in pixels of the message box (defaults to 100)
15828          * @type Number
15829          */
15830         minWidth : 100,
15831         /**
15832          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
15833          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15834          * @type Number
15835          */
15836         minProgressWidth : 250,
15837         /**
15838          * An object containing the default button text strings that can be overriden for localized language support.
15839          * Supported properties are: ok, cancel, yes and no.
15840          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
15841          * @type Object
15842          */
15843         buttonText : {
15844             ok : "OK",
15845             cancel : "Cancel",
15846             yes : "Yes",
15847             no : "No"
15848         }
15849     };
15850 }();
15851
15852 /**
15853  * Shorthand for {@link Roo.MessageBox}
15854  */
15855 Roo.Msg = Roo.MessageBox;/*
15856  * Based on:
15857  * Ext JS Library 1.1.1
15858  * Copyright(c) 2006-2007, Ext JS, LLC.
15859  *
15860  * Originally Released Under LGPL - original licence link has changed is not relivant.
15861  *
15862  * Fork - LGPL
15863  * <script type="text/javascript">
15864  */
15865 /**
15866  * @class Roo.QuickTips
15867  * Provides attractive and customizable tooltips for any element.
15868  * @singleton
15869  */
15870 Roo.QuickTips = function(){
15871     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
15872     var ce, bd, xy, dd;
15873     var visible = false, disabled = true, inited = false;
15874     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
15875     
15876     var onOver = function(e){
15877         if(disabled){
15878             return;
15879         }
15880         var t = e.getTarget();
15881         if(!t || t.nodeType !== 1 || t == document || t == document.body){
15882             return;
15883         }
15884         if(ce && t == ce.el){
15885             clearTimeout(hideProc);
15886             return;
15887         }
15888         if(t && tagEls[t.id]){
15889             tagEls[t.id].el = t;
15890             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
15891             return;
15892         }
15893         var ttp, et = Roo.fly(t);
15894         var ns = cfg.namespace;
15895         if(tm.interceptTitles && t.title){
15896             ttp = t.title;
15897             t.qtip = ttp;
15898             t.removeAttribute("title");
15899             e.preventDefault();
15900         }else{
15901             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
15902         }
15903         if(ttp){
15904             showProc = show.defer(tm.showDelay, tm, [{
15905                 el: t, 
15906                 text: ttp, 
15907                 width: et.getAttributeNS(ns, cfg.width),
15908                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
15909                 title: et.getAttributeNS(ns, cfg.title),
15910                     cls: et.getAttributeNS(ns, cfg.cls)
15911             }]);
15912         }
15913     };
15914     
15915     var onOut = function(e){
15916         clearTimeout(showProc);
15917         var t = e.getTarget();
15918         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
15919             hideProc = setTimeout(hide, tm.hideDelay);
15920         }
15921     };
15922     
15923     var onMove = function(e){
15924         if(disabled){
15925             return;
15926         }
15927         xy = e.getXY();
15928         xy[1] += 18;
15929         if(tm.trackMouse && ce){
15930             el.setXY(xy);
15931         }
15932     };
15933     
15934     var onDown = function(e){
15935         clearTimeout(showProc);
15936         clearTimeout(hideProc);
15937         if(!e.within(el)){
15938             if(tm.hideOnClick){
15939                 hide();
15940                 tm.disable();
15941                 tm.enable.defer(100, tm);
15942             }
15943         }
15944     };
15945     
15946     var getPad = function(){
15947         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
15948     };
15949
15950     var show = function(o){
15951         if(disabled){
15952             return;
15953         }
15954         clearTimeout(dismissProc);
15955         ce = o;
15956         if(removeCls){ // in case manually hidden
15957             el.removeClass(removeCls);
15958             removeCls = null;
15959         }
15960         if(ce.cls){
15961             el.addClass(ce.cls);
15962             removeCls = ce.cls;
15963         }
15964         if(ce.title){
15965             tipTitle.update(ce.title);
15966             tipTitle.show();
15967         }else{
15968             tipTitle.update('');
15969             tipTitle.hide();
15970         }
15971         el.dom.style.width  = tm.maxWidth+'px';
15972         //tipBody.dom.style.width = '';
15973         tipBodyText.update(o.text);
15974         var p = getPad(), w = ce.width;
15975         if(!w){
15976             var td = tipBodyText.dom;
15977             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
15978             if(aw > tm.maxWidth){
15979                 w = tm.maxWidth;
15980             }else if(aw < tm.minWidth){
15981                 w = tm.minWidth;
15982             }else{
15983                 w = aw;
15984             }
15985         }
15986         //tipBody.setWidth(w);
15987         el.setWidth(parseInt(w, 10) + p);
15988         if(ce.autoHide === false){
15989             close.setDisplayed(true);
15990             if(dd){
15991                 dd.unlock();
15992             }
15993         }else{
15994             close.setDisplayed(false);
15995             if(dd){
15996                 dd.lock();
15997             }
15998         }
15999         if(xy){
16000             el.avoidY = xy[1]-18;
16001             el.setXY(xy);
16002         }
16003         if(tm.animate){
16004             el.setOpacity(.1);
16005             el.setStyle("visibility", "visible");
16006             el.fadeIn({callback: afterShow});
16007         }else{
16008             afterShow();
16009         }
16010     };
16011     
16012     var afterShow = function(){
16013         if(ce){
16014             el.show();
16015             esc.enable();
16016             if(tm.autoDismiss && ce.autoHide !== false){
16017                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16018             }
16019         }
16020     };
16021     
16022     var hide = function(noanim){
16023         clearTimeout(dismissProc);
16024         clearTimeout(hideProc);
16025         ce = null;
16026         if(el.isVisible()){
16027             esc.disable();
16028             if(noanim !== true && tm.animate){
16029                 el.fadeOut({callback: afterHide});
16030             }else{
16031                 afterHide();
16032             } 
16033         }
16034     };
16035     
16036     var afterHide = function(){
16037         el.hide();
16038         if(removeCls){
16039             el.removeClass(removeCls);
16040             removeCls = null;
16041         }
16042     };
16043     
16044     return {
16045         /**
16046         * @cfg {Number} minWidth
16047         * The minimum width of the quick tip (defaults to 40)
16048         */
16049        minWidth : 40,
16050         /**
16051         * @cfg {Number} maxWidth
16052         * The maximum width of the quick tip (defaults to 300)
16053         */
16054        maxWidth : 300,
16055         /**
16056         * @cfg {Boolean} interceptTitles
16057         * True to automatically use the element's DOM title value if available (defaults to false)
16058         */
16059        interceptTitles : false,
16060         /**
16061         * @cfg {Boolean} trackMouse
16062         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16063         */
16064        trackMouse : false,
16065         /**
16066         * @cfg {Boolean} hideOnClick
16067         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16068         */
16069        hideOnClick : true,
16070         /**
16071         * @cfg {Number} showDelay
16072         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16073         */
16074        showDelay : 500,
16075         /**
16076         * @cfg {Number} hideDelay
16077         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16078         */
16079        hideDelay : 200,
16080         /**
16081         * @cfg {Boolean} autoHide
16082         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16083         * Used in conjunction with hideDelay.
16084         */
16085        autoHide : true,
16086         /**
16087         * @cfg {Boolean}
16088         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16089         * (defaults to true).  Used in conjunction with autoDismissDelay.
16090         */
16091        autoDismiss : true,
16092         /**
16093         * @cfg {Number}
16094         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16095         */
16096        autoDismissDelay : 5000,
16097        /**
16098         * @cfg {Boolean} animate
16099         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16100         */
16101        animate : false,
16102
16103        /**
16104         * @cfg {String} title
16105         * Title text to display (defaults to '').  This can be any valid HTML markup.
16106         */
16107         title: '',
16108        /**
16109         * @cfg {String} text
16110         * Body text to display (defaults to '').  This can be any valid HTML markup.
16111         */
16112         text : '',
16113        /**
16114         * @cfg {String} cls
16115         * A CSS class to apply to the base quick tip element (defaults to '').
16116         */
16117         cls : '',
16118        /**
16119         * @cfg {Number} width
16120         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16121         * minWidth or maxWidth.
16122         */
16123         width : null,
16124
16125     /**
16126      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16127      * or display QuickTips in a page.
16128      */
16129        init : function(){
16130           tm = Roo.QuickTips;
16131           cfg = tm.tagConfig;
16132           if(!inited){
16133               if(!Roo.isReady){ // allow calling of init() before onReady
16134                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16135                   return;
16136               }
16137               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16138               el.fxDefaults = {stopFx: true};
16139               // maximum custom styling
16140               //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>');
16141               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>');              
16142               tipTitle = el.child('h3');
16143               tipTitle.enableDisplayMode("block");
16144               tipBody = el.child('div.x-tip-bd');
16145               tipBodyText = el.child('div.x-tip-bd-inner');
16146               //bdLeft = el.child('div.x-tip-bd-left');
16147               //bdRight = el.child('div.x-tip-bd-right');
16148               close = el.child('div.x-tip-close');
16149               close.enableDisplayMode("block");
16150               close.on("click", hide);
16151               var d = Roo.get(document);
16152               d.on("mousedown", onDown);
16153               d.on("mouseover", onOver);
16154               d.on("mouseout", onOut);
16155               d.on("mousemove", onMove);
16156               esc = d.addKeyListener(27, hide);
16157               esc.disable();
16158               if(Roo.dd.DD){
16159                   dd = el.initDD("default", null, {
16160                       onDrag : function(){
16161                           el.sync();  
16162                       }
16163                   });
16164                   dd.setHandleElId(tipTitle.id);
16165                   dd.lock();
16166               }
16167               inited = true;
16168           }
16169           this.enable(); 
16170        },
16171
16172     /**
16173      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16174      * are supported:
16175      * <pre>
16176 Property    Type                   Description
16177 ----------  ---------------------  ------------------------------------------------------------------------
16178 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16179      * </ul>
16180      * @param {Object} config The config object
16181      */
16182        register : function(config){
16183            var cs = config instanceof Array ? config : arguments;
16184            for(var i = 0, len = cs.length; i < len; i++) {
16185                var c = cs[i];
16186                var target = c.target;
16187                if(target){
16188                    if(target instanceof Array){
16189                        for(var j = 0, jlen = target.length; j < jlen; j++){
16190                            tagEls[target[j]] = c;
16191                        }
16192                    }else{
16193                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16194                    }
16195                }
16196            }
16197        },
16198
16199     /**
16200      * Removes this quick tip from its element and destroys it.
16201      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16202      */
16203        unregister : function(el){
16204            delete tagEls[Roo.id(el)];
16205        },
16206
16207     /**
16208      * Enable this quick tip.
16209      */
16210        enable : function(){
16211            if(inited && disabled){
16212                locks.pop();
16213                if(locks.length < 1){
16214                    disabled = false;
16215                }
16216            }
16217        },
16218
16219     /**
16220      * Disable this quick tip.
16221      */
16222        disable : function(){
16223           disabled = true;
16224           clearTimeout(showProc);
16225           clearTimeout(hideProc);
16226           clearTimeout(dismissProc);
16227           if(ce){
16228               hide(true);
16229           }
16230           locks.push(1);
16231        },
16232
16233     /**
16234      * Returns true if the quick tip is enabled, else false.
16235      */
16236        isEnabled : function(){
16237             return !disabled;
16238        },
16239
16240         // private
16241        tagConfig : {
16242            namespace : "ext",
16243            attribute : "qtip",
16244            width : "width",
16245            target : "target",
16246            title : "qtitle",
16247            hide : "hide",
16248            cls : "qclass"
16249        }
16250    };
16251 }();
16252
16253 // backwards compat
16254 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16255  * Based on:
16256  * Ext JS Library 1.1.1
16257  * Copyright(c) 2006-2007, Ext JS, LLC.
16258  *
16259  * Originally Released Under LGPL - original licence link has changed is not relivant.
16260  *
16261  * Fork - LGPL
16262  * <script type="text/javascript">
16263  */
16264  
16265
16266 /**
16267  * @class Roo.tree.TreePanel
16268  * @extends Roo.data.Tree
16269
16270  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16271  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16272  * @cfg {Boolean} enableDD true to enable drag and drop
16273  * @cfg {Boolean} enableDrag true to enable just drag
16274  * @cfg {Boolean} enableDrop true to enable just drop
16275  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16276  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16277  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16278  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16279  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16280  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16281  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16282  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16283  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16284  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16285  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16286  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16287  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16288  * @cfg {Function} renderer 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>
16289  * @cfg {Function} rendererTip 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>
16290  * 
16291  * @constructor
16292  * @param {String/HTMLElement/Element} el The container element
16293  * @param {Object} config
16294  */
16295 Roo.tree.TreePanel = function(el, config){
16296     var root = false;
16297     var loader = false;
16298     if (config.root) {
16299         root = config.root;
16300         delete config.root;
16301     }
16302     if (config.loader) {
16303         loader = config.loader;
16304         delete config.loader;
16305     }
16306     
16307     Roo.apply(this, config);
16308     Roo.tree.TreePanel.superclass.constructor.call(this);
16309     this.el = Roo.get(el);
16310     this.el.addClass('x-tree');
16311     //console.log(root);
16312     if (root) {
16313         this.setRootNode( Roo.factory(root, Roo.tree));
16314     }
16315     if (loader) {
16316         this.loader = Roo.factory(loader, Roo.tree);
16317     }
16318    /**
16319     * Read-only. The id of the container element becomes this TreePanel's id.
16320     */
16321    this.id = this.el.id;
16322    this.addEvents({
16323         /**
16324         * @event beforeload
16325         * Fires before a node is loaded, return false to cancel
16326         * @param {Node} node The node being loaded
16327         */
16328         "beforeload" : true,
16329         /**
16330         * @event load
16331         * Fires when a node is loaded
16332         * @param {Node} node The node that was loaded
16333         */
16334         "load" : true,
16335         /**
16336         * @event textchange
16337         * Fires when the text for a node is changed
16338         * @param {Node} node The node
16339         * @param {String} text The new text
16340         * @param {String} oldText The old text
16341         */
16342         "textchange" : true,
16343         /**
16344         * @event beforeexpand
16345         * Fires before a node is expanded, return false to cancel.
16346         * @param {Node} node The node
16347         * @param {Boolean} deep
16348         * @param {Boolean} anim
16349         */
16350         "beforeexpand" : true,
16351         /**
16352         * @event beforecollapse
16353         * Fires before a node is collapsed, return false to cancel.
16354         * @param {Node} node The node
16355         * @param {Boolean} deep
16356         * @param {Boolean} anim
16357         */
16358         "beforecollapse" : true,
16359         /**
16360         * @event expand
16361         * Fires when a node is expanded
16362         * @param {Node} node The node
16363         */
16364         "expand" : true,
16365         /**
16366         * @event disabledchange
16367         * Fires when the disabled status of a node changes
16368         * @param {Node} node The node
16369         * @param {Boolean} disabled
16370         */
16371         "disabledchange" : true,
16372         /**
16373         * @event collapse
16374         * Fires when a node is collapsed
16375         * @param {Node} node The node
16376         */
16377         "collapse" : true,
16378         /**
16379         * @event beforeclick
16380         * Fires before click processing on a node. Return false to cancel the default action.
16381         * @param {Node} node The node
16382         * @param {Roo.EventObject} e The event object
16383         */
16384         "beforeclick":true,
16385         /**
16386         * @event checkchange
16387         * Fires when a node with a checkbox's checked property changes
16388         * @param {Node} this This node
16389         * @param {Boolean} checked
16390         */
16391         "checkchange":true,
16392         /**
16393         * @event click
16394         * Fires when a node is clicked
16395         * @param {Node} node The node
16396         * @param {Roo.EventObject} e The event object
16397         */
16398         "click":true,
16399         /**
16400         * @event dblclick
16401         * Fires when a node is double clicked
16402         * @param {Node} node The node
16403         * @param {Roo.EventObject} e The event object
16404         */
16405         "dblclick":true,
16406         /**
16407         * @event contextmenu
16408         * Fires when a node is right clicked
16409         * @param {Node} node The node
16410         * @param {Roo.EventObject} e The event object
16411         */
16412         "contextmenu":true,
16413         /**
16414         * @event beforechildrenrendered
16415         * Fires right before the child nodes for a node are rendered
16416         * @param {Node} node The node
16417         */
16418         "beforechildrenrendered":true,
16419        /**
16420              * @event startdrag
16421              * Fires when a node starts being dragged
16422              * @param {Roo.tree.TreePanel} this
16423              * @param {Roo.tree.TreeNode} node
16424              * @param {event} e The raw browser event
16425              */ 
16426             "startdrag" : true,
16427             /**
16428              * @event enddrag
16429              * Fires when a drag operation is complete
16430              * @param {Roo.tree.TreePanel} this
16431              * @param {Roo.tree.TreeNode} node
16432              * @param {event} e The raw browser event
16433              */
16434             "enddrag" : true,
16435             /**
16436              * @event dragdrop
16437              * Fires when a dragged node is dropped on a valid DD target
16438              * @param {Roo.tree.TreePanel} this
16439              * @param {Roo.tree.TreeNode} node
16440              * @param {DD} dd The dd it was dropped on
16441              * @param {event} e The raw browser event
16442              */
16443             "dragdrop" : true,
16444             /**
16445              * @event beforenodedrop
16446              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16447              * passed to handlers has the following properties:<br />
16448              * <ul style="padding:5px;padding-left:16px;">
16449              * <li>tree - The TreePanel</li>
16450              * <li>target - The node being targeted for the drop</li>
16451              * <li>data - The drag data from the drag source</li>
16452              * <li>point - The point of the drop - append, above or below</li>
16453              * <li>source - The drag source</li>
16454              * <li>rawEvent - Raw mouse event</li>
16455              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16456              * to be inserted by setting them on this object.</li>
16457              * <li>cancel - Set this to true to cancel the drop.</li>
16458              * </ul>
16459              * @param {Object} dropEvent
16460              */
16461             "beforenodedrop" : true,
16462             /**
16463              * @event nodedrop
16464              * Fires after a DD object is dropped on a node in this tree. The dropEvent
16465              * passed to handlers has the following properties:<br />
16466              * <ul style="padding:5px;padding-left:16px;">
16467              * <li>tree - The TreePanel</li>
16468              * <li>target - The node being targeted for the drop</li>
16469              * <li>data - The drag data from the drag source</li>
16470              * <li>point - The point of the drop - append, above or below</li>
16471              * <li>source - The drag source</li>
16472              * <li>rawEvent - Raw mouse event</li>
16473              * <li>dropNode - Dropped node(s).</li>
16474              * </ul>
16475              * @param {Object} dropEvent
16476              */
16477             "nodedrop" : true,
16478              /**
16479              * @event nodedragover
16480              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16481              * passed to handlers has the following properties:<br />
16482              * <ul style="padding:5px;padding-left:16px;">
16483              * <li>tree - The TreePanel</li>
16484              * <li>target - The node being targeted for the drop</li>
16485              * <li>data - The drag data from the drag source</li>
16486              * <li>point - The point of the drop - append, above or below</li>
16487              * <li>source - The drag source</li>
16488              * <li>rawEvent - Raw mouse event</li>
16489              * <li>dropNode - Drop node(s) provided by the source.</li>
16490              * <li>cancel - Set this to true to signal drop not allowed.</li>
16491              * </ul>
16492              * @param {Object} dragOverEvent
16493              */
16494             "nodedragover" : true
16495         
16496    });
16497    if(this.singleExpand){
16498        this.on("beforeexpand", this.restrictExpand, this);
16499    }
16500 };
16501 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16502     rootVisible : true,
16503     animate: Roo.enableFx,
16504     lines : true,
16505     enableDD : false,
16506     hlDrop : Roo.enableFx,
16507   
16508     renderer: false,
16509     
16510     rendererTip: false,
16511     // private
16512     restrictExpand : function(node){
16513         var p = node.parentNode;
16514         if(p){
16515             if(p.expandedChild && p.expandedChild.parentNode == p){
16516                 p.expandedChild.collapse();
16517             }
16518             p.expandedChild = node;
16519         }
16520     },
16521
16522     // private override
16523     setRootNode : function(node){
16524         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16525         if(!this.rootVisible){
16526             node.ui = new Roo.tree.RootTreeNodeUI(node);
16527         }
16528         return node;
16529     },
16530
16531     /**
16532      * Returns the container element for this TreePanel
16533      */
16534     getEl : function(){
16535         return this.el;
16536     },
16537
16538     /**
16539      * Returns the default TreeLoader for this TreePanel
16540      */
16541     getLoader : function(){
16542         return this.loader;
16543     },
16544
16545     /**
16546      * Expand all nodes
16547      */
16548     expandAll : function(){
16549         this.root.expand(true);
16550     },
16551
16552     /**
16553      * Collapse all nodes
16554      */
16555     collapseAll : function(){
16556         this.root.collapse(true);
16557     },
16558
16559     /**
16560      * Returns the selection model used by this TreePanel
16561      */
16562     getSelectionModel : function(){
16563         if(!this.selModel){
16564             this.selModel = new Roo.tree.DefaultSelectionModel();
16565         }
16566         return this.selModel;
16567     },
16568
16569     /**
16570      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16571      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16572      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16573      * @return {Array}
16574      */
16575     getChecked : function(a, startNode){
16576         startNode = startNode || this.root;
16577         var r = [];
16578         var f = function(){
16579             if(this.attributes.checked){
16580                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16581             }
16582         }
16583         startNode.cascade(f);
16584         return r;
16585     },
16586
16587     /**
16588      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16589      * @param {String} path
16590      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16591      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16592      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16593      */
16594     expandPath : function(path, attr, callback){
16595         attr = attr || "id";
16596         var keys = path.split(this.pathSeparator);
16597         var curNode = this.root;
16598         if(curNode.attributes[attr] != keys[1]){ // invalid root
16599             if(callback){
16600                 callback(false, null);
16601             }
16602             return;
16603         }
16604         var index = 1;
16605         var f = function(){
16606             if(++index == keys.length){
16607                 if(callback){
16608                     callback(true, curNode);
16609                 }
16610                 return;
16611             }
16612             var c = curNode.findChild(attr, keys[index]);
16613             if(!c){
16614                 if(callback){
16615                     callback(false, curNode);
16616                 }
16617                 return;
16618             }
16619             curNode = c;
16620             c.expand(false, false, f);
16621         };
16622         curNode.expand(false, false, f);
16623     },
16624
16625     /**
16626      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16627      * @param {String} path
16628      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16629      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16630      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16631      */
16632     selectPath : function(path, attr, callback){
16633         attr = attr || "id";
16634         var keys = path.split(this.pathSeparator);
16635         var v = keys.pop();
16636         if(keys.length > 0){
16637             var f = function(success, node){
16638                 if(success && node){
16639                     var n = node.findChild(attr, v);
16640                     if(n){
16641                         n.select();
16642                         if(callback){
16643                             callback(true, n);
16644                         }
16645                     }else if(callback){
16646                         callback(false, n);
16647                     }
16648                 }else{
16649                     if(callback){
16650                         callback(false, n);
16651                     }
16652                 }
16653             };
16654             this.expandPath(keys.join(this.pathSeparator), attr, f);
16655         }else{
16656             this.root.select();
16657             if(callback){
16658                 callback(true, this.root);
16659             }
16660         }
16661     },
16662
16663     getTreeEl : function(){
16664         return this.el;
16665     },
16666
16667     /**
16668      * Trigger rendering of this TreePanel
16669      */
16670     render : function(){
16671         if (this.innerCt) {
16672             return this; // stop it rendering more than once!!
16673         }
16674         
16675         this.innerCt = this.el.createChild({tag:"ul",
16676                cls:"x-tree-root-ct " +
16677                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16678
16679         if(this.containerScroll){
16680             Roo.dd.ScrollManager.register(this.el);
16681         }
16682         if((this.enableDD || this.enableDrop) && !this.dropZone){
16683            /**
16684             * The dropZone used by this tree if drop is enabled
16685             * @type Roo.tree.TreeDropZone
16686             */
16687              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16688                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16689            });
16690         }
16691         if((this.enableDD || this.enableDrag) && !this.dragZone){
16692            /**
16693             * The dragZone used by this tree if drag is enabled
16694             * @type Roo.tree.TreeDragZone
16695             */
16696             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16697                ddGroup: this.ddGroup || "TreeDD",
16698                scroll: this.ddScroll
16699            });
16700         }
16701         this.getSelectionModel().init(this);
16702         if (!this.root) {
16703             console.log("ROOT not set in tree");
16704             return;
16705         }
16706         this.root.render();
16707         if(!this.rootVisible){
16708             this.root.renderChildren();
16709         }
16710         return this;
16711     }
16712 });/*
16713  * Based on:
16714  * Ext JS Library 1.1.1
16715  * Copyright(c) 2006-2007, Ext JS, LLC.
16716  *
16717  * Originally Released Under LGPL - original licence link has changed is not relivant.
16718  *
16719  * Fork - LGPL
16720  * <script type="text/javascript">
16721  */
16722  
16723
16724 /**
16725  * @class Roo.tree.DefaultSelectionModel
16726  * @extends Roo.util.Observable
16727  * The default single selection for a TreePanel.
16728  */
16729 Roo.tree.DefaultSelectionModel = function(){
16730    this.selNode = null;
16731    
16732    this.addEvents({
16733        /**
16734         * @event selectionchange
16735         * Fires when the selected node changes
16736         * @param {DefaultSelectionModel} this
16737         * @param {TreeNode} node the new selection
16738         */
16739        "selectionchange" : true,
16740
16741        /**
16742         * @event beforeselect
16743         * Fires before the selected node changes, return false to cancel the change
16744         * @param {DefaultSelectionModel} this
16745         * @param {TreeNode} node the new selection
16746         * @param {TreeNode} node the old selection
16747         */
16748        "beforeselect" : true
16749    });
16750 };
16751
16752 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16753     init : function(tree){
16754         this.tree = tree;
16755         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16756         tree.on("click", this.onNodeClick, this);
16757     },
16758     
16759     onNodeClick : function(node, e){
16760         if (e.ctrlKey && this.selNode == node)  {
16761             this.unselect(node);
16762             return;
16763         }
16764         this.select(node);
16765     },
16766     
16767     /**
16768      * Select a node.
16769      * @param {TreeNode} node The node to select
16770      * @return {TreeNode} The selected node
16771      */
16772     select : function(node){
16773         var last = this.selNode;
16774         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16775             if(last){
16776                 last.ui.onSelectedChange(false);
16777             }
16778             this.selNode = node;
16779             node.ui.onSelectedChange(true);
16780             this.fireEvent("selectionchange", this, node, last);
16781         }
16782         return node;
16783     },
16784     
16785     /**
16786      * Deselect a node.
16787      * @param {TreeNode} node The node to unselect
16788      */
16789     unselect : function(node){
16790         if(this.selNode == node){
16791             this.clearSelections();
16792         }    
16793     },
16794     
16795     /**
16796      * Clear all selections
16797      */
16798     clearSelections : function(){
16799         var n = this.selNode;
16800         if(n){
16801             n.ui.onSelectedChange(false);
16802             this.selNode = null;
16803             this.fireEvent("selectionchange", this, null);
16804         }
16805         return n;
16806     },
16807     
16808     /**
16809      * Get the selected node
16810      * @return {TreeNode} The selected node
16811      */
16812     getSelectedNode : function(){
16813         return this.selNode;    
16814     },
16815     
16816     /**
16817      * Returns true if the node is selected
16818      * @param {TreeNode} node The node to check
16819      * @return {Boolean}
16820      */
16821     isSelected : function(node){
16822         return this.selNode == node;  
16823     },
16824
16825     /**
16826      * Selects the node above the selected node in the tree, intelligently walking the nodes
16827      * @return TreeNode The new selection
16828      */
16829     selectPrevious : function(){
16830         var s = this.selNode || this.lastSelNode;
16831         if(!s){
16832             return null;
16833         }
16834         var ps = s.previousSibling;
16835         if(ps){
16836             if(!ps.isExpanded() || ps.childNodes.length < 1){
16837                 return this.select(ps);
16838             } else{
16839                 var lc = ps.lastChild;
16840                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
16841                     lc = lc.lastChild;
16842                 }
16843                 return this.select(lc);
16844             }
16845         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
16846             return this.select(s.parentNode);
16847         }
16848         return null;
16849     },
16850
16851     /**
16852      * Selects the node above the selected node in the tree, intelligently walking the nodes
16853      * @return TreeNode The new selection
16854      */
16855     selectNext : function(){
16856         var s = this.selNode || this.lastSelNode;
16857         if(!s){
16858             return null;
16859         }
16860         if(s.firstChild && s.isExpanded()){
16861              return this.select(s.firstChild);
16862          }else if(s.nextSibling){
16863              return this.select(s.nextSibling);
16864          }else if(s.parentNode){
16865             var newS = null;
16866             s.parentNode.bubble(function(){
16867                 if(this.nextSibling){
16868                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
16869                     return false;
16870                 }
16871             });
16872             return newS;
16873          }
16874         return null;
16875     },
16876
16877     onKeyDown : function(e){
16878         var s = this.selNode || this.lastSelNode;
16879         // undesirable, but required
16880         var sm = this;
16881         if(!s){
16882             return;
16883         }
16884         var k = e.getKey();
16885         switch(k){
16886              case e.DOWN:
16887                  e.stopEvent();
16888                  this.selectNext();
16889              break;
16890              case e.UP:
16891                  e.stopEvent();
16892                  this.selectPrevious();
16893              break;
16894              case e.RIGHT:
16895                  e.preventDefault();
16896                  if(s.hasChildNodes()){
16897                      if(!s.isExpanded()){
16898                          s.expand();
16899                      }else if(s.firstChild){
16900                          this.select(s.firstChild, e);
16901                      }
16902                  }
16903              break;
16904              case e.LEFT:
16905                  e.preventDefault();
16906                  if(s.hasChildNodes() && s.isExpanded()){
16907                      s.collapse();
16908                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
16909                      this.select(s.parentNode, e);
16910                  }
16911              break;
16912         };
16913     }
16914 });
16915
16916 /**
16917  * @class Roo.tree.MultiSelectionModel
16918  * @extends Roo.util.Observable
16919  * Multi selection for a TreePanel.
16920  */
16921 Roo.tree.MultiSelectionModel = function(){
16922    this.selNodes = [];
16923    this.selMap = {};
16924    this.addEvents({
16925        /**
16926         * @event selectionchange
16927         * Fires when the selected nodes change
16928         * @param {MultiSelectionModel} this
16929         * @param {Array} nodes Array of the selected nodes
16930         */
16931        "selectionchange" : true
16932    });
16933 };
16934
16935 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
16936     init : function(tree){
16937         this.tree = tree;
16938         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16939         tree.on("click", this.onNodeClick, this);
16940     },
16941     
16942     onNodeClick : function(node, e){
16943         this.select(node, e, e.ctrlKey);
16944     },
16945     
16946     /**
16947      * Select a node.
16948      * @param {TreeNode} node The node to select
16949      * @param {EventObject} e (optional) An event associated with the selection
16950      * @param {Boolean} keepExisting True to retain existing selections
16951      * @return {TreeNode} The selected node
16952      */
16953     select : function(node, e, keepExisting){
16954         if(keepExisting !== true){
16955             this.clearSelections(true);
16956         }
16957         if(this.isSelected(node)){
16958             this.lastSelNode = node;
16959             return node;
16960         }
16961         this.selNodes.push(node);
16962         this.selMap[node.id] = node;
16963         this.lastSelNode = node;
16964         node.ui.onSelectedChange(true);
16965         this.fireEvent("selectionchange", this, this.selNodes);
16966         return node;
16967     },
16968     
16969     /**
16970      * Deselect a node.
16971      * @param {TreeNode} node The node to unselect
16972      */
16973     unselect : function(node){
16974         if(this.selMap[node.id]){
16975             node.ui.onSelectedChange(false);
16976             var sn = this.selNodes;
16977             var index = -1;
16978             if(sn.indexOf){
16979                 index = sn.indexOf(node);
16980             }else{
16981                 for(var i = 0, len = sn.length; i < len; i++){
16982                     if(sn[i] == node){
16983                         index = i;
16984                         break;
16985                     }
16986                 }
16987             }
16988             if(index != -1){
16989                 this.selNodes.splice(index, 1);
16990             }
16991             delete this.selMap[node.id];
16992             this.fireEvent("selectionchange", this, this.selNodes);
16993         }
16994     },
16995     
16996     /**
16997      * Clear all selections
16998      */
16999     clearSelections : function(suppressEvent){
17000         var sn = this.selNodes;
17001         if(sn.length > 0){
17002             for(var i = 0, len = sn.length; i < len; i++){
17003                 sn[i].ui.onSelectedChange(false);
17004             }
17005             this.selNodes = [];
17006             this.selMap = {};
17007             if(suppressEvent !== true){
17008                 this.fireEvent("selectionchange", this, this.selNodes);
17009             }
17010         }
17011     },
17012     
17013     /**
17014      * Returns true if the node is selected
17015      * @param {TreeNode} node The node to check
17016      * @return {Boolean}
17017      */
17018     isSelected : function(node){
17019         return this.selMap[node.id] ? true : false;  
17020     },
17021     
17022     /**
17023      * Returns an array of the selected nodes
17024      * @return {Array}
17025      */
17026     getSelectedNodes : function(){
17027         return this.selNodes;    
17028     },
17029
17030     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17031
17032     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17033
17034     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17035 });/*
17036  * Based on:
17037  * Ext JS Library 1.1.1
17038  * Copyright(c) 2006-2007, Ext JS, LLC.
17039  *
17040  * Originally Released Under LGPL - original licence link has changed is not relivant.
17041  *
17042  * Fork - LGPL
17043  * <script type="text/javascript">
17044  */
17045  
17046 /**
17047  * @class Roo.tree.TreeNode
17048  * @extends Roo.data.Node
17049  * @cfg {String} text The text for this node
17050  * @cfg {Boolean} expanded true to start the node expanded
17051  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17052  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17053  * @cfg {Boolean} disabled true to start the node disabled
17054  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17055  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17056  * @cfg {String} cls A css class to be added to the node
17057  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17058  * @cfg {String} href URL of the link used for the node (defaults to #)
17059  * @cfg {String} hrefTarget target frame for the link
17060  * @cfg {String} qtip An Ext QuickTip for the node
17061  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17062  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17063  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17064  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17065  * (defaults to undefined with no checkbox rendered)
17066  * @constructor
17067  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17068  */
17069 Roo.tree.TreeNode = function(attributes){
17070     attributes = attributes || {};
17071     if(typeof attributes == "string"){
17072         attributes = {text: attributes};
17073     }
17074     this.childrenRendered = false;
17075     this.rendered = false;
17076     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17077     this.expanded = attributes.expanded === true;
17078     this.isTarget = attributes.isTarget !== false;
17079     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17080     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17081
17082     /**
17083      * Read-only. The text for this node. To change it use setText().
17084      * @type String
17085      */
17086     this.text = attributes.text;
17087     /**
17088      * True if this node is disabled.
17089      * @type Boolean
17090      */
17091     this.disabled = attributes.disabled === true;
17092
17093     this.addEvents({
17094         /**
17095         * @event textchange
17096         * Fires when the text for this node is changed
17097         * @param {Node} this This node
17098         * @param {String} text The new text
17099         * @param {String} oldText The old text
17100         */
17101         "textchange" : true,
17102         /**
17103         * @event beforeexpand
17104         * Fires before this node is expanded, return false to cancel.
17105         * @param {Node} this This node
17106         * @param {Boolean} deep
17107         * @param {Boolean} anim
17108         */
17109         "beforeexpand" : true,
17110         /**
17111         * @event beforecollapse
17112         * Fires before this node is collapsed, return false to cancel.
17113         * @param {Node} this This node
17114         * @param {Boolean} deep
17115         * @param {Boolean} anim
17116         */
17117         "beforecollapse" : true,
17118         /**
17119         * @event expand
17120         * Fires when this node is expanded
17121         * @param {Node} this This node
17122         */
17123         "expand" : true,
17124         /**
17125         * @event disabledchange
17126         * Fires when the disabled status of this node changes
17127         * @param {Node} this This node
17128         * @param {Boolean} disabled
17129         */
17130         "disabledchange" : true,
17131         /**
17132         * @event collapse
17133         * Fires when this node is collapsed
17134         * @param {Node} this This node
17135         */
17136         "collapse" : true,
17137         /**
17138         * @event beforeclick
17139         * Fires before click processing. Return false to cancel the default action.
17140         * @param {Node} this This node
17141         * @param {Roo.EventObject} e The event object
17142         */
17143         "beforeclick":true,
17144         /**
17145         * @event checkchange
17146         * Fires when a node with a checkbox's checked property changes
17147         * @param {Node} this This node
17148         * @param {Boolean} checked
17149         */
17150         "checkchange":true,
17151         /**
17152         * @event click
17153         * Fires when this node is clicked
17154         * @param {Node} this This node
17155         * @param {Roo.EventObject} e The event object
17156         */
17157         "click":true,
17158         /**
17159         * @event dblclick
17160         * Fires when this node is double clicked
17161         * @param {Node} this This node
17162         * @param {Roo.EventObject} e The event object
17163         */
17164         "dblclick":true,
17165         /**
17166         * @event contextmenu
17167         * Fires when this node is right clicked
17168         * @param {Node} this This node
17169         * @param {Roo.EventObject} e The event object
17170         */
17171         "contextmenu":true,
17172         /**
17173         * @event beforechildrenrendered
17174         * Fires right before the child nodes for this node are rendered
17175         * @param {Node} this This node
17176         */
17177         "beforechildrenrendered":true
17178     });
17179
17180     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17181
17182     /**
17183      * Read-only. The UI for this node
17184      * @type TreeNodeUI
17185      */
17186     this.ui = new uiClass(this);
17187 };
17188 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17189     preventHScroll: true,
17190     /**
17191      * Returns true if this node is expanded
17192      * @return {Boolean}
17193      */
17194     isExpanded : function(){
17195         return this.expanded;
17196     },
17197
17198     /**
17199      * Returns the UI object for this node
17200      * @return {TreeNodeUI}
17201      */
17202     getUI : function(){
17203         return this.ui;
17204     },
17205
17206     // private override
17207     setFirstChild : function(node){
17208         var of = this.firstChild;
17209         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17210         if(this.childrenRendered && of && node != of){
17211             of.renderIndent(true, true);
17212         }
17213         if(this.rendered){
17214             this.renderIndent(true, true);
17215         }
17216     },
17217
17218     // private override
17219     setLastChild : function(node){
17220         var ol = this.lastChild;
17221         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17222         if(this.childrenRendered && ol && node != ol){
17223             ol.renderIndent(true, true);
17224         }
17225         if(this.rendered){
17226             this.renderIndent(true, true);
17227         }
17228     },
17229
17230     // these methods are overridden to provide lazy rendering support
17231     // private override
17232     appendChild : function(){
17233         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17234         if(node && this.childrenRendered){
17235             node.render();
17236         }
17237         this.ui.updateExpandIcon();
17238         return node;
17239     },
17240
17241     // private override
17242     removeChild : function(node){
17243         this.ownerTree.getSelectionModel().unselect(node);
17244         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17245         // if it's been rendered remove dom node
17246         if(this.childrenRendered){
17247             node.ui.remove();
17248         }
17249         if(this.childNodes.length < 1){
17250             this.collapse(false, false);
17251         }else{
17252             this.ui.updateExpandIcon();
17253         }
17254         if(!this.firstChild) {
17255             this.childrenRendered = false;
17256         }
17257         return node;
17258     },
17259
17260     // private override
17261     insertBefore : function(node, refNode){
17262         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17263         if(newNode && refNode && this.childrenRendered){
17264             node.render();
17265         }
17266         this.ui.updateExpandIcon();
17267         return newNode;
17268     },
17269
17270     /**
17271      * Sets the text for this node
17272      * @param {String} text
17273      */
17274     setText : function(text){
17275         var oldText = this.text;
17276         this.text = text;
17277         this.attributes.text = text;
17278         if(this.rendered){ // event without subscribing
17279             this.ui.onTextChange(this, text, oldText);
17280         }
17281         this.fireEvent("textchange", this, text, oldText);
17282     },
17283
17284     /**
17285      * Triggers selection of this node
17286      */
17287     select : function(){
17288         this.getOwnerTree().getSelectionModel().select(this);
17289     },
17290
17291     /**
17292      * Triggers deselection of this node
17293      */
17294     unselect : function(){
17295         this.getOwnerTree().getSelectionModel().unselect(this);
17296     },
17297
17298     /**
17299      * Returns true if this node is selected
17300      * @return {Boolean}
17301      */
17302     isSelected : function(){
17303         return this.getOwnerTree().getSelectionModel().isSelected(this);
17304     },
17305
17306     /**
17307      * Expand this node.
17308      * @param {Boolean} deep (optional) True to expand all children as well
17309      * @param {Boolean} anim (optional) false to cancel the default animation
17310      * @param {Function} callback (optional) A callback to be called when
17311      * expanding this node completes (does not wait for deep expand to complete).
17312      * Called with 1 parameter, this node.
17313      */
17314     expand : function(deep, anim, callback){
17315         if(!this.expanded){
17316             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17317                 return;
17318             }
17319             if(!this.childrenRendered){
17320                 this.renderChildren();
17321             }
17322             this.expanded = true;
17323             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17324                 this.ui.animExpand(function(){
17325                     this.fireEvent("expand", this);
17326                     if(typeof callback == "function"){
17327                         callback(this);
17328                     }
17329                     if(deep === true){
17330                         this.expandChildNodes(true);
17331                     }
17332                 }.createDelegate(this));
17333                 return;
17334             }else{
17335                 this.ui.expand();
17336                 this.fireEvent("expand", this);
17337                 if(typeof callback == "function"){
17338                     callback(this);
17339                 }
17340             }
17341         }else{
17342            if(typeof callback == "function"){
17343                callback(this);
17344            }
17345         }
17346         if(deep === true){
17347             this.expandChildNodes(true);
17348         }
17349     },
17350
17351     isHiddenRoot : function(){
17352         return this.isRoot && !this.getOwnerTree().rootVisible;
17353     },
17354
17355     /**
17356      * Collapse this node.
17357      * @param {Boolean} deep (optional) True to collapse all children as well
17358      * @param {Boolean} anim (optional) false to cancel the default animation
17359      */
17360     collapse : function(deep, anim){
17361         if(this.expanded && !this.isHiddenRoot()){
17362             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17363                 return;
17364             }
17365             this.expanded = false;
17366             if((this.getOwnerTree().animate && anim !== false) || anim){
17367                 this.ui.animCollapse(function(){
17368                     this.fireEvent("collapse", this);
17369                     if(deep === true){
17370                         this.collapseChildNodes(true);
17371                     }
17372                 }.createDelegate(this));
17373                 return;
17374             }else{
17375                 this.ui.collapse();
17376                 this.fireEvent("collapse", this);
17377             }
17378         }
17379         if(deep === true){
17380             var cs = this.childNodes;
17381             for(var i = 0, len = cs.length; i < len; i++) {
17382                 cs[i].collapse(true, false);
17383             }
17384         }
17385     },
17386
17387     // private
17388     delayedExpand : function(delay){
17389         if(!this.expandProcId){
17390             this.expandProcId = this.expand.defer(delay, this);
17391         }
17392     },
17393
17394     // private
17395     cancelExpand : function(){
17396         if(this.expandProcId){
17397             clearTimeout(this.expandProcId);
17398         }
17399         this.expandProcId = false;
17400     },
17401
17402     /**
17403      * Toggles expanded/collapsed state of the node
17404      */
17405     toggle : function(){
17406         if(this.expanded){
17407             this.collapse();
17408         }else{
17409             this.expand();
17410         }
17411     },
17412
17413     /**
17414      * Ensures all parent nodes are expanded
17415      */
17416     ensureVisible : function(callback){
17417         var tree = this.getOwnerTree();
17418         tree.expandPath(this.parentNode.getPath(), false, function(){
17419             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17420             Roo.callback(callback);
17421         }.createDelegate(this));
17422     },
17423
17424     /**
17425      * Expand all child nodes
17426      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17427      */
17428     expandChildNodes : function(deep){
17429         var cs = this.childNodes;
17430         for(var i = 0, len = cs.length; i < len; i++) {
17431                 cs[i].expand(deep);
17432         }
17433     },
17434
17435     /**
17436      * Collapse all child nodes
17437      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17438      */
17439     collapseChildNodes : function(deep){
17440         var cs = this.childNodes;
17441         for(var i = 0, len = cs.length; i < len; i++) {
17442                 cs[i].collapse(deep);
17443         }
17444     },
17445
17446     /**
17447      * Disables this node
17448      */
17449     disable : function(){
17450         this.disabled = true;
17451         this.unselect();
17452         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17453             this.ui.onDisableChange(this, true);
17454         }
17455         this.fireEvent("disabledchange", this, true);
17456     },
17457
17458     /**
17459      * Enables this node
17460      */
17461     enable : function(){
17462         this.disabled = false;
17463         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17464             this.ui.onDisableChange(this, false);
17465         }
17466         this.fireEvent("disabledchange", this, false);
17467     },
17468
17469     // private
17470     renderChildren : function(suppressEvent){
17471         if(suppressEvent !== false){
17472             this.fireEvent("beforechildrenrendered", this);
17473         }
17474         var cs = this.childNodes;
17475         for(var i = 0, len = cs.length; i < len; i++){
17476             cs[i].render(true);
17477         }
17478         this.childrenRendered = true;
17479     },
17480
17481     // private
17482     sort : function(fn, scope){
17483         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17484         if(this.childrenRendered){
17485             var cs = this.childNodes;
17486             for(var i = 0, len = cs.length; i < len; i++){
17487                 cs[i].render(true);
17488             }
17489         }
17490     },
17491
17492     // private
17493     render : function(bulkRender){
17494         this.ui.render(bulkRender);
17495         if(!this.rendered){
17496             this.rendered = true;
17497             if(this.expanded){
17498                 this.expanded = false;
17499                 this.expand(false, false);
17500             }
17501         }
17502     },
17503
17504     // private
17505     renderIndent : function(deep, refresh){
17506         if(refresh){
17507             this.ui.childIndent = null;
17508         }
17509         this.ui.renderIndent();
17510         if(deep === true && this.childrenRendered){
17511             var cs = this.childNodes;
17512             for(var i = 0, len = cs.length; i < len; i++){
17513                 cs[i].renderIndent(true, refresh);
17514             }
17515         }
17516     }
17517 });/*
17518  * Based on:
17519  * Ext JS Library 1.1.1
17520  * Copyright(c) 2006-2007, Ext JS, LLC.
17521  *
17522  * Originally Released Under LGPL - original licence link has changed is not relivant.
17523  *
17524  * Fork - LGPL
17525  * <script type="text/javascript">
17526  */
17527  
17528 /**
17529  * @class Roo.tree.AsyncTreeNode
17530  * @extends Roo.tree.TreeNode
17531  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17532  * @constructor
17533  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17534  */
17535  Roo.tree.AsyncTreeNode = function(config){
17536     this.loaded = false;
17537     this.loading = false;
17538     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17539     /**
17540     * @event beforeload
17541     * Fires before this node is loaded, return false to cancel
17542     * @param {Node} this This node
17543     */
17544     this.addEvents({'beforeload':true, 'load': true});
17545     /**
17546     * @event load
17547     * Fires when this node is loaded
17548     * @param {Node} this This node
17549     */
17550     /**
17551      * The loader used by this node (defaults to using the tree's defined loader)
17552      * @type TreeLoader
17553      * @property loader
17554      */
17555 };
17556 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17557     expand : function(deep, anim, callback){
17558         if(this.loading){ // if an async load is already running, waiting til it's done
17559             var timer;
17560             var f = function(){
17561                 if(!this.loading){ // done loading
17562                     clearInterval(timer);
17563                     this.expand(deep, anim, callback);
17564                 }
17565             }.createDelegate(this);
17566             timer = setInterval(f, 200);
17567             return;
17568         }
17569         if(!this.loaded){
17570             if(this.fireEvent("beforeload", this) === false){
17571                 return;
17572             }
17573             this.loading = true;
17574             this.ui.beforeLoad(this);
17575             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17576             if(loader){
17577                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17578                 return;
17579             }
17580         }
17581         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17582     },
17583     
17584     /**
17585      * Returns true if this node is currently loading
17586      * @return {Boolean}
17587      */
17588     isLoading : function(){
17589         return this.loading;  
17590     },
17591     
17592     loadComplete : function(deep, anim, callback){
17593         this.loading = false;
17594         this.loaded = true;
17595         this.ui.afterLoad(this);
17596         this.fireEvent("load", this);
17597         this.expand(deep, anim, callback);
17598     },
17599     
17600     /**
17601      * Returns true if this node has been loaded
17602      * @return {Boolean}
17603      */
17604     isLoaded : function(){
17605         return this.loaded;
17606     },
17607     
17608     hasChildNodes : function(){
17609         if(!this.isLeaf() && !this.loaded){
17610             return true;
17611         }else{
17612             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17613         }
17614     },
17615
17616     /**
17617      * Trigger a reload for this node
17618      * @param {Function} callback
17619      */
17620     reload : function(callback){
17621         this.collapse(false, false);
17622         while(this.firstChild){
17623             this.removeChild(this.firstChild);
17624         }
17625         this.childrenRendered = false;
17626         this.loaded = false;
17627         if(this.isHiddenRoot()){
17628             this.expanded = false;
17629         }
17630         this.expand(false, false, callback);
17631     }
17632 });/*
17633  * Based on:
17634  * Ext JS Library 1.1.1
17635  * Copyright(c) 2006-2007, Ext JS, LLC.
17636  *
17637  * Originally Released Under LGPL - original licence link has changed is not relivant.
17638  *
17639  * Fork - LGPL
17640  * <script type="text/javascript">
17641  */
17642  
17643 /**
17644  * @class Roo.tree.TreeNodeUI
17645  * @constructor
17646  * @param {Object} node The node to render
17647  * The TreeNode UI implementation is separate from the
17648  * tree implementation. Unless you are customizing the tree UI,
17649  * you should never have to use this directly.
17650  */
17651 Roo.tree.TreeNodeUI = function(node){
17652     this.node = node;
17653     this.rendered = false;
17654     this.animating = false;
17655     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17656 };
17657
17658 Roo.tree.TreeNodeUI.prototype = {
17659     removeChild : function(node){
17660         if(this.rendered){
17661             this.ctNode.removeChild(node.ui.getEl());
17662         }
17663     },
17664
17665     beforeLoad : function(){
17666          this.addClass("x-tree-node-loading");
17667     },
17668
17669     afterLoad : function(){
17670          this.removeClass("x-tree-node-loading");
17671     },
17672
17673     onTextChange : function(node, text, oldText){
17674         if(this.rendered){
17675             this.textNode.innerHTML = text;
17676         }
17677     },
17678
17679     onDisableChange : function(node, state){
17680         this.disabled = state;
17681         if(state){
17682             this.addClass("x-tree-node-disabled");
17683         }else{
17684             this.removeClass("x-tree-node-disabled");
17685         }
17686     },
17687
17688     onSelectedChange : function(state){
17689         if(state){
17690             this.focus();
17691             this.addClass("x-tree-selected");
17692         }else{
17693             //this.blur();
17694             this.removeClass("x-tree-selected");
17695         }
17696     },
17697
17698     onMove : function(tree, node, oldParent, newParent, index, refNode){
17699         this.childIndent = null;
17700         if(this.rendered){
17701             var targetNode = newParent.ui.getContainer();
17702             if(!targetNode){//target not rendered
17703                 this.holder = document.createElement("div");
17704                 this.holder.appendChild(this.wrap);
17705                 return;
17706             }
17707             var insertBefore = refNode ? refNode.ui.getEl() : null;
17708             if(insertBefore){
17709                 targetNode.insertBefore(this.wrap, insertBefore);
17710             }else{
17711                 targetNode.appendChild(this.wrap);
17712             }
17713             this.node.renderIndent(true);
17714         }
17715     },
17716
17717     addClass : function(cls){
17718         if(this.elNode){
17719             Roo.fly(this.elNode).addClass(cls);
17720         }
17721     },
17722
17723     removeClass : function(cls){
17724         if(this.elNode){
17725             Roo.fly(this.elNode).removeClass(cls);
17726         }
17727     },
17728
17729     remove : function(){
17730         if(this.rendered){
17731             this.holder = document.createElement("div");
17732             this.holder.appendChild(this.wrap);
17733         }
17734     },
17735
17736     fireEvent : function(){
17737         return this.node.fireEvent.apply(this.node, arguments);
17738     },
17739
17740     initEvents : function(){
17741         this.node.on("move", this.onMove, this);
17742         var E = Roo.EventManager;
17743         var a = this.anchor;
17744
17745         var el = Roo.fly(a, '_treeui');
17746
17747         if(Roo.isOpera){ // opera render bug ignores the CSS
17748             el.setStyle("text-decoration", "none");
17749         }
17750
17751         el.on("click", this.onClick, this);
17752         el.on("dblclick", this.onDblClick, this);
17753
17754         if(this.checkbox){
17755             Roo.EventManager.on(this.checkbox,
17756                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17757         }
17758
17759         el.on("contextmenu", this.onContextMenu, this);
17760
17761         var icon = Roo.fly(this.iconNode);
17762         icon.on("click", this.onClick, this);
17763         icon.on("dblclick", this.onDblClick, this);
17764         icon.on("contextmenu", this.onContextMenu, this);
17765         E.on(this.ecNode, "click", this.ecClick, this, true);
17766
17767         if(this.node.disabled){
17768             this.addClass("x-tree-node-disabled");
17769         }
17770         if(this.node.hidden){
17771             this.addClass("x-tree-node-disabled");
17772         }
17773         var ot = this.node.getOwnerTree();
17774         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17775         if(dd && (!this.node.isRoot || ot.rootVisible)){
17776             Roo.dd.Registry.register(this.elNode, {
17777                 node: this.node,
17778                 handles: this.getDDHandles(),
17779                 isHandle: false
17780             });
17781         }
17782     },
17783
17784     getDDHandles : function(){
17785         return [this.iconNode, this.textNode];
17786     },
17787
17788     hide : function(){
17789         if(this.rendered){
17790             this.wrap.style.display = "none";
17791         }
17792     },
17793
17794     show : function(){
17795         if(this.rendered){
17796             this.wrap.style.display = "";
17797         }
17798     },
17799
17800     onContextMenu : function(e){
17801         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17802             e.preventDefault();
17803             this.focus();
17804             this.fireEvent("contextmenu", this.node, e);
17805         }
17806     },
17807
17808     onClick : function(e){
17809         if(this.dropping){
17810             e.stopEvent();
17811             return;
17812         }
17813         if(this.fireEvent("beforeclick", this.node, e) !== false){
17814             if(!this.disabled && this.node.attributes.href){
17815                 this.fireEvent("click", this.node, e);
17816                 return;
17817             }
17818             e.preventDefault();
17819             if(this.disabled){
17820                 return;
17821             }
17822
17823             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
17824                 this.node.toggle();
17825             }
17826
17827             this.fireEvent("click", this.node, e);
17828         }else{
17829             e.stopEvent();
17830         }
17831     },
17832
17833     onDblClick : function(e){
17834         e.preventDefault();
17835         if(this.disabled){
17836             return;
17837         }
17838         if(this.checkbox){
17839             this.toggleCheck();
17840         }
17841         if(!this.animating && this.node.hasChildNodes()){
17842             this.node.toggle();
17843         }
17844         this.fireEvent("dblclick", this.node, e);
17845     },
17846
17847     onCheckChange : function(){
17848         var checked = this.checkbox.checked;
17849         this.node.attributes.checked = checked;
17850         this.fireEvent('checkchange', this.node, checked);
17851     },
17852
17853     ecClick : function(e){
17854         if(!this.animating && this.node.hasChildNodes()){
17855             this.node.toggle();
17856         }
17857     },
17858
17859     startDrop : function(){
17860         this.dropping = true;
17861     },
17862
17863     // delayed drop so the click event doesn't get fired on a drop
17864     endDrop : function(){
17865        setTimeout(function(){
17866            this.dropping = false;
17867        }.createDelegate(this), 50);
17868     },
17869
17870     expand : function(){
17871         this.updateExpandIcon();
17872         this.ctNode.style.display = "";
17873     },
17874
17875     focus : function(){
17876         if(!this.node.preventHScroll){
17877             try{this.anchor.focus();
17878             }catch(e){}
17879         }else if(!Roo.isIE){
17880             try{
17881                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
17882                 var l = noscroll.scrollLeft;
17883                 this.anchor.focus();
17884                 noscroll.scrollLeft = l;
17885             }catch(e){}
17886         }
17887     },
17888
17889     toggleCheck : function(value){
17890         var cb = this.checkbox;
17891         if(cb){
17892             cb.checked = (value === undefined ? !cb.checked : value);
17893         }
17894     },
17895
17896     blur : function(){
17897         try{
17898             this.anchor.blur();
17899         }catch(e){}
17900     },
17901
17902     animExpand : function(callback){
17903         var ct = Roo.get(this.ctNode);
17904         ct.stopFx();
17905         if(!this.node.hasChildNodes()){
17906             this.updateExpandIcon();
17907             this.ctNode.style.display = "";
17908             Roo.callback(callback);
17909             return;
17910         }
17911         this.animating = true;
17912         this.updateExpandIcon();
17913
17914         ct.slideIn('t', {
17915            callback : function(){
17916                this.animating = false;
17917                Roo.callback(callback);
17918             },
17919             scope: this,
17920             duration: this.node.ownerTree.duration || .25
17921         });
17922     },
17923
17924     highlight : function(){
17925         var tree = this.node.getOwnerTree();
17926         Roo.fly(this.wrap).highlight(
17927             tree.hlColor || "C3DAF9",
17928             {endColor: tree.hlBaseColor}
17929         );
17930     },
17931
17932     collapse : function(){
17933         this.updateExpandIcon();
17934         this.ctNode.style.display = "none";
17935     },
17936
17937     animCollapse : function(callback){
17938         var ct = Roo.get(this.ctNode);
17939         ct.enableDisplayMode('block');
17940         ct.stopFx();
17941
17942         this.animating = true;
17943         this.updateExpandIcon();
17944
17945         ct.slideOut('t', {
17946             callback : function(){
17947                this.animating = false;
17948                Roo.callback(callback);
17949             },
17950             scope: this,
17951             duration: this.node.ownerTree.duration || .25
17952         });
17953     },
17954
17955     getContainer : function(){
17956         return this.ctNode;
17957     },
17958
17959     getEl : function(){
17960         return this.wrap;
17961     },
17962
17963     appendDDGhost : function(ghostNode){
17964         ghostNode.appendChild(this.elNode.cloneNode(true));
17965     },
17966
17967     getDDRepairXY : function(){
17968         return Roo.lib.Dom.getXY(this.iconNode);
17969     },
17970
17971     onRender : function(){
17972         this.render();
17973     },
17974
17975     render : function(bulkRender){
17976         var n = this.node, a = n.attributes;
17977         var targetNode = n.parentNode ?
17978               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
17979
17980         if(!this.rendered){
17981             this.rendered = true;
17982
17983             this.renderElements(n, a, targetNode, bulkRender);
17984
17985             if(a.qtip){
17986                if(this.textNode.setAttributeNS){
17987                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
17988                    if(a.qtipTitle){
17989                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
17990                    }
17991                }else{
17992                    this.textNode.setAttribute("ext:qtip", a.qtip);
17993                    if(a.qtipTitle){
17994                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
17995                    }
17996                }
17997             }else if(a.qtipCfg){
17998                 a.qtipCfg.target = Roo.id(this.textNode);
17999                 Roo.QuickTips.register(a.qtipCfg);
18000             }
18001             this.initEvents();
18002             if(!this.node.expanded){
18003                 this.updateExpandIcon();
18004             }
18005         }else{
18006             if(bulkRender === true) {
18007                 targetNode.appendChild(this.wrap);
18008             }
18009         }
18010     },
18011
18012     renderElements : function(n, a, targetNode, bulkRender){
18013         // add some indent caching, this helps performance when rendering a large tree
18014         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18015         var t = n.getOwnerTree();
18016         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18017         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18018         var cb = typeof a.checked == 'boolean';
18019         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18020         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18021             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18022             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18023             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18024             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18025             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18026              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18027                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18028             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18029             "</li>"];
18030
18031         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18032             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18033                                 n.nextSibling.ui.getEl(), buf.join(""));
18034         }else{
18035             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18036         }
18037
18038         this.elNode = this.wrap.childNodes[0];
18039         this.ctNode = this.wrap.childNodes[1];
18040         var cs = this.elNode.childNodes;
18041         this.indentNode = cs[0];
18042         this.ecNode = cs[1];
18043         this.iconNode = cs[2];
18044         var index = 3;
18045         if(cb){
18046             this.checkbox = cs[3];
18047             index++;
18048         }
18049         this.anchor = cs[index];
18050         this.textNode = cs[index].firstChild;
18051     },
18052
18053     getAnchor : function(){
18054         return this.anchor;
18055     },
18056
18057     getTextEl : function(){
18058         return this.textNode;
18059     },
18060
18061     getIconEl : function(){
18062         return this.iconNode;
18063     },
18064
18065     isChecked : function(){
18066         return this.checkbox ? this.checkbox.checked : false;
18067     },
18068
18069     updateExpandIcon : function(){
18070         if(this.rendered){
18071             var n = this.node, c1, c2;
18072             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18073             var hasChild = n.hasChildNodes();
18074             if(hasChild){
18075                 if(n.expanded){
18076                     cls += "-minus";
18077                     c1 = "x-tree-node-collapsed";
18078                     c2 = "x-tree-node-expanded";
18079                 }else{
18080                     cls += "-plus";
18081                     c1 = "x-tree-node-expanded";
18082                     c2 = "x-tree-node-collapsed";
18083                 }
18084                 if(this.wasLeaf){
18085                     this.removeClass("x-tree-node-leaf");
18086                     this.wasLeaf = false;
18087                 }
18088                 if(this.c1 != c1 || this.c2 != c2){
18089                     Roo.fly(this.elNode).replaceClass(c1, c2);
18090                     this.c1 = c1; this.c2 = c2;
18091                 }
18092             }else{
18093                 if(!this.wasLeaf){
18094                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18095                     delete this.c1;
18096                     delete this.c2;
18097                     this.wasLeaf = true;
18098                 }
18099             }
18100             var ecc = "x-tree-ec-icon "+cls;
18101             if(this.ecc != ecc){
18102                 this.ecNode.className = ecc;
18103                 this.ecc = ecc;
18104             }
18105         }
18106     },
18107
18108     getChildIndent : function(){
18109         if(!this.childIndent){
18110             var buf = [];
18111             var p = this.node;
18112             while(p){
18113                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18114                     if(!p.isLast()) {
18115                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18116                     } else {
18117                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18118                     }
18119                 }
18120                 p = p.parentNode;
18121             }
18122             this.childIndent = buf.join("");
18123         }
18124         return this.childIndent;
18125     },
18126
18127     renderIndent : function(){
18128         if(this.rendered){
18129             var indent = "";
18130             var p = this.node.parentNode;
18131             if(p){
18132                 indent = p.ui.getChildIndent();
18133             }
18134             if(this.indentMarkup != indent){ // don't rerender if not required
18135                 this.indentNode.innerHTML = indent;
18136                 this.indentMarkup = indent;
18137             }
18138             this.updateExpandIcon();
18139         }
18140     }
18141 };
18142
18143 Roo.tree.RootTreeNodeUI = function(){
18144     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18145 };
18146 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18147     render : function(){
18148         if(!this.rendered){
18149             var targetNode = this.node.ownerTree.innerCt.dom;
18150             this.node.expanded = true;
18151             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18152             this.wrap = this.ctNode = targetNode.firstChild;
18153         }
18154     },
18155     collapse : function(){
18156     },
18157     expand : function(){
18158     }
18159 });/*
18160  * Based on:
18161  * Ext JS Library 1.1.1
18162  * Copyright(c) 2006-2007, Ext JS, LLC.
18163  *
18164  * Originally Released Under LGPL - original licence link has changed is not relivant.
18165  *
18166  * Fork - LGPL
18167  * <script type="text/javascript">
18168  */
18169 /**
18170  * @class Roo.tree.TreeLoader
18171  * @extends Roo.util.Observable
18172  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18173  * nodes from a specified URL. The response must be a javascript Array definition
18174  * who's elements are node definition objects. eg:
18175  * <pre><code>
18176    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
18177     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
18178 </code></pre>
18179  * <br><br>
18180  * A server request is sent, and child nodes are loaded only when a node is expanded.
18181  * The loading node's id is passed to the server under the parameter name "node" to
18182  * enable the server to produce the correct child nodes.
18183  * <br><br>
18184  * To pass extra parameters, an event handler may be attached to the "beforeload"
18185  * event, and the parameters specified in the TreeLoader's baseParams property:
18186  * <pre><code>
18187     myTreeLoader.on("beforeload", function(treeLoader, node) {
18188         this.baseParams.category = node.attributes.category;
18189     }, this);
18190 </code></pre><
18191  * This would pass an HTTP parameter called "category" to the server containing
18192  * the value of the Node's "category" attribute.
18193  * @constructor
18194  * Creates a new Treeloader.
18195  * @param {Object} config A config object containing config properties.
18196  */
18197 Roo.tree.TreeLoader = function(config){
18198     this.baseParams = {};
18199     this.requestMethod = "POST";
18200     Roo.apply(this, config);
18201
18202     this.addEvents({
18203     
18204         /**
18205          * @event beforeload
18206          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18207          * @param {Object} This TreeLoader object.
18208          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18209          * @param {Object} callback The callback function specified in the {@link #load} call.
18210          */
18211         beforeload : true,
18212         /**
18213          * @event load
18214          * Fires when the node has been successfuly loaded.
18215          * @param {Object} This TreeLoader object.
18216          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18217          * @param {Object} response The response object containing the data from the server.
18218          */
18219         load : true,
18220         /**
18221          * @event loadexception
18222          * Fires if the network request failed.
18223          * @param {Object} This TreeLoader object.
18224          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18225          * @param {Object} response The response object containing the data from the server.
18226          */
18227         loadexception : true,
18228         /**
18229          * @event create
18230          * Fires before a node is created, enabling you to return custom Node types 
18231          * @param {Object} This TreeLoader object.
18232          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18233          */
18234         create : true
18235     });
18236
18237     Roo.tree.TreeLoader.superclass.constructor.call(this);
18238 };
18239
18240 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18241     /**
18242     * @cfg {String} dataUrl The URL from which to request a Json string which
18243     * specifies an array of node definition object representing the child nodes
18244     * to be loaded.
18245     */
18246     /**
18247     * @cfg {Object} baseParams (optional) An object containing properties which
18248     * specify HTTP parameters to be passed to each request for child nodes.
18249     */
18250     /**
18251     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18252     * created by this loader. If the attributes sent by the server have an attribute in this object,
18253     * they take priority.
18254     */
18255     /**
18256     * @cfg {Object} uiProviders (optional) An object containing properties which
18257     * 
18258     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
18259     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18260     * <i>uiProvider</i> attribute of a returned child node is a string rather
18261     * than a reference to a TreeNodeUI implementation, this that string value
18262     * is used as a property name in the uiProviders object. You can define the provider named
18263     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18264     */
18265     uiProviders : {},
18266
18267     /**
18268     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18269     * child nodes before loading.
18270     */
18271     clearOnLoad : true,
18272
18273     /**
18274     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18275     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18276     * Grid query { data : [ .....] }
18277     */
18278     
18279     root : false,
18280      /**
18281     * @cfg {String} queryParam (optional) 
18282     * Name of the query as it will be passed on the querystring (defaults to 'node')
18283     * eg. the request will be ?node=[id]
18284     */
18285     
18286     
18287     queryParam: false,
18288     
18289     /**
18290      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18291      * This is called automatically when a node is expanded, but may be used to reload
18292      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18293      * @param {Roo.tree.TreeNode} node
18294      * @param {Function} callback
18295      */
18296     load : function(node, callback){
18297         if(this.clearOnLoad){
18298             while(node.firstChild){
18299                 node.removeChild(node.firstChild);
18300             }
18301         }
18302         if(node.attributes.children){ // preloaded json children
18303             var cs = node.attributes.children;
18304             for(var i = 0, len = cs.length; i < len; i++){
18305                 node.appendChild(this.createNode(cs[i]));
18306             }
18307             if(typeof callback == "function"){
18308                 callback();
18309             }
18310         }else if(this.dataUrl){
18311             this.requestData(node, callback);
18312         }
18313     },
18314
18315     getParams: function(node){
18316         var buf = [], bp = this.baseParams;
18317         for(var key in bp){
18318             if(typeof bp[key] != "function"){
18319                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18320             }
18321         }
18322         var n = this.queryParam === false ? 'node' : this.queryParam;
18323         buf.push(n + "=", encodeURIComponent(node.id));
18324         return buf.join("");
18325     },
18326
18327     requestData : function(node, callback){
18328         if(this.fireEvent("beforeload", this, node, callback) !== false){
18329             this.transId = Roo.Ajax.request({
18330                 method:this.requestMethod,
18331                 url: this.dataUrl||this.url,
18332                 success: this.handleResponse,
18333                 failure: this.handleFailure,
18334                 scope: this,
18335                 argument: {callback: callback, node: node},
18336                 params: this.getParams(node)
18337             });
18338         }else{
18339             // if the load is cancelled, make sure we notify
18340             // the node that we are done
18341             if(typeof callback == "function"){
18342                 callback();
18343             }
18344         }
18345     },
18346
18347     isLoading : function(){
18348         return this.transId ? true : false;
18349     },
18350
18351     abort : function(){
18352         if(this.isLoading()){
18353             Roo.Ajax.abort(this.transId);
18354         }
18355     },
18356
18357     // private
18358     createNode : function(attr){
18359         // apply baseAttrs, nice idea Corey!
18360         if(this.baseAttrs){
18361             Roo.applyIf(attr, this.baseAttrs);
18362         }
18363         if(this.applyLoader !== false){
18364             attr.loader = this;
18365         }
18366         // uiProvider = depreciated..
18367         
18368         if(typeof(attr.uiProvider) == 'string'){
18369            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18370                 /**  eval:var:attr */ eval(attr.uiProvider);
18371         }
18372         if(typeof(this.uiProviders['default']) != 'undefined') {
18373             attr.uiProvider = this.uiProviders['default'];
18374         }
18375         
18376         this.fireEvent('create', this, attr);
18377         
18378         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18379         return(attr.leaf ?
18380                         new Roo.tree.TreeNode(attr) :
18381                         new Roo.tree.AsyncTreeNode(attr));
18382     },
18383
18384     processResponse : function(response, node, callback){
18385         var json = response.responseText;
18386         try {
18387             
18388             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
18389             if (this.root !== false) {
18390                 o = o[this.root];
18391             }
18392             
18393             for(var i = 0, len = o.length; i < len; i++){
18394                 var n = this.createNode(o[i]);
18395                 if(n){
18396                     node.appendChild(n);
18397                 }
18398             }
18399             if(typeof callback == "function"){
18400                 callback(this, node);
18401             }
18402         }catch(e){
18403             this.handleFailure(response);
18404         }
18405     },
18406
18407     handleResponse : function(response){
18408         this.transId = false;
18409         var a = response.argument;
18410         this.processResponse(response, a.node, a.callback);
18411         this.fireEvent("load", this, a.node, response);
18412     },
18413
18414     handleFailure : function(response){
18415         this.transId = false;
18416         var a = response.argument;
18417         this.fireEvent("loadexception", this, a.node, response);
18418         if(typeof a.callback == "function"){
18419             a.callback(this, a.node);
18420         }
18421     }
18422 });/*
18423  * Based on:
18424  * Ext JS Library 1.1.1
18425  * Copyright(c) 2006-2007, Ext JS, LLC.
18426  *
18427  * Originally Released Under LGPL - original licence link has changed is not relivant.
18428  *
18429  * Fork - LGPL
18430  * <script type="text/javascript">
18431  */
18432
18433 /**
18434 * @class Roo.tree.TreeFilter
18435 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18436 * @param {TreePanel} tree
18437 * @param {Object} config (optional)
18438  */
18439 Roo.tree.TreeFilter = function(tree, config){
18440     this.tree = tree;
18441     this.filtered = {};
18442     Roo.apply(this, config);
18443 };
18444
18445 Roo.tree.TreeFilter.prototype = {
18446     clearBlank:false,
18447     reverse:false,
18448     autoClear:false,
18449     remove:false,
18450
18451      /**
18452      * Filter the data by a specific attribute.
18453      * @param {String/RegExp} value Either string that the attribute value
18454      * should start with or a RegExp to test against the attribute
18455      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18456      * @param {TreeNode} startNode (optional) The node to start the filter at.
18457      */
18458     filter : function(value, attr, startNode){
18459         attr = attr || "text";
18460         var f;
18461         if(typeof value == "string"){
18462             var vlen = value.length;
18463             // auto clear empty filter
18464             if(vlen == 0 && this.clearBlank){
18465                 this.clear();
18466                 return;
18467             }
18468             value = value.toLowerCase();
18469             f = function(n){
18470                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18471             };
18472         }else if(value.exec){ // regex?
18473             f = function(n){
18474                 return value.test(n.attributes[attr]);
18475             };
18476         }else{
18477             throw 'Illegal filter type, must be string or regex';
18478         }
18479         this.filterBy(f, null, startNode);
18480         },
18481
18482     /**
18483      * Filter by a function. The passed function will be called with each
18484      * node in the tree (or from the startNode). If the function returns true, the node is kept
18485      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18486      * @param {Function} fn The filter function
18487      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18488      */
18489     filterBy : function(fn, scope, startNode){
18490         startNode = startNode || this.tree.root;
18491         if(this.autoClear){
18492             this.clear();
18493         }
18494         var af = this.filtered, rv = this.reverse;
18495         var f = function(n){
18496             if(n == startNode){
18497                 return true;
18498             }
18499             if(af[n.id]){
18500                 return false;
18501             }
18502             var m = fn.call(scope || n, n);
18503             if(!m || rv){
18504                 af[n.id] = n;
18505                 n.ui.hide();
18506                 return false;
18507             }
18508             return true;
18509         };
18510         startNode.cascade(f);
18511         if(this.remove){
18512            for(var id in af){
18513                if(typeof id != "function"){
18514                    var n = af[id];
18515                    if(n && n.parentNode){
18516                        n.parentNode.removeChild(n);
18517                    }
18518                }
18519            }
18520         }
18521     },
18522
18523     /**
18524      * Clears the current filter. Note: with the "remove" option
18525      * set a filter cannot be cleared.
18526      */
18527     clear : function(){
18528         var t = this.tree;
18529         var af = this.filtered;
18530         for(var id in af){
18531             if(typeof id != "function"){
18532                 var n = af[id];
18533                 if(n){
18534                     n.ui.show();
18535                 }
18536             }
18537         }
18538         this.filtered = {};
18539     }
18540 };
18541 /*
18542  * Based on:
18543  * Ext JS Library 1.1.1
18544  * Copyright(c) 2006-2007, Ext JS, LLC.
18545  *
18546  * Originally Released Under LGPL - original licence link has changed is not relivant.
18547  *
18548  * Fork - LGPL
18549  * <script type="text/javascript">
18550  */
18551  
18552
18553 /**
18554  * @class Roo.tree.TreeSorter
18555  * Provides sorting of nodes in a TreePanel
18556  * 
18557  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18558  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18559  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18560  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18561  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18562  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18563  * @constructor
18564  * @param {TreePanel} tree
18565  * @param {Object} config
18566  */
18567 Roo.tree.TreeSorter = function(tree, config){
18568     Roo.apply(this, config);
18569     tree.on("beforechildrenrendered", this.doSort, this);
18570     tree.on("append", this.updateSort, this);
18571     tree.on("insert", this.updateSort, this);
18572     
18573     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18574     var p = this.property || "text";
18575     var sortType = this.sortType;
18576     var fs = this.folderSort;
18577     var cs = this.caseSensitive === true;
18578     var leafAttr = this.leafAttr || 'leaf';
18579
18580     this.sortFn = function(n1, n2){
18581         if(fs){
18582             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18583                 return 1;
18584             }
18585             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18586                 return -1;
18587             }
18588         }
18589         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18590         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18591         if(v1 < v2){
18592                         return dsc ? +1 : -1;
18593                 }else if(v1 > v2){
18594                         return dsc ? -1 : +1;
18595         }else{
18596                 return 0;
18597         }
18598     };
18599 };
18600
18601 Roo.tree.TreeSorter.prototype = {
18602     doSort : function(node){
18603         node.sort(this.sortFn);
18604     },
18605     
18606     compareNodes : function(n1, n2){
18607         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18608     },
18609     
18610     updateSort : function(tree, node){
18611         if(node.childrenRendered){
18612             this.doSort.defer(1, this, [node]);
18613         }
18614     }
18615 };/*
18616  * Based on:
18617  * Ext JS Library 1.1.1
18618  * Copyright(c) 2006-2007, Ext JS, LLC.
18619  *
18620  * Originally Released Under LGPL - original licence link has changed is not relivant.
18621  *
18622  * Fork - LGPL
18623  * <script type="text/javascript">
18624  */
18625
18626 if(Roo.dd.DropZone){
18627     
18628 Roo.tree.TreeDropZone = function(tree, config){
18629     this.allowParentInsert = false;
18630     this.allowContainerDrop = false;
18631     this.appendOnly = false;
18632     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18633     this.tree = tree;
18634     this.lastInsertClass = "x-tree-no-status";
18635     this.dragOverData = {};
18636 };
18637
18638 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18639     ddGroup : "TreeDD",
18640     
18641     expandDelay : 1000,
18642     
18643     expandNode : function(node){
18644         if(node.hasChildNodes() && !node.isExpanded()){
18645             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18646         }
18647     },
18648     
18649     queueExpand : function(node){
18650         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18651     },
18652     
18653     cancelExpand : function(){
18654         if(this.expandProcId){
18655             clearTimeout(this.expandProcId);
18656             this.expandProcId = false;
18657         }
18658     },
18659     
18660     isValidDropPoint : function(n, pt, dd, e, data){
18661         if(!n || !data){ return false; }
18662         var targetNode = n.node;
18663         var dropNode = data.node;
18664         // default drop rules
18665         if(!(targetNode && targetNode.isTarget && pt)){
18666             return false;
18667         }
18668         if(pt == "append" && targetNode.allowChildren === false){
18669             return false;
18670         }
18671         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18672             return false;
18673         }
18674         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18675             return false;
18676         }
18677         // reuse the object
18678         var overEvent = this.dragOverData;
18679         overEvent.tree = this.tree;
18680         overEvent.target = targetNode;
18681         overEvent.data = data;
18682         overEvent.point = pt;
18683         overEvent.source = dd;
18684         overEvent.rawEvent = e;
18685         overEvent.dropNode = dropNode;
18686         overEvent.cancel = false;  
18687         var result = this.tree.fireEvent("nodedragover", overEvent);
18688         return overEvent.cancel === false && result !== false;
18689     },
18690     
18691     getDropPoint : function(e, n, dd){
18692         var tn = n.node;
18693         if(tn.isRoot){
18694             return tn.allowChildren !== false ? "append" : false; // always append for root
18695         }
18696         var dragEl = n.ddel;
18697         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18698         var y = Roo.lib.Event.getPageY(e);
18699         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18700         
18701         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18702         var noAppend = tn.allowChildren === false;
18703         if(this.appendOnly || tn.parentNode.allowChildren === false){
18704             return noAppend ? false : "append";
18705         }
18706         var noBelow = false;
18707         if(!this.allowParentInsert){
18708             noBelow = tn.hasChildNodes() && tn.isExpanded();
18709         }
18710         var q = (b - t) / (noAppend ? 2 : 3);
18711         if(y >= t && y < (t + q)){
18712             return "above";
18713         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18714             return "below";
18715         }else{
18716             return "append";
18717         }
18718     },
18719     
18720     onNodeEnter : function(n, dd, e, data){
18721         this.cancelExpand();
18722     },
18723     
18724     onNodeOver : function(n, dd, e, data){
18725         var pt = this.getDropPoint(e, n, dd);
18726         var node = n.node;
18727         
18728         // auto node expand check
18729         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18730             this.queueExpand(node);
18731         }else if(pt != "append"){
18732             this.cancelExpand();
18733         }
18734         
18735         // set the insert point style on the target node
18736         var returnCls = this.dropNotAllowed;
18737         if(this.isValidDropPoint(n, pt, dd, e, data)){
18738            if(pt){
18739                var el = n.ddel;
18740                var cls;
18741                if(pt == "above"){
18742                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18743                    cls = "x-tree-drag-insert-above";
18744                }else if(pt == "below"){
18745                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18746                    cls = "x-tree-drag-insert-below";
18747                }else{
18748                    returnCls = "x-tree-drop-ok-append";
18749                    cls = "x-tree-drag-append";
18750                }
18751                if(this.lastInsertClass != cls){
18752                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18753                    this.lastInsertClass = cls;
18754                }
18755            }
18756        }
18757        return returnCls;
18758     },
18759     
18760     onNodeOut : function(n, dd, e, data){
18761         this.cancelExpand();
18762         this.removeDropIndicators(n);
18763     },
18764     
18765     onNodeDrop : function(n, dd, e, data){
18766         var point = this.getDropPoint(e, n, dd);
18767         var targetNode = n.node;
18768         targetNode.ui.startDrop();
18769         if(!this.isValidDropPoint(n, point, dd, e, data)){
18770             targetNode.ui.endDrop();
18771             return false;
18772         }
18773         // first try to find the drop node
18774         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18775         var dropEvent = {
18776             tree : this.tree,
18777             target: targetNode,
18778             data: data,
18779             point: point,
18780             source: dd,
18781             rawEvent: e,
18782             dropNode: dropNode,
18783             cancel: !dropNode   
18784         };
18785         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18786         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18787             targetNode.ui.endDrop();
18788             return false;
18789         }
18790         // allow target changing
18791         targetNode = dropEvent.target;
18792         if(point == "append" && !targetNode.isExpanded()){
18793             targetNode.expand(false, null, function(){
18794                 this.completeDrop(dropEvent);
18795             }.createDelegate(this));
18796         }else{
18797             this.completeDrop(dropEvent);
18798         }
18799         return true;
18800     },
18801     
18802     completeDrop : function(de){
18803         var ns = de.dropNode, p = de.point, t = de.target;
18804         if(!(ns instanceof Array)){
18805             ns = [ns];
18806         }
18807         var n;
18808         for(var i = 0, len = ns.length; i < len; i++){
18809             n = ns[i];
18810             if(p == "above"){
18811                 t.parentNode.insertBefore(n, t);
18812             }else if(p == "below"){
18813                 t.parentNode.insertBefore(n, t.nextSibling);
18814             }else{
18815                 t.appendChild(n);
18816             }
18817         }
18818         n.ui.focus();
18819         if(this.tree.hlDrop){
18820             n.ui.highlight();
18821         }
18822         t.ui.endDrop();
18823         this.tree.fireEvent("nodedrop", de);
18824     },
18825     
18826     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
18827         if(this.tree.hlDrop){
18828             dropNode.ui.focus();
18829             dropNode.ui.highlight();
18830         }
18831         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
18832     },
18833     
18834     getTree : function(){
18835         return this.tree;
18836     },
18837     
18838     removeDropIndicators : function(n){
18839         if(n && n.ddel){
18840             var el = n.ddel;
18841             Roo.fly(el).removeClass([
18842                     "x-tree-drag-insert-above",
18843                     "x-tree-drag-insert-below",
18844                     "x-tree-drag-append"]);
18845             this.lastInsertClass = "_noclass";
18846         }
18847     },
18848     
18849     beforeDragDrop : function(target, e, id){
18850         this.cancelExpand();
18851         return true;
18852     },
18853     
18854     afterRepair : function(data){
18855         if(data && Roo.enableFx){
18856             data.node.ui.highlight();
18857         }
18858         this.hideProxy();
18859     }    
18860 });
18861
18862 }
18863 /*
18864  * Based on:
18865  * Ext JS Library 1.1.1
18866  * Copyright(c) 2006-2007, Ext JS, LLC.
18867  *
18868  * Originally Released Under LGPL - original licence link has changed is not relivant.
18869  *
18870  * Fork - LGPL
18871  * <script type="text/javascript">
18872  */
18873  
18874
18875 if(Roo.dd.DragZone){
18876 Roo.tree.TreeDragZone = function(tree, config){
18877     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
18878     this.tree = tree;
18879 };
18880
18881 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
18882     ddGroup : "TreeDD",
18883     
18884     onBeforeDrag : function(data, e){
18885         var n = data.node;
18886         return n && n.draggable && !n.disabled;
18887     },
18888     
18889     onInitDrag : function(e){
18890         var data = this.dragData;
18891         this.tree.getSelectionModel().select(data.node);
18892         this.proxy.update("");
18893         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
18894         this.tree.fireEvent("startdrag", this.tree, data.node, e);
18895     },
18896     
18897     getRepairXY : function(e, data){
18898         return data.node.ui.getDDRepairXY();
18899     },
18900     
18901     onEndDrag : function(data, e){
18902         this.tree.fireEvent("enddrag", this.tree, data.node, e);
18903     },
18904     
18905     onValidDrop : function(dd, e, id){
18906         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
18907         this.hideProxy();
18908     },
18909     
18910     beforeInvalidDrop : function(e, id){
18911         // this scrolls the original position back into view
18912         var sm = this.tree.getSelectionModel();
18913         sm.clearSelections();
18914         sm.select(this.dragData.node);
18915     }
18916 });
18917 }/*
18918  * Based on:
18919  * Ext JS Library 1.1.1
18920  * Copyright(c) 2006-2007, Ext JS, LLC.
18921  *
18922  * Originally Released Under LGPL - original licence link has changed is not relivant.
18923  *
18924  * Fork - LGPL
18925  * <script type="text/javascript">
18926  */
18927 /**
18928  * @class Roo.tree.TreeEditor
18929  * @extends Roo.Editor
18930  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
18931  * as the editor field.
18932  * @constructor
18933  * @param {TreePanel} tree
18934  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
18935  */
18936 Roo.tree.TreeEditor = function(tree, config){
18937     config = config || {};
18938     var field = config.events ? config : new Roo.form.TextField(config);
18939     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
18940
18941     this.tree = tree;
18942
18943     tree.on('beforeclick', this.beforeNodeClick, this);
18944     tree.getTreeEl().on('mousedown', this.hide, this);
18945     this.on('complete', this.updateNode, this);
18946     this.on('beforestartedit', this.fitToTree, this);
18947     this.on('startedit', this.bindScroll, this, {delay:10});
18948     this.on('specialkey', this.onSpecialKey, this);
18949 };
18950
18951 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
18952     /**
18953      * @cfg {String} alignment
18954      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
18955      */
18956     alignment: "l-l",
18957     // inherit
18958     autoSize: false,
18959     /**
18960      * @cfg {Boolean} hideEl
18961      * True to hide the bound element while the editor is displayed (defaults to false)
18962      */
18963     hideEl : false,
18964     /**
18965      * @cfg {String} cls
18966      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
18967      */
18968     cls: "x-small-editor x-tree-editor",
18969     /**
18970      * @cfg {Boolean} shim
18971      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
18972      */
18973     shim:false,
18974     // inherit
18975     shadow:"frame",
18976     /**
18977      * @cfg {Number} maxWidth
18978      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
18979      * the containing tree element's size, it will be automatically limited for you to the container width, taking
18980      * scroll and client offsets into account prior to each edit.
18981      */
18982     maxWidth: 250,
18983
18984     editDelay : 350,
18985
18986     // private
18987     fitToTree : function(ed, el){
18988         var td = this.tree.getTreeEl().dom, nd = el.dom;
18989         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
18990             td.scrollLeft = nd.offsetLeft;
18991         }
18992         var w = Math.min(
18993                 this.maxWidth,
18994                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
18995         this.setSize(w, '');
18996     },
18997
18998     // private
18999     triggerEdit : function(node){
19000         this.completeEdit();
19001         this.editNode = node;
19002         this.startEdit(node.ui.textNode, node.text);
19003     },
19004
19005     // private
19006     bindScroll : function(){
19007         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19008     },
19009
19010     // private
19011     beforeNodeClick : function(node, e){
19012         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19013         this.lastClick = new Date();
19014         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19015             e.stopEvent();
19016             this.triggerEdit(node);
19017             return false;
19018         }
19019     },
19020
19021     // private
19022     updateNode : function(ed, value){
19023         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19024         this.editNode.setText(value);
19025     },
19026
19027     // private
19028     onHide : function(){
19029         Roo.tree.TreeEditor.superclass.onHide.call(this);
19030         if(this.editNode){
19031             this.editNode.ui.focus();
19032         }
19033     },
19034
19035     // private
19036     onSpecialKey : function(field, e){
19037         var k = e.getKey();
19038         if(k == e.ESC){
19039             e.stopEvent();
19040             this.cancelEdit();
19041         }else if(k == e.ENTER && !e.hasModifier()){
19042             e.stopEvent();
19043             this.completeEdit();
19044         }
19045     }
19046 });//<Script type="text/javascript">
19047 /*
19048  * Based on:
19049  * Ext JS Library 1.1.1
19050  * Copyright(c) 2006-2007, Ext JS, LLC.
19051  *
19052  * Originally Released Under LGPL - original licence link has changed is not relivant.
19053  *
19054  * Fork - LGPL
19055  * <script type="text/javascript">
19056  */
19057  
19058 /**
19059  * Not documented??? - probably should be...
19060  */
19061
19062 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19063     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19064     
19065     renderElements : function(n, a, targetNode, bulkRender){
19066         //consel.log("renderElements?");
19067         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19068
19069         var t = n.getOwnerTree();
19070         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19071         
19072         var cols = t.columns;
19073         var bw = t.borderWidth;
19074         var c = cols[0];
19075         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19076          var cb = typeof a.checked == "boolean";
19077         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19078         var colcls = 'x-t-' + tid + '-c0';
19079         var buf = [
19080             '<li class="x-tree-node">',
19081             
19082                 
19083                 '<div class="x-tree-node-el ', a.cls,'">',
19084                     // extran...
19085                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19086                 
19087                 
19088                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19089                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19090                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19091                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19092                            (a.iconCls ? ' '+a.iconCls : ''),
19093                            '" unselectable="on" />',
19094                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19095                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19096                              
19097                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19098                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19099                             '<span unselectable="on" qtip="' + tx + '">',
19100                              tx,
19101                              '</span></a>' ,
19102                     '</div>',
19103                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19104                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19105                  ];
19106         for(var i = 1, len = cols.length; i < len; i++){
19107             c = cols[i];
19108             colcls = 'x-t-' + tid + '-c' +i;
19109             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19110             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19111                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19112                       "</div>");
19113          }
19114          
19115          buf.push(
19116             '</a>',
19117             '<div class="x-clear"></div></div>',
19118             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19119             "</li>");
19120         
19121         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19122             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19123                                 n.nextSibling.ui.getEl(), buf.join(""));
19124         }else{
19125             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19126         }
19127         var el = this.wrap.firstChild;
19128         this.elRow = el;
19129         this.elNode = el.firstChild;
19130         this.ranchor = el.childNodes[1];
19131         this.ctNode = this.wrap.childNodes[1];
19132         var cs = el.firstChild.childNodes;
19133         this.indentNode = cs[0];
19134         this.ecNode = cs[1];
19135         this.iconNode = cs[2];
19136         var index = 3;
19137         if(cb){
19138             this.checkbox = cs[3];
19139             index++;
19140         }
19141         this.anchor = cs[index];
19142         
19143         this.textNode = cs[index].firstChild;
19144         
19145         //el.on("click", this.onClick, this);
19146         //el.on("dblclick", this.onDblClick, this);
19147         
19148         
19149        // console.log(this);
19150     },
19151     initEvents : function(){
19152         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19153         
19154             
19155         var a = this.ranchor;
19156
19157         var el = Roo.get(a);
19158
19159         if(Roo.isOpera){ // opera render bug ignores the CSS
19160             el.setStyle("text-decoration", "none");
19161         }
19162
19163         el.on("click", this.onClick, this);
19164         el.on("dblclick", this.onDblClick, this);
19165         el.on("contextmenu", this.onContextMenu, this);
19166         
19167     },
19168     
19169     /*onSelectedChange : function(state){
19170         if(state){
19171             this.focus();
19172             this.addClass("x-tree-selected");
19173         }else{
19174             //this.blur();
19175             this.removeClass("x-tree-selected");
19176         }
19177     },*/
19178     addClass : function(cls){
19179         if(this.elRow){
19180             Roo.fly(this.elRow).addClass(cls);
19181         }
19182         
19183     },
19184     
19185     
19186     removeClass : function(cls){
19187         if(this.elRow){
19188             Roo.fly(this.elRow).removeClass(cls);
19189         }
19190     }
19191
19192     
19193     
19194 });//<Script type="text/javascript">
19195
19196 /*
19197  * Based on:
19198  * Ext JS Library 1.1.1
19199  * Copyright(c) 2006-2007, Ext JS, LLC.
19200  *
19201  * Originally Released Under LGPL - original licence link has changed is not relivant.
19202  *
19203  * Fork - LGPL
19204  * <script type="text/javascript">
19205  */
19206  
19207
19208 /**
19209  * @class Roo.tree.ColumnTree
19210  * @extends Roo.data.TreePanel
19211  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19212  * @cfg {int} borderWidth  compined right/left border allowance
19213  * @constructor
19214  * @param {String/HTMLElement/Element} el The container element
19215  * @param {Object} config
19216  */
19217 Roo.tree.ColumnTree =  function(el, config)
19218 {
19219    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19220    this.addEvents({
19221         /**
19222         * @event resize
19223         * Fire this event on a container when it resizes
19224         * @param {int} w Width
19225         * @param {int} h Height
19226         */
19227        "resize" : true
19228     });
19229     this.on('resize', this.onResize, this);
19230 };
19231
19232 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19233     //lines:false,
19234     
19235     
19236     borderWidth: Roo.isBorderBox ? 0 : 2, 
19237     headEls : false,
19238     
19239     render : function(){
19240         // add the header.....
19241        
19242         Roo.tree.ColumnTree.superclass.render.apply(this);
19243         
19244         this.el.addClass('x-column-tree');
19245         
19246         this.headers = this.el.createChild(
19247             {cls:'x-tree-headers'},this.innerCt.dom);
19248    
19249         var cols = this.columns, c;
19250         var totalWidth = 0;
19251         this.headEls = [];
19252         var  len = cols.length;
19253         for(var i = 0; i < len; i++){
19254              c = cols[i];
19255              totalWidth += c.width;
19256             this.headEls.push(this.headers.createChild({
19257                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19258                  cn: {
19259                      cls:'x-tree-hd-text',
19260                      html: c.header
19261                  },
19262                  style:'width:'+(c.width-this.borderWidth)+'px;'
19263              }));
19264         }
19265         this.headers.createChild({cls:'x-clear'});
19266         // prevent floats from wrapping when clipped
19267         this.headers.setWidth(totalWidth);
19268         //this.innerCt.setWidth(totalWidth);
19269         this.innerCt.setStyle({ overflow: 'auto' });
19270         this.onResize(this.width, this.height);
19271              
19272         
19273     },
19274     onResize : function(w,h)
19275     {
19276         this.height = h;
19277         this.width = w;
19278         // resize cols..
19279         this.innerCt.setWidth(this.width);
19280         this.innerCt.setHeight(this.height-20);
19281         
19282         // headers...
19283         var cols = this.columns, c;
19284         var totalWidth = 0;
19285         var expEl = false;
19286         var len = cols.length;
19287         for(var i = 0; i < len; i++){
19288             c = cols[i];
19289             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19290                 // it's the expander..
19291                 expEl  = this.headEls[i];
19292                 continue;
19293             }
19294             totalWidth += c.width;
19295             
19296         }
19297         if (expEl) {
19298             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19299         }
19300         this.headers.setWidth(w-20);
19301
19302         
19303         
19304         
19305     }
19306 });
19307 /*
19308  * Based on:
19309  * Ext JS Library 1.1.1
19310  * Copyright(c) 2006-2007, Ext JS, LLC.
19311  *
19312  * Originally Released Under LGPL - original licence link has changed is not relivant.
19313  *
19314  * Fork - LGPL
19315  * <script type="text/javascript">
19316  */
19317  
19318 /**
19319  * @class Roo.menu.Menu
19320  * @extends Roo.util.Observable
19321  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19322  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19323  * @constructor
19324  * Creates a new Menu
19325  * @param {Object} config Configuration options
19326  */
19327 Roo.menu.Menu = function(config){
19328     Roo.apply(this, config);
19329     this.id = this.id || Roo.id();
19330     this.addEvents({
19331         /**
19332          * @event beforeshow
19333          * Fires before this menu is displayed
19334          * @param {Roo.menu.Menu} this
19335          */
19336         beforeshow : true,
19337         /**
19338          * @event beforehide
19339          * Fires before this menu is hidden
19340          * @param {Roo.menu.Menu} this
19341          */
19342         beforehide : true,
19343         /**
19344          * @event show
19345          * Fires after this menu is displayed
19346          * @param {Roo.menu.Menu} this
19347          */
19348         show : true,
19349         /**
19350          * @event hide
19351          * Fires after this menu is hidden
19352          * @param {Roo.menu.Menu} this
19353          */
19354         hide : true,
19355         /**
19356          * @event click
19357          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19358          * @param {Roo.menu.Menu} this
19359          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19360          * @param {Roo.EventObject} e
19361          */
19362         click : true,
19363         /**
19364          * @event mouseover
19365          * Fires when the mouse is hovering over this menu
19366          * @param {Roo.menu.Menu} this
19367          * @param {Roo.EventObject} e
19368          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19369          */
19370         mouseover : true,
19371         /**
19372          * @event mouseout
19373          * Fires when the mouse exits this menu
19374          * @param {Roo.menu.Menu} this
19375          * @param {Roo.EventObject} e
19376          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19377          */
19378         mouseout : true,
19379         /**
19380          * @event itemclick
19381          * Fires when a menu item contained in this menu is clicked
19382          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19383          * @param {Roo.EventObject} e
19384          */
19385         itemclick: true
19386     });
19387     if (this.registerMenu) {
19388         Roo.menu.MenuMgr.register(this);
19389     }
19390     
19391     var mis = this.items;
19392     this.items = new Roo.util.MixedCollection();
19393     if(mis){
19394         this.add.apply(this, mis);
19395     }
19396 };
19397
19398 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19399     /**
19400      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19401      */
19402     minWidth : 120,
19403     /**
19404      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19405      * for bottom-right shadow (defaults to "sides")
19406      */
19407     shadow : "sides",
19408     /**
19409      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19410      * this menu (defaults to "tl-tr?")
19411      */
19412     subMenuAlign : "tl-tr?",
19413     /**
19414      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19415      * relative to its element of origin (defaults to "tl-bl?")
19416      */
19417     defaultAlign : "tl-bl?",
19418     /**
19419      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19420      */
19421     allowOtherMenus : false,
19422     /**
19423      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19424      */
19425     registerMenu : true,
19426
19427     hidden:true,
19428
19429     // private
19430     render : function(){
19431         if(this.el){
19432             return;
19433         }
19434         var el = this.el = new Roo.Layer({
19435             cls: "x-menu",
19436             shadow:this.shadow,
19437             constrain: false,
19438             parentEl: this.parentEl || document.body,
19439             zindex:15000
19440         });
19441
19442         this.keyNav = new Roo.menu.MenuNav(this);
19443
19444         if(this.plain){
19445             el.addClass("x-menu-plain");
19446         }
19447         if(this.cls){
19448             el.addClass(this.cls);
19449         }
19450         // generic focus element
19451         this.focusEl = el.createChild({
19452             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19453         });
19454         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19455         ul.on("click", this.onClick, this);
19456         ul.on("mouseover", this.onMouseOver, this);
19457         ul.on("mouseout", this.onMouseOut, this);
19458         this.items.each(function(item){
19459             var li = document.createElement("li");
19460             li.className = "x-menu-list-item";
19461             ul.dom.appendChild(li);
19462             item.render(li, this);
19463         }, this);
19464         this.ul = ul;
19465         this.autoWidth();
19466     },
19467
19468     // private
19469     autoWidth : function(){
19470         var el = this.el, ul = this.ul;
19471         if(!el){
19472             return;
19473         }
19474         var w = this.width;
19475         if(w){
19476             el.setWidth(w);
19477         }else if(Roo.isIE){
19478             el.setWidth(this.minWidth);
19479             var t = el.dom.offsetWidth; // force recalc
19480             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19481         }
19482     },
19483
19484     // private
19485     delayAutoWidth : function(){
19486         if(this.rendered){
19487             if(!this.awTask){
19488                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19489             }
19490             this.awTask.delay(20);
19491         }
19492     },
19493
19494     // private
19495     findTargetItem : function(e){
19496         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19497         if(t && t.menuItemId){
19498             return this.items.get(t.menuItemId);
19499         }
19500     },
19501
19502     // private
19503     onClick : function(e){
19504         var t;
19505         if(t = this.findTargetItem(e)){
19506             t.onClick(e);
19507             this.fireEvent("click", this, t, e);
19508         }
19509     },
19510
19511     // private
19512     setActiveItem : function(item, autoExpand){
19513         if(item != this.activeItem){
19514             if(this.activeItem){
19515                 this.activeItem.deactivate();
19516             }
19517             this.activeItem = item;
19518             item.activate(autoExpand);
19519         }else if(autoExpand){
19520             item.expandMenu();
19521         }
19522     },
19523
19524     // private
19525     tryActivate : function(start, step){
19526         var items = this.items;
19527         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19528             var item = items.get(i);
19529             if(!item.disabled && item.canActivate){
19530                 this.setActiveItem(item, false);
19531                 return item;
19532             }
19533         }
19534         return false;
19535     },
19536
19537     // private
19538     onMouseOver : function(e){
19539         var t;
19540         if(t = this.findTargetItem(e)){
19541             if(t.canActivate && !t.disabled){
19542                 this.setActiveItem(t, true);
19543             }
19544         }
19545         this.fireEvent("mouseover", this, e, t);
19546     },
19547
19548     // private
19549     onMouseOut : function(e){
19550         var t;
19551         if(t = this.findTargetItem(e)){
19552             if(t == this.activeItem && t.shouldDeactivate(e)){
19553                 this.activeItem.deactivate();
19554                 delete this.activeItem;
19555             }
19556         }
19557         this.fireEvent("mouseout", this, e, t);
19558     },
19559
19560     /**
19561      * Read-only.  Returns true if the menu is currently displayed, else false.
19562      * @type Boolean
19563      */
19564     isVisible : function(){
19565         return this.el && !this.hidden;
19566     },
19567
19568     /**
19569      * Displays this menu relative to another element
19570      * @param {String/HTMLElement/Roo.Element} element The element to align to
19571      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19572      * the element (defaults to this.defaultAlign)
19573      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19574      */
19575     show : function(el, pos, parentMenu){
19576         this.parentMenu = parentMenu;
19577         if(!this.el){
19578             this.render();
19579         }
19580         this.fireEvent("beforeshow", this);
19581         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19582     },
19583
19584     /**
19585      * Displays this menu at a specific xy position
19586      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19587      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19588      */
19589     showAt : function(xy, parentMenu, /* private: */_e){
19590         this.parentMenu = parentMenu;
19591         if(!this.el){
19592             this.render();
19593         }
19594         if(_e !== false){
19595             this.fireEvent("beforeshow", this);
19596             xy = this.el.adjustForConstraints(xy);
19597         }
19598         this.el.setXY(xy);
19599         this.el.show();
19600         this.hidden = false;
19601         this.focus();
19602         this.fireEvent("show", this);
19603     },
19604
19605     focus : function(){
19606         if(!this.hidden){
19607             this.doFocus.defer(50, this);
19608         }
19609     },
19610
19611     doFocus : function(){
19612         if(!this.hidden){
19613             this.focusEl.focus();
19614         }
19615     },
19616
19617     /**
19618      * Hides this menu and optionally all parent menus
19619      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19620      */
19621     hide : function(deep){
19622         if(this.el && this.isVisible()){
19623             this.fireEvent("beforehide", this);
19624             if(this.activeItem){
19625                 this.activeItem.deactivate();
19626                 this.activeItem = null;
19627             }
19628             this.el.hide();
19629             this.hidden = true;
19630             this.fireEvent("hide", this);
19631         }
19632         if(deep === true && this.parentMenu){
19633             this.parentMenu.hide(true);
19634         }
19635     },
19636
19637     /**
19638      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19639      * Any of the following are valid:
19640      * <ul>
19641      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19642      * <li>An HTMLElement object which will be converted to a menu item</li>
19643      * <li>A menu item config object that will be created as a new menu item</li>
19644      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19645      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19646      * </ul>
19647      * Usage:
19648      * <pre><code>
19649 // Create the menu
19650 var menu = new Roo.menu.Menu();
19651
19652 // Create a menu item to add by reference
19653 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19654
19655 // Add a bunch of items at once using different methods.
19656 // Only the last item added will be returned.
19657 var item = menu.add(
19658     menuItem,                // add existing item by ref
19659     'Dynamic Item',          // new TextItem
19660     '-',                     // new separator
19661     { text: 'Config Item' }  // new item by config
19662 );
19663 </code></pre>
19664      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19665      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19666      */
19667     add : function(){
19668         var a = arguments, l = a.length, item;
19669         for(var i = 0; i < l; i++){
19670             var el = a[i];
19671             if ((typeof(el) == "object") && el.xtype && el.xns) {
19672                 el = Roo.factory(el, Roo.menu);
19673             }
19674             
19675             if(el.render){ // some kind of Item
19676                 item = this.addItem(el);
19677             }else if(typeof el == "string"){ // string
19678                 if(el == "separator" || el == "-"){
19679                     item = this.addSeparator();
19680                 }else{
19681                     item = this.addText(el);
19682                 }
19683             }else if(el.tagName || el.el){ // element
19684                 item = this.addElement(el);
19685             }else if(typeof el == "object"){ // must be menu item config?
19686                 item = this.addMenuItem(el);
19687             }
19688         }
19689         return item;
19690     },
19691
19692     /**
19693      * Returns this menu's underlying {@link Roo.Element} object
19694      * @return {Roo.Element} The element
19695      */
19696     getEl : function(){
19697         if(!this.el){
19698             this.render();
19699         }
19700         return this.el;
19701     },
19702
19703     /**
19704      * Adds a separator bar to the menu
19705      * @return {Roo.menu.Item} The menu item that was added
19706      */
19707     addSeparator : function(){
19708         return this.addItem(new Roo.menu.Separator());
19709     },
19710
19711     /**
19712      * Adds an {@link Roo.Element} object to the menu
19713      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19714      * @return {Roo.menu.Item} The menu item that was added
19715      */
19716     addElement : function(el){
19717         return this.addItem(new Roo.menu.BaseItem(el));
19718     },
19719
19720     /**
19721      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19722      * @param {Roo.menu.Item} item The menu item to add
19723      * @return {Roo.menu.Item} The menu item that was added
19724      */
19725     addItem : function(item){
19726         this.items.add(item);
19727         if(this.ul){
19728             var li = document.createElement("li");
19729             li.className = "x-menu-list-item";
19730             this.ul.dom.appendChild(li);
19731             item.render(li, this);
19732             this.delayAutoWidth();
19733         }
19734         return item;
19735     },
19736
19737     /**
19738      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19739      * @param {Object} config A MenuItem config object
19740      * @return {Roo.menu.Item} The menu item that was added
19741      */
19742     addMenuItem : function(config){
19743         if(!(config instanceof Roo.menu.Item)){
19744             if(typeof config.checked == "boolean"){ // must be check menu item config?
19745                 config = new Roo.menu.CheckItem(config);
19746             }else{
19747                 config = new Roo.menu.Item(config);
19748             }
19749         }
19750         return this.addItem(config);
19751     },
19752
19753     /**
19754      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19755      * @param {String} text The text to display in the menu item
19756      * @return {Roo.menu.Item} The menu item that was added
19757      */
19758     addText : function(text){
19759         return this.addItem(new Roo.menu.TextItem({ text : text }));
19760     },
19761
19762     /**
19763      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
19764      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
19765      * @param {Roo.menu.Item} item The menu item to add
19766      * @return {Roo.menu.Item} The menu item that was added
19767      */
19768     insert : function(index, item){
19769         this.items.insert(index, item);
19770         if(this.ul){
19771             var li = document.createElement("li");
19772             li.className = "x-menu-list-item";
19773             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
19774             item.render(li, this);
19775             this.delayAutoWidth();
19776         }
19777         return item;
19778     },
19779
19780     /**
19781      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
19782      * @param {Roo.menu.Item} item The menu item to remove
19783      */
19784     remove : function(item){
19785         this.items.removeKey(item.id);
19786         item.destroy();
19787     },
19788
19789     /**
19790      * Removes and destroys all items in the menu
19791      */
19792     removeAll : function(){
19793         var f;
19794         while(f = this.items.first()){
19795             this.remove(f);
19796         }
19797     }
19798 });
19799
19800 // MenuNav is a private utility class used internally by the Menu
19801 Roo.menu.MenuNav = function(menu){
19802     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
19803     this.scope = this.menu = menu;
19804 };
19805
19806 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
19807     doRelay : function(e, h){
19808         var k = e.getKey();
19809         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
19810             this.menu.tryActivate(0, 1);
19811             return false;
19812         }
19813         return h.call(this.scope || this, e, this.menu);
19814     },
19815
19816     up : function(e, m){
19817         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
19818             m.tryActivate(m.items.length-1, -1);
19819         }
19820     },
19821
19822     down : function(e, m){
19823         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
19824             m.tryActivate(0, 1);
19825         }
19826     },
19827
19828     right : function(e, m){
19829         if(m.activeItem){
19830             m.activeItem.expandMenu(true);
19831         }
19832     },
19833
19834     left : function(e, m){
19835         m.hide();
19836         if(m.parentMenu && m.parentMenu.activeItem){
19837             m.parentMenu.activeItem.activate();
19838         }
19839     },
19840
19841     enter : function(e, m){
19842         if(m.activeItem){
19843             e.stopPropagation();
19844             m.activeItem.onClick(e);
19845             m.fireEvent("click", this, m.activeItem);
19846             return true;
19847         }
19848     }
19849 });/*
19850  * Based on:
19851  * Ext JS Library 1.1.1
19852  * Copyright(c) 2006-2007, Ext JS, LLC.
19853  *
19854  * Originally Released Under LGPL - original licence link has changed is not relivant.
19855  *
19856  * Fork - LGPL
19857  * <script type="text/javascript">
19858  */
19859  
19860 /**
19861  * @class Roo.menu.MenuMgr
19862  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
19863  * @singleton
19864  */
19865 Roo.menu.MenuMgr = function(){
19866    var menus, active, groups = {}, attached = false, lastShow = new Date();
19867
19868    // private - called when first menu is created
19869    function init(){
19870        menus = {};
19871        active = new Roo.util.MixedCollection();
19872        Roo.get(document).addKeyListener(27, function(){
19873            if(active.length > 0){
19874                hideAll();
19875            }
19876        });
19877    }
19878
19879    // private
19880    function hideAll(){
19881        if(active && active.length > 0){
19882            var c = active.clone();
19883            c.each(function(m){
19884                m.hide();
19885            });
19886        }
19887    }
19888
19889    // private
19890    function onHide(m){
19891        active.remove(m);
19892        if(active.length < 1){
19893            Roo.get(document).un("mousedown", onMouseDown);
19894            attached = false;
19895        }
19896    }
19897
19898    // private
19899    function onShow(m){
19900        var last = active.last();
19901        lastShow = new Date();
19902        active.add(m);
19903        if(!attached){
19904            Roo.get(document).on("mousedown", onMouseDown);
19905            attached = true;
19906        }
19907        if(m.parentMenu){
19908           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
19909           m.parentMenu.activeChild = m;
19910        }else if(last && last.isVisible()){
19911           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
19912        }
19913    }
19914
19915    // private
19916    function onBeforeHide(m){
19917        if(m.activeChild){
19918            m.activeChild.hide();
19919        }
19920        if(m.autoHideTimer){
19921            clearTimeout(m.autoHideTimer);
19922            delete m.autoHideTimer;
19923        }
19924    }
19925
19926    // private
19927    function onBeforeShow(m){
19928        var pm = m.parentMenu;
19929        if(!pm && !m.allowOtherMenus){
19930            hideAll();
19931        }else if(pm && pm.activeChild && active != m){
19932            pm.activeChild.hide();
19933        }
19934    }
19935
19936    // private
19937    function onMouseDown(e){
19938        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
19939            hideAll();
19940        }
19941    }
19942
19943    // private
19944    function onBeforeCheck(mi, state){
19945        if(state){
19946            var g = groups[mi.group];
19947            for(var i = 0, l = g.length; i < l; i++){
19948                if(g[i] != mi){
19949                    g[i].setChecked(false);
19950                }
19951            }
19952        }
19953    }
19954
19955    return {
19956
19957        /**
19958         * Hides all menus that are currently visible
19959         */
19960        hideAll : function(){
19961             hideAll();  
19962        },
19963
19964        // private
19965        register : function(menu){
19966            if(!menus){
19967                init();
19968            }
19969            menus[menu.id] = menu;
19970            menu.on("beforehide", onBeforeHide);
19971            menu.on("hide", onHide);
19972            menu.on("beforeshow", onBeforeShow);
19973            menu.on("show", onShow);
19974            var g = menu.group;
19975            if(g && menu.events["checkchange"]){
19976                if(!groups[g]){
19977                    groups[g] = [];
19978                }
19979                groups[g].push(menu);
19980                menu.on("checkchange", onCheck);
19981            }
19982        },
19983
19984         /**
19985          * Returns a {@link Roo.menu.Menu} object
19986          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
19987          * be used to generate and return a new Menu instance.
19988          */
19989        get : function(menu){
19990            if(typeof menu == "string"){ // menu id
19991                return menus[menu];
19992            }else if(menu.events){  // menu instance
19993                return menu;
19994            }else if(typeof menu.length == 'number'){ // array of menu items?
19995                return new Roo.menu.Menu({items:menu});
19996            }else{ // otherwise, must be a config
19997                return new Roo.menu.Menu(menu);
19998            }
19999        },
20000
20001        // private
20002        unregister : function(menu){
20003            delete menus[menu.id];
20004            menu.un("beforehide", onBeforeHide);
20005            menu.un("hide", onHide);
20006            menu.un("beforeshow", onBeforeShow);
20007            menu.un("show", onShow);
20008            var g = menu.group;
20009            if(g && menu.events["checkchange"]){
20010                groups[g].remove(menu);
20011                menu.un("checkchange", onCheck);
20012            }
20013        },
20014
20015        // private
20016        registerCheckable : function(menuItem){
20017            var g = menuItem.group;
20018            if(g){
20019                if(!groups[g]){
20020                    groups[g] = [];
20021                }
20022                groups[g].push(menuItem);
20023                menuItem.on("beforecheckchange", onBeforeCheck);
20024            }
20025        },
20026
20027        // private
20028        unregisterCheckable : function(menuItem){
20029            var g = menuItem.group;
20030            if(g){
20031                groups[g].remove(menuItem);
20032                menuItem.un("beforecheckchange", onBeforeCheck);
20033            }
20034        }
20035    };
20036 }();/*
20037  * Based on:
20038  * Ext JS Library 1.1.1
20039  * Copyright(c) 2006-2007, Ext JS, LLC.
20040  *
20041  * Originally Released Under LGPL - original licence link has changed is not relivant.
20042  *
20043  * Fork - LGPL
20044  * <script type="text/javascript">
20045  */
20046  
20047
20048 /**
20049  * @class Roo.menu.BaseItem
20050  * @extends Roo.Component
20051  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20052  * management and base configuration options shared by all menu components.
20053  * @constructor
20054  * Creates a new BaseItem
20055  * @param {Object} config Configuration options
20056  */
20057 Roo.menu.BaseItem = function(config){
20058     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20059
20060     this.addEvents({
20061         /**
20062          * @event click
20063          * Fires when this item is clicked
20064          * @param {Roo.menu.BaseItem} this
20065          * @param {Roo.EventObject} e
20066          */
20067         click: true,
20068         /**
20069          * @event activate
20070          * Fires when this item is activated
20071          * @param {Roo.menu.BaseItem} this
20072          */
20073         activate : true,
20074         /**
20075          * @event deactivate
20076          * Fires when this item is deactivated
20077          * @param {Roo.menu.BaseItem} this
20078          */
20079         deactivate : true
20080     });
20081
20082     if(this.handler){
20083         this.on("click", this.handler, this.scope, true);
20084     }
20085 };
20086
20087 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20088     /**
20089      * @cfg {Function} handler
20090      * A function that will handle the click event of this menu item (defaults to undefined)
20091      */
20092     /**
20093      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20094      */
20095     canActivate : false,
20096     /**
20097      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20098      */
20099     activeClass : "x-menu-item-active",
20100     /**
20101      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20102      */
20103     hideOnClick : true,
20104     /**
20105      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20106      */
20107     hideDelay : 100,
20108
20109     // private
20110     ctype: "Roo.menu.BaseItem",
20111
20112     // private
20113     actionMode : "container",
20114
20115     // private
20116     render : function(container, parentMenu){
20117         this.parentMenu = parentMenu;
20118         Roo.menu.BaseItem.superclass.render.call(this, container);
20119         this.container.menuItemId = this.id;
20120     },
20121
20122     // private
20123     onRender : function(container, position){
20124         this.el = Roo.get(this.el);
20125         container.dom.appendChild(this.el.dom);
20126     },
20127
20128     // private
20129     onClick : function(e){
20130         if(!this.disabled && this.fireEvent("click", this, e) !== false
20131                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20132             this.handleClick(e);
20133         }else{
20134             e.stopEvent();
20135         }
20136     },
20137
20138     // private
20139     activate : function(){
20140         if(this.disabled){
20141             return false;
20142         }
20143         var li = this.container;
20144         li.addClass(this.activeClass);
20145         this.region = li.getRegion().adjust(2, 2, -2, -2);
20146         this.fireEvent("activate", this);
20147         return true;
20148     },
20149
20150     // private
20151     deactivate : function(){
20152         this.container.removeClass(this.activeClass);
20153         this.fireEvent("deactivate", this);
20154     },
20155
20156     // private
20157     shouldDeactivate : function(e){
20158         return !this.region || !this.region.contains(e.getPoint());
20159     },
20160
20161     // private
20162     handleClick : function(e){
20163         if(this.hideOnClick){
20164             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20165         }
20166     },
20167
20168     // private
20169     expandMenu : function(autoActivate){
20170         // do nothing
20171     },
20172
20173     // private
20174     hideMenu : function(){
20175         // do nothing
20176     }
20177 });/*
20178  * Based on:
20179  * Ext JS Library 1.1.1
20180  * Copyright(c) 2006-2007, Ext JS, LLC.
20181  *
20182  * Originally Released Under LGPL - original licence link has changed is not relivant.
20183  *
20184  * Fork - LGPL
20185  * <script type="text/javascript">
20186  */
20187  
20188 /**
20189  * @class Roo.menu.Adapter
20190  * @extends Roo.menu.BaseItem
20191  * 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.
20192  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20193  * @constructor
20194  * Creates a new Adapter
20195  * @param {Object} config Configuration options
20196  */
20197 Roo.menu.Adapter = function(component, config){
20198     Roo.menu.Adapter.superclass.constructor.call(this, config);
20199     this.component = component;
20200 };
20201 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20202     // private
20203     canActivate : true,
20204
20205     // private
20206     onRender : function(container, position){
20207         this.component.render(container);
20208         this.el = this.component.getEl();
20209     },
20210
20211     // private
20212     activate : function(){
20213         if(this.disabled){
20214             return false;
20215         }
20216         this.component.focus();
20217         this.fireEvent("activate", this);
20218         return true;
20219     },
20220
20221     // private
20222     deactivate : function(){
20223         this.fireEvent("deactivate", this);
20224     },
20225
20226     // private
20227     disable : function(){
20228         this.component.disable();
20229         Roo.menu.Adapter.superclass.disable.call(this);
20230     },
20231
20232     // private
20233     enable : function(){
20234         this.component.enable();
20235         Roo.menu.Adapter.superclass.enable.call(this);
20236     }
20237 });/*
20238  * Based on:
20239  * Ext JS Library 1.1.1
20240  * Copyright(c) 2006-2007, Ext JS, LLC.
20241  *
20242  * Originally Released Under LGPL - original licence link has changed is not relivant.
20243  *
20244  * Fork - LGPL
20245  * <script type="text/javascript">
20246  */
20247
20248 /**
20249  * @class Roo.menu.TextItem
20250  * @extends Roo.menu.BaseItem
20251  * Adds a static text string to a menu, usually used as either a heading or group separator.
20252  * Note: old style constructor with text is still supported.
20253  * 
20254  * @constructor
20255  * Creates a new TextItem
20256  * @param {Object} cfg Configuration
20257  */
20258 Roo.menu.TextItem = function(cfg){
20259     if (typeof(cfg) == 'string') {
20260         this.text = cfg;
20261     } else {
20262         Roo.apply(this,cfg);
20263     }
20264     
20265     Roo.menu.TextItem.superclass.constructor.call(this);
20266 };
20267
20268 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20269     /**
20270      * @cfg {Boolean} text Text to show on item.
20271      */
20272     text : '',
20273     
20274     /**
20275      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20276      */
20277     hideOnClick : false,
20278     /**
20279      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20280      */
20281     itemCls : "x-menu-text",
20282
20283     // private
20284     onRender : function(){
20285         var s = document.createElement("span");
20286         s.className = this.itemCls;
20287         s.innerHTML = this.text;
20288         this.el = s;
20289         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20290     }
20291 });/*
20292  * Based on:
20293  * Ext JS Library 1.1.1
20294  * Copyright(c) 2006-2007, Ext JS, LLC.
20295  *
20296  * Originally Released Under LGPL - original licence link has changed is not relivant.
20297  *
20298  * Fork - LGPL
20299  * <script type="text/javascript">
20300  */
20301
20302 /**
20303  * @class Roo.menu.Separator
20304  * @extends Roo.menu.BaseItem
20305  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20306  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20307  * @constructor
20308  * @param {Object} config Configuration options
20309  */
20310 Roo.menu.Separator = function(config){
20311     Roo.menu.Separator.superclass.constructor.call(this, config);
20312 };
20313
20314 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20315     /**
20316      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20317      */
20318     itemCls : "x-menu-sep",
20319     /**
20320      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20321      */
20322     hideOnClick : false,
20323
20324     // private
20325     onRender : function(li){
20326         var s = document.createElement("span");
20327         s.className = this.itemCls;
20328         s.innerHTML = "&#160;";
20329         this.el = s;
20330         li.addClass("x-menu-sep-li");
20331         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20332     }
20333 });/*
20334  * Based on:
20335  * Ext JS Library 1.1.1
20336  * Copyright(c) 2006-2007, Ext JS, LLC.
20337  *
20338  * Originally Released Under LGPL - original licence link has changed is not relivant.
20339  *
20340  * Fork - LGPL
20341  * <script type="text/javascript">
20342  */
20343 /**
20344  * @class Roo.menu.Item
20345  * @extends Roo.menu.BaseItem
20346  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20347  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20348  * activation and click handling.
20349  * @constructor
20350  * Creates a new Item
20351  * @param {Object} config Configuration options
20352  */
20353 Roo.menu.Item = function(config){
20354     Roo.menu.Item.superclass.constructor.call(this, config);
20355     if(this.menu){
20356         this.menu = Roo.menu.MenuMgr.get(this.menu);
20357     }
20358 };
20359 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20360     
20361     /**
20362      * @cfg {String} text
20363      * The text to show on the menu item.
20364      */
20365     text: '',
20366      /**
20367      * @cfg {String} HTML to render in menu
20368      * The text to show on the menu item (HTML version).
20369      */
20370     html: '',
20371     /**
20372      * @cfg {String} icon
20373      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20374      */
20375     icon: undefined,
20376     /**
20377      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20378      */
20379     itemCls : "x-menu-item",
20380     /**
20381      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20382      */
20383     canActivate : true,
20384     /**
20385      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20386      */
20387     showDelay: 200,
20388     // doc'd in BaseItem
20389     hideDelay: 200,
20390
20391     // private
20392     ctype: "Roo.menu.Item",
20393     
20394     // private
20395     onRender : function(container, position){
20396         var el = document.createElement("a");
20397         el.hideFocus = true;
20398         el.unselectable = "on";
20399         el.href = this.href || "#";
20400         if(this.hrefTarget){
20401             el.target = this.hrefTarget;
20402         }
20403         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20404         
20405         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20406         
20407         el.innerHTML = String.format(
20408                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20409                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20410         this.el = el;
20411         Roo.menu.Item.superclass.onRender.call(this, container, position);
20412     },
20413
20414     /**
20415      * Sets the text to display in this menu item
20416      * @param {String} text The text to display
20417      * @param {Boolean} isHTML true to indicate text is pure html.
20418      */
20419     setText : function(text, isHTML){
20420         if (isHTML) {
20421             this.html = text;
20422         } else {
20423             this.text = text;
20424             this.html = '';
20425         }
20426         if(this.rendered){
20427             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20428      
20429             this.el.update(String.format(
20430                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20431                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20432             this.parentMenu.autoWidth();
20433         }
20434     },
20435
20436     // private
20437     handleClick : function(e){
20438         if(!this.href){ // if no link defined, stop the event automatically
20439             e.stopEvent();
20440         }
20441         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20442     },
20443
20444     // private
20445     activate : function(autoExpand){
20446         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20447             this.focus();
20448             if(autoExpand){
20449                 this.expandMenu();
20450             }
20451         }
20452         return true;
20453     },
20454
20455     // private
20456     shouldDeactivate : function(e){
20457         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20458             if(this.menu && this.menu.isVisible()){
20459                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20460             }
20461             return true;
20462         }
20463         return false;
20464     },
20465
20466     // private
20467     deactivate : function(){
20468         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20469         this.hideMenu();
20470     },
20471
20472     // private
20473     expandMenu : function(autoActivate){
20474         if(!this.disabled && this.menu){
20475             clearTimeout(this.hideTimer);
20476             delete this.hideTimer;
20477             if(!this.menu.isVisible() && !this.showTimer){
20478                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20479             }else if (this.menu.isVisible() && autoActivate){
20480                 this.menu.tryActivate(0, 1);
20481             }
20482         }
20483     },
20484
20485     // private
20486     deferExpand : function(autoActivate){
20487         delete this.showTimer;
20488         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20489         if(autoActivate){
20490             this.menu.tryActivate(0, 1);
20491         }
20492     },
20493
20494     // private
20495     hideMenu : function(){
20496         clearTimeout(this.showTimer);
20497         delete this.showTimer;
20498         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20499             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20500         }
20501     },
20502
20503     // private
20504     deferHide : function(){
20505         delete this.hideTimer;
20506         this.menu.hide();
20507     }
20508 });/*
20509  * Based on:
20510  * Ext JS Library 1.1.1
20511  * Copyright(c) 2006-2007, Ext JS, LLC.
20512  *
20513  * Originally Released Under LGPL - original licence link has changed is not relivant.
20514  *
20515  * Fork - LGPL
20516  * <script type="text/javascript">
20517  */
20518  
20519 /**
20520  * @class Roo.menu.CheckItem
20521  * @extends Roo.menu.Item
20522  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20523  * @constructor
20524  * Creates a new CheckItem
20525  * @param {Object} config Configuration options
20526  */
20527 Roo.menu.CheckItem = function(config){
20528     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20529     this.addEvents({
20530         /**
20531          * @event beforecheckchange
20532          * Fires before the checked value is set, providing an opportunity to cancel if needed
20533          * @param {Roo.menu.CheckItem} this
20534          * @param {Boolean} checked The new checked value that will be set
20535          */
20536         "beforecheckchange" : true,
20537         /**
20538          * @event checkchange
20539          * Fires after the checked value has been set
20540          * @param {Roo.menu.CheckItem} this
20541          * @param {Boolean} checked The checked value that was set
20542          */
20543         "checkchange" : true
20544     });
20545     if(this.checkHandler){
20546         this.on('checkchange', this.checkHandler, this.scope);
20547     }
20548 };
20549 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20550     /**
20551      * @cfg {String} group
20552      * All check items with the same group name will automatically be grouped into a single-select
20553      * radio button group (defaults to '')
20554      */
20555     /**
20556      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20557      */
20558     itemCls : "x-menu-item x-menu-check-item",
20559     /**
20560      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20561      */
20562     groupClass : "x-menu-group-item",
20563
20564     /**
20565      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20566      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20567      * initialized with checked = true will be rendered as checked.
20568      */
20569     checked: false,
20570
20571     // private
20572     ctype: "Roo.menu.CheckItem",
20573
20574     // private
20575     onRender : function(c){
20576         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20577         if(this.group){
20578             this.el.addClass(this.groupClass);
20579         }
20580         Roo.menu.MenuMgr.registerCheckable(this);
20581         if(this.checked){
20582             this.checked = false;
20583             this.setChecked(true, true);
20584         }
20585     },
20586
20587     // private
20588     destroy : function(){
20589         if(this.rendered){
20590             Roo.menu.MenuMgr.unregisterCheckable(this);
20591         }
20592         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20593     },
20594
20595     /**
20596      * Set the checked state of this item
20597      * @param {Boolean} checked The new checked value
20598      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20599      */
20600     setChecked : function(state, suppressEvent){
20601         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20602             if(this.container){
20603                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20604             }
20605             this.checked = state;
20606             if(suppressEvent !== true){
20607                 this.fireEvent("checkchange", this, state);
20608             }
20609         }
20610     },
20611
20612     // private
20613     handleClick : function(e){
20614        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20615            this.setChecked(!this.checked);
20616        }
20617        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20618     }
20619 });/*
20620  * Based on:
20621  * Ext JS Library 1.1.1
20622  * Copyright(c) 2006-2007, Ext JS, LLC.
20623  *
20624  * Originally Released Under LGPL - original licence link has changed is not relivant.
20625  *
20626  * Fork - LGPL
20627  * <script type="text/javascript">
20628  */
20629  
20630 /**
20631  * @class Roo.menu.DateItem
20632  * @extends Roo.menu.Adapter
20633  * A menu item that wraps the {@link Roo.DatPicker} component.
20634  * @constructor
20635  * Creates a new DateItem
20636  * @param {Object} config Configuration options
20637  */
20638 Roo.menu.DateItem = function(config){
20639     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20640     /** The Roo.DatePicker object @type Roo.DatePicker */
20641     this.picker = this.component;
20642     this.addEvents({select: true});
20643     
20644     this.picker.on("render", function(picker){
20645         picker.getEl().swallowEvent("click");
20646         picker.container.addClass("x-menu-date-item");
20647     });
20648
20649     this.picker.on("select", this.onSelect, this);
20650 };
20651
20652 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20653     // private
20654     onSelect : function(picker, date){
20655         this.fireEvent("select", this, date, picker);
20656         Roo.menu.DateItem.superclass.handleClick.call(this);
20657     }
20658 });/*
20659  * Based on:
20660  * Ext JS Library 1.1.1
20661  * Copyright(c) 2006-2007, Ext JS, LLC.
20662  *
20663  * Originally Released Under LGPL - original licence link has changed is not relivant.
20664  *
20665  * Fork - LGPL
20666  * <script type="text/javascript">
20667  */
20668  
20669 /**
20670  * @class Roo.menu.ColorItem
20671  * @extends Roo.menu.Adapter
20672  * A menu item that wraps the {@link Roo.ColorPalette} component.
20673  * @constructor
20674  * Creates a new ColorItem
20675  * @param {Object} config Configuration options
20676  */
20677 Roo.menu.ColorItem = function(config){
20678     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20679     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20680     this.palette = this.component;
20681     this.relayEvents(this.palette, ["select"]);
20682     if(this.selectHandler){
20683         this.on('select', this.selectHandler, this.scope);
20684     }
20685 };
20686 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20687  * Based on:
20688  * Ext JS Library 1.1.1
20689  * Copyright(c) 2006-2007, Ext JS, LLC.
20690  *
20691  * Originally Released Under LGPL - original licence link has changed is not relivant.
20692  *
20693  * Fork - LGPL
20694  * <script type="text/javascript">
20695  */
20696  
20697
20698 /**
20699  * @class Roo.menu.DateMenu
20700  * @extends Roo.menu.Menu
20701  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20702  * @constructor
20703  * Creates a new DateMenu
20704  * @param {Object} config Configuration options
20705  */
20706 Roo.menu.DateMenu = function(config){
20707     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20708     this.plain = true;
20709     var di = new Roo.menu.DateItem(config);
20710     this.add(di);
20711     /**
20712      * The {@link Roo.DatePicker} instance for this DateMenu
20713      * @type DatePicker
20714      */
20715     this.picker = di.picker;
20716     /**
20717      * @event select
20718      * @param {DatePicker} picker
20719      * @param {Date} date
20720      */
20721     this.relayEvents(di, ["select"]);
20722
20723     this.on('beforeshow', function(){
20724         if(this.picker){
20725             this.picker.hideMonthPicker(true);
20726         }
20727     }, this);
20728 };
20729 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20730     cls:'x-date-menu'
20731 });/*
20732  * Based on:
20733  * Ext JS Library 1.1.1
20734  * Copyright(c) 2006-2007, Ext JS, LLC.
20735  *
20736  * Originally Released Under LGPL - original licence link has changed is not relivant.
20737  *
20738  * Fork - LGPL
20739  * <script type="text/javascript">
20740  */
20741  
20742
20743 /**
20744  * @class Roo.menu.ColorMenu
20745  * @extends Roo.menu.Menu
20746  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20747  * @constructor
20748  * Creates a new ColorMenu
20749  * @param {Object} config Configuration options
20750  */
20751 Roo.menu.ColorMenu = function(config){
20752     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20753     this.plain = true;
20754     var ci = new Roo.menu.ColorItem(config);
20755     this.add(ci);
20756     /**
20757      * The {@link Roo.ColorPalette} instance for this ColorMenu
20758      * @type ColorPalette
20759      */
20760     this.palette = ci.palette;
20761     /**
20762      * @event select
20763      * @param {ColorPalette} palette
20764      * @param {String} color
20765      */
20766     this.relayEvents(ci, ["select"]);
20767 };
20768 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
20769  * Based on:
20770  * Ext JS Library 1.1.1
20771  * Copyright(c) 2006-2007, Ext JS, LLC.
20772  *
20773  * Originally Released Under LGPL - original licence link has changed is not relivant.
20774  *
20775  * Fork - LGPL
20776  * <script type="text/javascript">
20777  */
20778  
20779 /**
20780  * @class Roo.form.Field
20781  * @extends Roo.BoxComponent
20782  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
20783  * @constructor
20784  * Creates a new Field
20785  * @param {Object} config Configuration options
20786  */
20787 Roo.form.Field = function(config){
20788     Roo.form.Field.superclass.constructor.call(this, config);
20789 };
20790
20791 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
20792     /**
20793      * @cfg {String} fieldLabel Label to use when rendering a form.
20794      */
20795        /**
20796      * @cfg {String} qtip Mouse over tip
20797      */
20798      
20799     /**
20800      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
20801      */
20802     invalidClass : "x-form-invalid",
20803     /**
20804      * @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")
20805      */
20806     invalidText : "The value in this field is invalid",
20807     /**
20808      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
20809      */
20810     focusClass : "x-form-focus",
20811     /**
20812      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
20813       automatic validation (defaults to "keyup").
20814      */
20815     validationEvent : "keyup",
20816     /**
20817      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
20818      */
20819     validateOnBlur : true,
20820     /**
20821      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
20822      */
20823     validationDelay : 250,
20824     /**
20825      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20826      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
20827      */
20828     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
20829     /**
20830      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
20831      */
20832     fieldClass : "x-form-field",
20833     /**
20834      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
20835      *<pre>
20836 Value         Description
20837 -----------   ----------------------------------------------------------------------
20838 qtip          Display a quick tip when the user hovers over the field
20839 title         Display a default browser title attribute popup
20840 under         Add a block div beneath the field containing the error text
20841 side          Add an error icon to the right of the field with a popup on hover
20842 [element id]  Add the error text directly to the innerHTML of the specified element
20843 </pre>
20844      */
20845     msgTarget : 'qtip',
20846     /**
20847      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
20848      */
20849     msgFx : 'normal',
20850
20851     /**
20852      * @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.
20853      */
20854     readOnly : false,
20855
20856     /**
20857      * @cfg {Boolean} disabled True to disable the field (defaults to false).
20858      */
20859     disabled : false,
20860
20861     /**
20862      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
20863      */
20864     inputType : undefined,
20865     
20866     /**
20867      * @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).
20868          */
20869         tabIndex : undefined,
20870         
20871     // private
20872     isFormField : true,
20873
20874     // private
20875     hasFocus : false,
20876     /**
20877      * @property {Roo.Element} fieldEl
20878      * Element Containing the rendered Field (with label etc.)
20879      */
20880     /**
20881      * @cfg {Mixed} value A value to initialize this field with.
20882      */
20883     value : undefined,
20884
20885     /**
20886      * @cfg {String} name The field's HTML name attribute.
20887      */
20888     /**
20889      * @cfg {String} cls A CSS class to apply to the field's underlying element.
20890      */
20891
20892         // private ??
20893         initComponent : function(){
20894         Roo.form.Field.superclass.initComponent.call(this);
20895         this.addEvents({
20896             /**
20897              * @event focus
20898              * Fires when this field receives input focus.
20899              * @param {Roo.form.Field} this
20900              */
20901             focus : true,
20902             /**
20903              * @event blur
20904              * Fires when this field loses input focus.
20905              * @param {Roo.form.Field} this
20906              */
20907             blur : true,
20908             /**
20909              * @event specialkey
20910              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
20911              * {@link Roo.EventObject#getKey} to determine which key was pressed.
20912              * @param {Roo.form.Field} this
20913              * @param {Roo.EventObject} e The event object
20914              */
20915             specialkey : true,
20916             /**
20917              * @event change
20918              * Fires just before the field blurs if the field value has changed.
20919              * @param {Roo.form.Field} this
20920              * @param {Mixed} newValue The new value
20921              * @param {Mixed} oldValue The original value
20922              */
20923             change : true,
20924             /**
20925              * @event invalid
20926              * Fires after the field has been marked as invalid.
20927              * @param {Roo.form.Field} this
20928              * @param {String} msg The validation message
20929              */
20930             invalid : true,
20931             /**
20932              * @event valid
20933              * Fires after the field has been validated with no errors.
20934              * @param {Roo.form.Field} this
20935              */
20936             valid : true,
20937              /**
20938              * @event keyup
20939              * Fires after the key up
20940              * @param {Roo.form.Field} this
20941              * @param {Roo.EventObject}  e The event Object
20942              */
20943             keyup : true
20944         });
20945     },
20946
20947     /**
20948      * Returns the name attribute of the field if available
20949      * @return {String} name The field name
20950      */
20951     getName: function(){
20952          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
20953     },
20954
20955     // private
20956     onRender : function(ct, position){
20957         Roo.form.Field.superclass.onRender.call(this, ct, position);
20958         if(!this.el){
20959             var cfg = this.getAutoCreate();
20960             if(!cfg.name){
20961                 cfg.name = this.name || this.id;
20962             }
20963             if(this.inputType){
20964                 cfg.type = this.inputType;
20965             }
20966             this.el = ct.createChild(cfg, position);
20967         }
20968         var type = this.el.dom.type;
20969         if(type){
20970             if(type == 'password'){
20971                 type = 'text';
20972             }
20973             this.el.addClass('x-form-'+type);
20974         }
20975         if(this.readOnly){
20976             this.el.dom.readOnly = true;
20977         }
20978         if(this.tabIndex !== undefined){
20979             this.el.dom.setAttribute('tabIndex', this.tabIndex);
20980         }
20981
20982         this.el.addClass([this.fieldClass, this.cls]);
20983         this.initValue();
20984     },
20985
20986     /**
20987      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
20988      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
20989      * @return {Roo.form.Field} this
20990      */
20991     applyTo : function(target){
20992         this.allowDomMove = false;
20993         this.el = Roo.get(target);
20994         this.render(this.el.dom.parentNode);
20995         return this;
20996     },
20997
20998     // private
20999     initValue : function(){
21000         if(this.value !== undefined){
21001             this.setValue(this.value);
21002         }else if(this.el.dom.value.length > 0){
21003             this.setValue(this.el.dom.value);
21004         }
21005     },
21006
21007     /**
21008      * Returns true if this field has been changed since it was originally loaded and is not disabled.
21009      */
21010     isDirty : function() {
21011         if(this.disabled) {
21012             return false;
21013         }
21014         return String(this.getValue()) !== String(this.originalValue);
21015     },
21016
21017     // private
21018     afterRender : function(){
21019         Roo.form.Field.superclass.afterRender.call(this);
21020         this.initEvents();
21021     },
21022
21023     // private
21024     fireKey : function(e){
21025         //Roo.log('field ' + e.getKey());
21026         if(e.isNavKeyPress()){
21027             this.fireEvent("specialkey", this, e);
21028         }
21029     },
21030
21031     /**
21032      * Resets the current field value to the originally loaded value and clears any validation messages
21033      */
21034     reset : function(){
21035         this.setValue(this.originalValue);
21036         this.clearInvalid();
21037     },
21038
21039     // private
21040     initEvents : function(){
21041         // safari killled keypress - so keydown is now used..
21042         this.el.on("keydown" , this.fireKey,  this);
21043         this.el.on("focus", this.onFocus,  this);
21044         this.el.on("blur", this.onBlur,  this);
21045         this.el.relayEvent('keyup', this);
21046
21047         // reference to original value for reset
21048         this.originalValue = this.getValue();
21049     },
21050
21051     // private
21052     onFocus : function(){
21053         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21054             this.el.addClass(this.focusClass);
21055         }
21056         if(!this.hasFocus){
21057             this.hasFocus = true;
21058             this.startValue = this.getValue();
21059             this.fireEvent("focus", this);
21060         }
21061     },
21062
21063     beforeBlur : Roo.emptyFn,
21064
21065     // private
21066     onBlur : function(){
21067         this.beforeBlur();
21068         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21069             this.el.removeClass(this.focusClass);
21070         }
21071         this.hasFocus = false;
21072         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21073             this.validate();
21074         }
21075         var v = this.getValue();
21076         if(String(v) !== String(this.startValue)){
21077             this.fireEvent('change', this, v, this.startValue);
21078         }
21079         this.fireEvent("blur", this);
21080     },
21081
21082     /**
21083      * Returns whether or not the field value is currently valid
21084      * @param {Boolean} preventMark True to disable marking the field invalid
21085      * @return {Boolean} True if the value is valid, else false
21086      */
21087     isValid : function(preventMark){
21088         if(this.disabled){
21089             return true;
21090         }
21091         var restore = this.preventMark;
21092         this.preventMark = preventMark === true;
21093         var v = this.validateValue(this.processValue(this.getRawValue()));
21094         this.preventMark = restore;
21095         return v;
21096     },
21097
21098     /**
21099      * Validates the field value
21100      * @return {Boolean} True if the value is valid, else false
21101      */
21102     validate : function(){
21103         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21104             this.clearInvalid();
21105             return true;
21106         }
21107         return false;
21108     },
21109
21110     processValue : function(value){
21111         return value;
21112     },
21113
21114     // private
21115     // Subclasses should provide the validation implementation by overriding this
21116     validateValue : function(value){
21117         return true;
21118     },
21119
21120     /**
21121      * Mark this field as invalid
21122      * @param {String} msg The validation message
21123      */
21124     markInvalid : function(msg){
21125         if(!this.rendered || this.preventMark){ // not rendered
21126             return;
21127         }
21128         this.el.addClass(this.invalidClass);
21129         msg = msg || this.invalidText;
21130         switch(this.msgTarget){
21131             case 'qtip':
21132                 this.el.dom.qtip = msg;
21133                 this.el.dom.qclass = 'x-form-invalid-tip';
21134                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21135                     Roo.QuickTips.enable();
21136                 }
21137                 break;
21138             case 'title':
21139                 this.el.dom.title = msg;
21140                 break;
21141             case 'under':
21142                 if(!this.errorEl){
21143                     var elp = this.el.findParent('.x-form-element', 5, true);
21144                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21145                     this.errorEl.setWidth(elp.getWidth(true)-20);
21146                 }
21147                 this.errorEl.update(msg);
21148                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21149                 break;
21150             case 'side':
21151                 if(!this.errorIcon){
21152                     var elp = this.el.findParent('.x-form-element', 5, true);
21153                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21154                 }
21155                 this.alignErrorIcon();
21156                 this.errorIcon.dom.qtip = msg;
21157                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21158                 this.errorIcon.show();
21159                 this.on('resize', this.alignErrorIcon, this);
21160                 break;
21161             default:
21162                 var t = Roo.getDom(this.msgTarget);
21163                 t.innerHTML = msg;
21164                 t.style.display = this.msgDisplay;
21165                 break;
21166         }
21167         this.fireEvent('invalid', this, msg);
21168     },
21169
21170     // private
21171     alignErrorIcon : function(){
21172         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21173     },
21174
21175     /**
21176      * Clear any invalid styles/messages for this field
21177      */
21178     clearInvalid : function(){
21179         if(!this.rendered || this.preventMark){ // not rendered
21180             return;
21181         }
21182         this.el.removeClass(this.invalidClass);
21183         switch(this.msgTarget){
21184             case 'qtip':
21185                 this.el.dom.qtip = '';
21186                 break;
21187             case 'title':
21188                 this.el.dom.title = '';
21189                 break;
21190             case 'under':
21191                 if(this.errorEl){
21192                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21193                 }
21194                 break;
21195             case 'side':
21196                 if(this.errorIcon){
21197                     this.errorIcon.dom.qtip = '';
21198                     this.errorIcon.hide();
21199                     this.un('resize', this.alignErrorIcon, this);
21200                 }
21201                 break;
21202             default:
21203                 var t = Roo.getDom(this.msgTarget);
21204                 t.innerHTML = '';
21205                 t.style.display = 'none';
21206                 break;
21207         }
21208         this.fireEvent('valid', this);
21209     },
21210
21211     /**
21212      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21213      * @return {Mixed} value The field value
21214      */
21215     getRawValue : function(){
21216         var v = this.el.getValue();
21217         if(v === this.emptyText){
21218             v = '';
21219         }
21220         return v;
21221     },
21222
21223     /**
21224      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21225      * @return {Mixed} value The field value
21226      */
21227     getValue : function(){
21228         var v = this.el.getValue();
21229         if(v === this.emptyText || v === undefined){
21230             v = '';
21231         }
21232         return v;
21233     },
21234
21235     /**
21236      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21237      * @param {Mixed} value The value to set
21238      */
21239     setRawValue : function(v){
21240         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21241     },
21242
21243     /**
21244      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21245      * @param {Mixed} value The value to set
21246      */
21247     setValue : function(v){
21248         this.value = v;
21249         if(this.rendered){
21250             this.el.dom.value = (v === null || v === undefined ? '' : v);
21251             this.validate();
21252         }
21253     },
21254
21255     adjustSize : function(w, h){
21256         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21257         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21258         return s;
21259     },
21260
21261     adjustWidth : function(tag, w){
21262         tag = tag.toLowerCase();
21263         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21264             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21265                 if(tag == 'input'){
21266                     return w + 2;
21267                 }
21268                 if(tag = 'textarea'){
21269                     return w-2;
21270                 }
21271             }else if(Roo.isOpera){
21272                 if(tag == 'input'){
21273                     return w + 2;
21274                 }
21275                 if(tag = 'textarea'){
21276                     return w-2;
21277                 }
21278             }
21279         }
21280         return w;
21281     }
21282 });
21283
21284
21285 // anything other than normal should be considered experimental
21286 Roo.form.Field.msgFx = {
21287     normal : {
21288         show: function(msgEl, f){
21289             msgEl.setDisplayed('block');
21290         },
21291
21292         hide : function(msgEl, f){
21293             msgEl.setDisplayed(false).update('');
21294         }
21295     },
21296
21297     slide : {
21298         show: function(msgEl, f){
21299             msgEl.slideIn('t', {stopFx:true});
21300         },
21301
21302         hide : function(msgEl, f){
21303             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21304         }
21305     },
21306
21307     slideRight : {
21308         show: function(msgEl, f){
21309             msgEl.fixDisplay();
21310             msgEl.alignTo(f.el, 'tl-tr');
21311             msgEl.slideIn('l', {stopFx:true});
21312         },
21313
21314         hide : function(msgEl, f){
21315             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21316         }
21317     }
21318 };/*
21319  * Based on:
21320  * Ext JS Library 1.1.1
21321  * Copyright(c) 2006-2007, Ext JS, LLC.
21322  *
21323  * Originally Released Under LGPL - original licence link has changed is not relivant.
21324  *
21325  * Fork - LGPL
21326  * <script type="text/javascript">
21327  */
21328  
21329
21330 /**
21331  * @class Roo.form.TextField
21332  * @extends Roo.form.Field
21333  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21334  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21335  * @constructor
21336  * Creates a new TextField
21337  * @param {Object} config Configuration options
21338  */
21339 Roo.form.TextField = function(config){
21340     Roo.form.TextField.superclass.constructor.call(this, config);
21341     this.addEvents({
21342         /**
21343          * @event autosize
21344          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21345          * according to the default logic, but this event provides a hook for the developer to apply additional
21346          * logic at runtime to resize the field if needed.
21347              * @param {Roo.form.Field} this This text field
21348              * @param {Number} width The new field width
21349              */
21350         autosize : true
21351     });
21352 };
21353
21354 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21355     /**
21356      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21357      */
21358     grow : false,
21359     /**
21360      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21361      */
21362     growMin : 30,
21363     /**
21364      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21365      */
21366     growMax : 800,
21367     /**
21368      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21369      */
21370     vtype : null,
21371     /**
21372      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21373      */
21374     maskRe : null,
21375     /**
21376      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21377      */
21378     disableKeyFilter : false,
21379     /**
21380      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21381      */
21382     allowBlank : true,
21383     /**
21384      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21385      */
21386     minLength : 0,
21387     /**
21388      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21389      */
21390     maxLength : Number.MAX_VALUE,
21391     /**
21392      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21393      */
21394     minLengthText : "The minimum length for this field is {0}",
21395     /**
21396      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21397      */
21398     maxLengthText : "The maximum length for this field is {0}",
21399     /**
21400      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21401      */
21402     selectOnFocus : false,
21403     /**
21404      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21405      */
21406     blankText : "This field is required",
21407     /**
21408      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21409      * If available, this function will be called only after the basic validators all return true, and will be passed the
21410      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21411      */
21412     validator : null,
21413     /**
21414      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21415      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21416      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21417      */
21418     regex : null,
21419     /**
21420      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21421      */
21422     regexText : "",
21423     /**
21424      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
21425      */
21426     emptyText : null,
21427     /**
21428      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
21429      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
21430      */
21431     emptyClass : 'x-form-empty-field',
21432
21433     // private
21434     initEvents : function(){
21435         Roo.form.TextField.superclass.initEvents.call(this);
21436         if(this.validationEvent == 'keyup'){
21437             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21438             this.el.on('keyup', this.filterValidation, this);
21439         }
21440         else if(this.validationEvent !== false){
21441             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21442         }
21443         if(this.selectOnFocus || this.emptyText){
21444             this.on("focus", this.preFocus, this);
21445             if(this.emptyText){
21446                 this.on('blur', this.postBlur, this);
21447                 this.applyEmptyText();
21448             }
21449         }
21450         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21451             this.el.on("keypress", this.filterKeys, this);
21452         }
21453         if(this.grow){
21454             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21455             this.el.on("click", this.autoSize,  this);
21456         }
21457     },
21458
21459     processValue : function(value){
21460         if(this.stripCharsRe){
21461             var newValue = value.replace(this.stripCharsRe, '');
21462             if(newValue !== value){
21463                 this.setRawValue(newValue);
21464                 return newValue;
21465             }
21466         }
21467         return value;
21468     },
21469
21470     filterValidation : function(e){
21471         if(!e.isNavKeyPress()){
21472             this.validationTask.delay(this.validationDelay);
21473         }
21474     },
21475
21476     // private
21477     onKeyUp : function(e){
21478         if(!e.isNavKeyPress()){
21479             this.autoSize();
21480         }
21481     },
21482
21483     /**
21484      * Resets the current field value to the originally-loaded value and clears any validation messages.
21485      * Also adds emptyText and emptyClass if the original value was blank.
21486      */
21487     reset : function(){
21488         Roo.form.TextField.superclass.reset.call(this);
21489         this.applyEmptyText();
21490     },
21491
21492     applyEmptyText : function(){
21493         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
21494             this.setRawValue(this.emptyText);
21495             this.el.addClass(this.emptyClass);
21496         }
21497     },
21498
21499     // private
21500     preFocus : function(){
21501         if(this.emptyText){
21502             if(this.el.dom.value == this.emptyText){
21503                 this.setRawValue('');
21504             }
21505             this.el.removeClass(this.emptyClass);
21506         }
21507         if(this.selectOnFocus){
21508             this.el.dom.select();
21509         }
21510     },
21511
21512     // private
21513     postBlur : function(){
21514         this.applyEmptyText();
21515     },
21516
21517     // private
21518     filterKeys : function(e){
21519         var k = e.getKey();
21520         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21521             return;
21522         }
21523         var c = e.getCharCode(), cc = String.fromCharCode(c);
21524         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21525             return;
21526         }
21527         if(!this.maskRe.test(cc)){
21528             e.stopEvent();
21529         }
21530     },
21531
21532     setValue : function(v){
21533         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
21534             this.el.removeClass(this.emptyClass);
21535         }
21536         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21537         this.applyEmptyText();
21538         this.autoSize();
21539     },
21540
21541     /**
21542      * Validates a value according to the field's validation rules and marks the field as invalid
21543      * if the validation fails
21544      * @param {Mixed} value The value to validate
21545      * @return {Boolean} True if the value is valid, else false
21546      */
21547     validateValue : function(value){
21548         if(value.length < 1 || value === this.emptyText){ // if it's blank
21549              if(this.allowBlank){
21550                 this.clearInvalid();
21551                 return true;
21552              }else{
21553                 this.markInvalid(this.blankText);
21554                 return false;
21555              }
21556         }
21557         if(value.length < this.minLength){
21558             this.markInvalid(String.format(this.minLengthText, this.minLength));
21559             return false;
21560         }
21561         if(value.length > this.maxLength){
21562             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21563             return false;
21564         }
21565         if(this.vtype){
21566             var vt = Roo.form.VTypes;
21567             if(!vt[this.vtype](value, this)){
21568                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21569                 return false;
21570             }
21571         }
21572         if(typeof this.validator == "function"){
21573             var msg = this.validator(value);
21574             if(msg !== true){
21575                 this.markInvalid(msg);
21576                 return false;
21577             }
21578         }
21579         if(this.regex && !this.regex.test(value)){
21580             this.markInvalid(this.regexText);
21581             return false;
21582         }
21583         return true;
21584     },
21585
21586     /**
21587      * Selects text in this field
21588      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21589      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21590      */
21591     selectText : function(start, end){
21592         var v = this.getRawValue();
21593         if(v.length > 0){
21594             start = start === undefined ? 0 : start;
21595             end = end === undefined ? v.length : end;
21596             var d = this.el.dom;
21597             if(d.setSelectionRange){
21598                 d.setSelectionRange(start, end);
21599             }else if(d.createTextRange){
21600                 var range = d.createTextRange();
21601                 range.moveStart("character", start);
21602                 range.moveEnd("character", v.length-end);
21603                 range.select();
21604             }
21605         }
21606     },
21607
21608     /**
21609      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21610      * This only takes effect if grow = true, and fires the autosize event.
21611      */
21612     autoSize : function(){
21613         if(!this.grow || !this.rendered){
21614             return;
21615         }
21616         if(!this.metrics){
21617             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21618         }
21619         var el = this.el;
21620         var v = el.dom.value;
21621         var d = document.createElement('div');
21622         d.appendChild(document.createTextNode(v));
21623         v = d.innerHTML;
21624         d = null;
21625         v += "&#160;";
21626         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21627         this.el.setWidth(w);
21628         this.fireEvent("autosize", this, w);
21629     }
21630 });/*
21631  * Based on:
21632  * Ext JS Library 1.1.1
21633  * Copyright(c) 2006-2007, Ext JS, LLC.
21634  *
21635  * Originally Released Under LGPL - original licence link has changed is not relivant.
21636  *
21637  * Fork - LGPL
21638  * <script type="text/javascript">
21639  */
21640  
21641 /**
21642  * @class Roo.form.Hidden
21643  * @extends Roo.form.TextField
21644  * Simple Hidden element used on forms 
21645  * 
21646  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21647  * 
21648  * @constructor
21649  * Creates a new Hidden form element.
21650  * @param {Object} config Configuration options
21651  */
21652
21653
21654
21655 // easy hidden field...
21656 Roo.form.Hidden = function(config){
21657     Roo.form.Hidden.superclass.constructor.call(this, config);
21658 };
21659   
21660 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21661     fieldLabel:      '',
21662     inputType:      'hidden',
21663     width:          50,
21664     allowBlank:     true,
21665     labelSeparator: '',
21666     hidden:         true,
21667     itemCls :       'x-form-item-display-none'
21668
21669
21670 });
21671
21672
21673 /*
21674  * Based on:
21675  * Ext JS Library 1.1.1
21676  * Copyright(c) 2006-2007, Ext JS, LLC.
21677  *
21678  * Originally Released Under LGPL - original licence link has changed is not relivant.
21679  *
21680  * Fork - LGPL
21681  * <script type="text/javascript">
21682  */
21683  
21684 /**
21685  * @class Roo.form.TriggerField
21686  * @extends Roo.form.TextField
21687  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21688  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21689  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21690  * for which you can provide a custom implementation.  For example:
21691  * <pre><code>
21692 var trigger = new Roo.form.TriggerField();
21693 trigger.onTriggerClick = myTriggerFn;
21694 trigger.applyTo('my-field');
21695 </code></pre>
21696  *
21697  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21698  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21699  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21700  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21701  * @constructor
21702  * Create a new TriggerField.
21703  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21704  * to the base TextField)
21705  */
21706 Roo.form.TriggerField = function(config){
21707     this.mimicing = false;
21708     Roo.form.TriggerField.superclass.constructor.call(this, config);
21709 };
21710
21711 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21712     /**
21713      * @cfg {String} triggerClass A CSS class to apply to the trigger
21714      */
21715     /**
21716      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21717      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21718      */
21719     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
21720     /**
21721      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21722      */
21723     hideTrigger:false,
21724
21725     /** @cfg {Boolean} grow @hide */
21726     /** @cfg {Number} growMin @hide */
21727     /** @cfg {Number} growMax @hide */
21728
21729     /**
21730      * @hide 
21731      * @method
21732      */
21733     autoSize: Roo.emptyFn,
21734     // private
21735     monitorTab : true,
21736     // private
21737     deferHeight : true,
21738
21739     
21740     actionMode : 'wrap',
21741     // private
21742     onResize : function(w, h){
21743         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21744         if(typeof w == 'number'){
21745             var x = w - this.trigger.getWidth();
21746             this.el.setWidth(this.adjustWidth('input', x));
21747             this.trigger.setStyle('left', x+'px');
21748         }
21749     },
21750
21751     // private
21752     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21753
21754     // private
21755     getResizeEl : function(){
21756         return this.wrap;
21757     },
21758
21759     // private
21760     getPositionEl : function(){
21761         return this.wrap;
21762     },
21763
21764     // private
21765     alignErrorIcon : function(){
21766         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
21767     },
21768
21769     // private
21770     onRender : function(ct, position){
21771         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
21772         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
21773         this.trigger = this.wrap.createChild(this.triggerConfig ||
21774                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
21775         if(this.hideTrigger){
21776             this.trigger.setDisplayed(false);
21777         }
21778         this.initTrigger();
21779         if(!this.width){
21780             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
21781         }
21782     },
21783
21784     // private
21785     initTrigger : function(){
21786         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
21787         this.trigger.addClassOnOver('x-form-trigger-over');
21788         this.trigger.addClassOnClick('x-form-trigger-click');
21789     },
21790
21791     // private
21792     onDestroy : function(){
21793         if(this.trigger){
21794             this.trigger.removeAllListeners();
21795             this.trigger.remove();
21796         }
21797         if(this.wrap){
21798             this.wrap.remove();
21799         }
21800         Roo.form.TriggerField.superclass.onDestroy.call(this);
21801     },
21802
21803     // private
21804     onFocus : function(){
21805         Roo.form.TriggerField.superclass.onFocus.call(this);
21806         if(!this.mimicing){
21807             this.wrap.addClass('x-trigger-wrap-focus');
21808             this.mimicing = true;
21809             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
21810             if(this.monitorTab){
21811                 this.el.on("keydown", this.checkTab, this);
21812             }
21813         }
21814     },
21815
21816     // private
21817     checkTab : function(e){
21818         if(e.getKey() == e.TAB){
21819             this.triggerBlur();
21820         }
21821     },
21822
21823     // private
21824     onBlur : function(){
21825         // do nothing
21826     },
21827
21828     // private
21829     mimicBlur : function(e, t){
21830         if(!this.wrap.contains(t) && this.validateBlur()){
21831             this.triggerBlur();
21832         }
21833     },
21834
21835     // private
21836     triggerBlur : function(){
21837         this.mimicing = false;
21838         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
21839         if(this.monitorTab){
21840             this.el.un("keydown", this.checkTab, this);
21841         }
21842         this.wrap.removeClass('x-trigger-wrap-focus');
21843         Roo.form.TriggerField.superclass.onBlur.call(this);
21844     },
21845
21846     // private
21847     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
21848     validateBlur : function(e, t){
21849         return true;
21850     },
21851
21852     // private
21853     onDisable : function(){
21854         Roo.form.TriggerField.superclass.onDisable.call(this);
21855         if(this.wrap){
21856             this.wrap.addClass('x-item-disabled');
21857         }
21858     },
21859
21860     // private
21861     onEnable : function(){
21862         Roo.form.TriggerField.superclass.onEnable.call(this);
21863         if(this.wrap){
21864             this.wrap.removeClass('x-item-disabled');
21865         }
21866     },
21867
21868     // private
21869     onShow : function(){
21870         var ae = this.getActionEl();
21871         
21872         if(ae){
21873             ae.dom.style.display = '';
21874             ae.dom.style.visibility = 'visible';
21875         }
21876     },
21877
21878     // private
21879     
21880     onHide : function(){
21881         var ae = this.getActionEl();
21882         ae.dom.style.display = 'none';
21883     },
21884
21885     /**
21886      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
21887      * by an implementing function.
21888      * @method
21889      * @param {EventObject} e
21890      */
21891     onTriggerClick : Roo.emptyFn
21892 });
21893
21894 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
21895 // to be extended by an implementing class.  For an example of implementing this class, see the custom
21896 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
21897 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
21898     initComponent : function(){
21899         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
21900
21901         this.triggerConfig = {
21902             tag:'span', cls:'x-form-twin-triggers', cn:[
21903             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
21904             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
21905         ]};
21906     },
21907
21908     getTrigger : function(index){
21909         return this.triggers[index];
21910     },
21911
21912     initTrigger : function(){
21913         var ts = this.trigger.select('.x-form-trigger', true);
21914         this.wrap.setStyle('overflow', 'hidden');
21915         var triggerField = this;
21916         ts.each(function(t, all, index){
21917             t.hide = function(){
21918                 var w = triggerField.wrap.getWidth();
21919                 this.dom.style.display = 'none';
21920                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21921             };
21922             t.show = function(){
21923                 var w = triggerField.wrap.getWidth();
21924                 this.dom.style.display = '';
21925                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21926             };
21927             var triggerIndex = 'Trigger'+(index+1);
21928
21929             if(this['hide'+triggerIndex]){
21930                 t.dom.style.display = 'none';
21931             }
21932             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
21933             t.addClassOnOver('x-form-trigger-over');
21934             t.addClassOnClick('x-form-trigger-click');
21935         }, this);
21936         this.triggers = ts.elements;
21937     },
21938
21939     onTrigger1Click : Roo.emptyFn,
21940     onTrigger2Click : Roo.emptyFn
21941 });/*
21942  * Based on:
21943  * Ext JS Library 1.1.1
21944  * Copyright(c) 2006-2007, Ext JS, LLC.
21945  *
21946  * Originally Released Under LGPL - original licence link has changed is not relivant.
21947  *
21948  * Fork - LGPL
21949  * <script type="text/javascript">
21950  */
21951  
21952 /**
21953  * @class Roo.form.TextArea
21954  * @extends Roo.form.TextField
21955  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
21956  * support for auto-sizing.
21957  * @constructor
21958  * Creates a new TextArea
21959  * @param {Object} config Configuration options
21960  */
21961 Roo.form.TextArea = function(config){
21962     Roo.form.TextArea.superclass.constructor.call(this, config);
21963     // these are provided exchanges for backwards compat
21964     // minHeight/maxHeight were replaced by growMin/growMax to be
21965     // compatible with TextField growing config values
21966     if(this.minHeight !== undefined){
21967         this.growMin = this.minHeight;
21968     }
21969     if(this.maxHeight !== undefined){
21970         this.growMax = this.maxHeight;
21971     }
21972 };
21973
21974 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
21975     /**
21976      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
21977      */
21978     growMin : 60,
21979     /**
21980      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
21981      */
21982     growMax: 1000,
21983     /**
21984      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
21985      * in the field (equivalent to setting overflow: hidden, defaults to false)
21986      */
21987     preventScrollbars: false,
21988     /**
21989      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21990      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
21991      */
21992
21993     // private
21994     onRender : function(ct, position){
21995         if(!this.el){
21996             this.defaultAutoCreate = {
21997                 tag: "textarea",
21998                 style:"width:300px;height:60px;",
21999                 autocomplete: "off"
22000             };
22001         }
22002         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22003         if(this.grow){
22004             this.textSizeEl = Roo.DomHelper.append(document.body, {
22005                 tag: "pre", cls: "x-form-grow-sizer"
22006             });
22007             if(this.preventScrollbars){
22008                 this.el.setStyle("overflow", "hidden");
22009             }
22010             this.el.setHeight(this.growMin);
22011         }
22012     },
22013
22014     onDestroy : function(){
22015         if(this.textSizeEl){
22016             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22017         }
22018         Roo.form.TextArea.superclass.onDestroy.call(this);
22019     },
22020
22021     // private
22022     onKeyUp : function(e){
22023         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22024             this.autoSize();
22025         }
22026     },
22027
22028     /**
22029      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22030      * This only takes effect if grow = true, and fires the autosize event if the height changes.
22031      */
22032     autoSize : function(){
22033         if(!this.grow || !this.textSizeEl){
22034             return;
22035         }
22036         var el = this.el;
22037         var v = el.dom.value;
22038         var ts = this.textSizeEl;
22039
22040         ts.innerHTML = '';
22041         ts.appendChild(document.createTextNode(v));
22042         v = ts.innerHTML;
22043
22044         Roo.fly(ts).setWidth(this.el.getWidth());
22045         if(v.length < 1){
22046             v = "&#160;&#160;";
22047         }else{
22048             if(Roo.isIE){
22049                 v = v.replace(/\n/g, '<p>&#160;</p>');
22050             }
22051             v += "&#160;\n&#160;";
22052         }
22053         ts.innerHTML = v;
22054         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22055         if(h != this.lastHeight){
22056             this.lastHeight = h;
22057             this.el.setHeight(h);
22058             this.fireEvent("autosize", this, h);
22059         }
22060     }
22061 });/*
22062  * Based on:
22063  * Ext JS Library 1.1.1
22064  * Copyright(c) 2006-2007, Ext JS, LLC.
22065  *
22066  * Originally Released Under LGPL - original licence link has changed is not relivant.
22067  *
22068  * Fork - LGPL
22069  * <script type="text/javascript">
22070  */
22071  
22072
22073 /**
22074  * @class Roo.form.NumberField
22075  * @extends Roo.form.TextField
22076  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22077  * @constructor
22078  * Creates a new NumberField
22079  * @param {Object} config Configuration options
22080  */
22081 Roo.form.NumberField = function(config){
22082     Roo.form.NumberField.superclass.constructor.call(this, config);
22083 };
22084
22085 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22086     /**
22087      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22088      */
22089     fieldClass: "x-form-field x-form-num-field",
22090     /**
22091      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22092      */
22093     allowDecimals : true,
22094     /**
22095      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22096      */
22097     decimalSeparator : ".",
22098     /**
22099      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22100      */
22101     decimalPrecision : 2,
22102     /**
22103      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22104      */
22105     allowNegative : true,
22106     /**
22107      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22108      */
22109     minValue : Number.NEGATIVE_INFINITY,
22110     /**
22111      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22112      */
22113     maxValue : Number.MAX_VALUE,
22114     /**
22115      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22116      */
22117     minText : "The minimum value for this field is {0}",
22118     /**
22119      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22120      */
22121     maxText : "The maximum value for this field is {0}",
22122     /**
22123      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22124      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22125      */
22126     nanText : "{0} is not a valid number",
22127
22128     // private
22129     initEvents : function(){
22130         Roo.form.NumberField.superclass.initEvents.call(this);
22131         var allowed = "0123456789";
22132         if(this.allowDecimals){
22133             allowed += this.decimalSeparator;
22134         }
22135         if(this.allowNegative){
22136             allowed += "-";
22137         }
22138         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22139         var keyPress = function(e){
22140             var k = e.getKey();
22141             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22142                 return;
22143             }
22144             var c = e.getCharCode();
22145             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22146                 e.stopEvent();
22147             }
22148         };
22149         this.el.on("keypress", keyPress, this);
22150     },
22151
22152     // private
22153     validateValue : function(value){
22154         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22155             return false;
22156         }
22157         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22158              return true;
22159         }
22160         var num = this.parseValue(value);
22161         if(isNaN(num)){
22162             this.markInvalid(String.format(this.nanText, value));
22163             return false;
22164         }
22165         if(num < this.minValue){
22166             this.markInvalid(String.format(this.minText, this.minValue));
22167             return false;
22168         }
22169         if(num > this.maxValue){
22170             this.markInvalid(String.format(this.maxText, this.maxValue));
22171             return false;
22172         }
22173         return true;
22174     },
22175
22176     getValue : function(){
22177         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22178     },
22179
22180     // private
22181     parseValue : function(value){
22182         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22183         return isNaN(value) ? '' : value;
22184     },
22185
22186     // private
22187     fixPrecision : function(value){
22188         var nan = isNaN(value);
22189         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22190             return nan ? '' : value;
22191         }
22192         return parseFloat(value).toFixed(this.decimalPrecision);
22193     },
22194
22195     setValue : function(v){
22196         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22197     },
22198
22199     // private
22200     decimalPrecisionFcn : function(v){
22201         return Math.floor(v);
22202     },
22203
22204     beforeBlur : function(){
22205         var v = this.parseValue(this.getRawValue());
22206         if(v){
22207             this.setValue(this.fixPrecision(v));
22208         }
22209     }
22210 });/*
22211  * Based on:
22212  * Ext JS Library 1.1.1
22213  * Copyright(c) 2006-2007, Ext JS, LLC.
22214  *
22215  * Originally Released Under LGPL - original licence link has changed is not relivant.
22216  *
22217  * Fork - LGPL
22218  * <script type="text/javascript">
22219  */
22220  
22221 /**
22222  * @class Roo.form.DateField
22223  * @extends Roo.form.TriggerField
22224  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22225 * @constructor
22226 * Create a new DateField
22227 * @param {Object} config
22228  */
22229 Roo.form.DateField = function(config){
22230     Roo.form.DateField.superclass.constructor.call(this, config);
22231     
22232       this.addEvents({
22233          
22234         /**
22235          * @event select
22236          * Fires when a date is selected
22237              * @param {Roo.form.DateField} combo This combo box
22238              * @param {Date} date The date selected
22239              */
22240         'select' : true
22241          
22242     });
22243     
22244     
22245     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22246     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22247     this.ddMatch = null;
22248     if(this.disabledDates){
22249         var dd = this.disabledDates;
22250         var re = "(?:";
22251         for(var i = 0; i < dd.length; i++){
22252             re += dd[i];
22253             if(i != dd.length-1) re += "|";
22254         }
22255         this.ddMatch = new RegExp(re + ")");
22256     }
22257 };
22258
22259 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22260     /**
22261      * @cfg {String} format
22262      * The default date format string which can be overriden for localization support.  The format must be
22263      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22264      */
22265     format : "m/d/y",
22266     /**
22267      * @cfg {String} altFormats
22268      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22269      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22270      */
22271     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22272     /**
22273      * @cfg {Array} disabledDays
22274      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22275      */
22276     disabledDays : null,
22277     /**
22278      * @cfg {String} disabledDaysText
22279      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22280      */
22281     disabledDaysText : "Disabled",
22282     /**
22283      * @cfg {Array} disabledDates
22284      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22285      * expression so they are very powerful. Some examples:
22286      * <ul>
22287      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22288      * <li>["03/08", "09/16"] would disable those days for every year</li>
22289      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22290      * <li>["03/../2006"] would disable every day in March 2006</li>
22291      * <li>["^03"] would disable every day in every March</li>
22292      * </ul>
22293      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22294      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22295      */
22296     disabledDates : null,
22297     /**
22298      * @cfg {String} disabledDatesText
22299      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22300      */
22301     disabledDatesText : "Disabled",
22302     /**
22303      * @cfg {Date/String} minValue
22304      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22305      * valid format (defaults to null).
22306      */
22307     minValue : null,
22308     /**
22309      * @cfg {Date/String} maxValue
22310      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22311      * valid format (defaults to null).
22312      */
22313     maxValue : null,
22314     /**
22315      * @cfg {String} minText
22316      * The error text to display when the date in the cell is before minValue (defaults to
22317      * 'The date in this field must be after {minValue}').
22318      */
22319     minText : "The date in this field must be equal to or after {0}",
22320     /**
22321      * @cfg {String} maxText
22322      * The error text to display when the date in the cell is after maxValue (defaults to
22323      * 'The date in this field must be before {maxValue}').
22324      */
22325     maxText : "The date in this field must be equal to or before {0}",
22326     /**
22327      * @cfg {String} invalidText
22328      * The error text to display when the date in the field is invalid (defaults to
22329      * '{value} is not a valid date - it must be in the format {format}').
22330      */
22331     invalidText : "{0} is not a valid date - it must be in the format {1}",
22332     /**
22333      * @cfg {String} triggerClass
22334      * An additional CSS class used to style the trigger button.  The trigger will always get the
22335      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22336      * which displays a calendar icon).
22337      */
22338     triggerClass : 'x-form-date-trigger',
22339     
22340
22341     /**
22342      * @cfg {bool} useIso
22343      * if enabled, then the date field will use a hidden field to store the 
22344      * real value as iso formated date. default (false)
22345      */ 
22346     useIso : false,
22347     /**
22348      * @cfg {String/Object} autoCreate
22349      * A DomHelper element spec, or true for a default element spec (defaults to
22350      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22351      */ 
22352     // private
22353     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22354     
22355     // private
22356     hiddenField: false,
22357     
22358     onRender : function(ct, position)
22359     {
22360         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22361         if (this.useIso) {
22362             this.el.dom.removeAttribute('name'); 
22363             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22364                     'before', true);
22365             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
22366             // prevent input submission
22367             this.hiddenName = this.name;
22368         }
22369             
22370             
22371     },
22372     
22373     // private
22374     validateValue : function(value)
22375     {
22376         value = this.formatDate(value);
22377         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22378             return false;
22379         }
22380         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22381              return true;
22382         }
22383         var svalue = value;
22384         value = this.parseDate(value);
22385         if(!value){
22386             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22387             return false;
22388         }
22389         var time = value.getTime();
22390         if(this.minValue && time < this.minValue.getTime()){
22391             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22392             return false;
22393         }
22394         if(this.maxValue && time > this.maxValue.getTime()){
22395             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22396             return false;
22397         }
22398         if(this.disabledDays){
22399             var day = value.getDay();
22400             for(var i = 0; i < this.disabledDays.length; i++) {
22401                 if(day === this.disabledDays[i]){
22402                     this.markInvalid(this.disabledDaysText);
22403                     return false;
22404                 }
22405             }
22406         }
22407         var fvalue = this.formatDate(value);
22408         if(this.ddMatch && this.ddMatch.test(fvalue)){
22409             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22410             return false;
22411         }
22412         return true;
22413     },
22414
22415     // private
22416     // Provides logic to override the default TriggerField.validateBlur which just returns true
22417     validateBlur : function(){
22418         return !this.menu || !this.menu.isVisible();
22419     },
22420
22421     /**
22422      * Returns the current date value of the date field.
22423      * @return {Date} The date value
22424      */
22425     getValue : function(){
22426         
22427         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22428     },
22429
22430     /**
22431      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22432      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22433      * (the default format used is "m/d/y").
22434      * <br />Usage:
22435      * <pre><code>
22436 //All of these calls set the same date value (May 4, 2006)
22437
22438 //Pass a date object:
22439 var dt = new Date('5/4/06');
22440 dateField.setValue(dt);
22441
22442 //Pass a date string (default format):
22443 dateField.setValue('5/4/06');
22444
22445 //Pass a date string (custom format):
22446 dateField.format = 'Y-m-d';
22447 dateField.setValue('2006-5-4');
22448 </code></pre>
22449      * @param {String/Date} date The date or valid date string
22450      */
22451     setValue : function(date){
22452         if (this.hiddenField) {
22453             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22454         }
22455         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22456     },
22457
22458     // private
22459     parseDate : function(value){
22460         if(!value || value instanceof Date){
22461             return value;
22462         }
22463         var v = Date.parseDate(value, this.format);
22464         if(!v && this.altFormats){
22465             if(!this.altFormatsArray){
22466                 this.altFormatsArray = this.altFormats.split("|");
22467             }
22468             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22469                 v = Date.parseDate(value, this.altFormatsArray[i]);
22470             }
22471         }
22472         return v;
22473     },
22474
22475     // private
22476     formatDate : function(date, fmt){
22477         return (!date || !(date instanceof Date)) ?
22478                date : date.dateFormat(fmt || this.format);
22479     },
22480
22481     // private
22482     menuListeners : {
22483         select: function(m, d){
22484             this.setValue(d);
22485             this.fireEvent('select', this, d);
22486         },
22487         show : function(){ // retain focus styling
22488             this.onFocus();
22489         },
22490         hide : function(){
22491             this.focus.defer(10, this);
22492             var ml = this.menuListeners;
22493             this.menu.un("select", ml.select,  this);
22494             this.menu.un("show", ml.show,  this);
22495             this.menu.un("hide", ml.hide,  this);
22496         }
22497     },
22498
22499     // private
22500     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22501     onTriggerClick : function(){
22502         if(this.disabled){
22503             return;
22504         }
22505         if(this.menu == null){
22506             this.menu = new Roo.menu.DateMenu();
22507         }
22508         Roo.apply(this.menu.picker,  {
22509             showClear: this.allowBlank,
22510             minDate : this.minValue,
22511             maxDate : this.maxValue,
22512             disabledDatesRE : this.ddMatch,
22513             disabledDatesText : this.disabledDatesText,
22514             disabledDays : this.disabledDays,
22515             disabledDaysText : this.disabledDaysText,
22516             format : this.format,
22517             minText : String.format(this.minText, this.formatDate(this.minValue)),
22518             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22519         });
22520         this.menu.on(Roo.apply({}, this.menuListeners, {
22521             scope:this
22522         }));
22523         this.menu.picker.setValue(this.getValue() || new Date());
22524         this.menu.show(this.el, "tl-bl?");
22525     },
22526
22527     beforeBlur : function(){
22528         var v = this.parseDate(this.getRawValue());
22529         if(v){
22530             this.setValue(v);
22531         }
22532     }
22533
22534     /** @cfg {Boolean} grow @hide */
22535     /** @cfg {Number} growMin @hide */
22536     /** @cfg {Number} growMax @hide */
22537     /**
22538      * @hide
22539      * @method autoSize
22540      */
22541 });/*
22542  * Based on:
22543  * Ext JS Library 1.1.1
22544  * Copyright(c) 2006-2007, Ext JS, LLC.
22545  *
22546  * Originally Released Under LGPL - original licence link has changed is not relivant.
22547  *
22548  * Fork - LGPL
22549  * <script type="text/javascript">
22550  */
22551  
22552
22553 /**
22554  * @class Roo.form.ComboBox
22555  * @extends Roo.form.TriggerField
22556  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22557  * @constructor
22558  * Create a new ComboBox.
22559  * @param {Object} config Configuration options
22560  */
22561 Roo.form.ComboBox = function(config){
22562     Roo.form.ComboBox.superclass.constructor.call(this, config);
22563     this.addEvents({
22564         /**
22565          * @event expand
22566          * Fires when the dropdown list is expanded
22567              * @param {Roo.form.ComboBox} combo This combo box
22568              */
22569         'expand' : true,
22570         /**
22571          * @event collapse
22572          * Fires when the dropdown list is collapsed
22573              * @param {Roo.form.ComboBox} combo This combo box
22574              */
22575         'collapse' : true,
22576         /**
22577          * @event beforeselect
22578          * Fires before a list item is selected. Return false to cancel the selection.
22579              * @param {Roo.form.ComboBox} combo This combo box
22580              * @param {Roo.data.Record} record The data record returned from the underlying store
22581              * @param {Number} index The index of the selected item in the dropdown list
22582              */
22583         'beforeselect' : true,
22584         /**
22585          * @event select
22586          * Fires when a list item is selected
22587              * @param {Roo.form.ComboBox} combo This combo box
22588              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22589              * @param {Number} index The index of the selected item in the dropdown list
22590              */
22591         'select' : true,
22592         /**
22593          * @event beforequery
22594          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22595          * The event object passed has these properties:
22596              * @param {Roo.form.ComboBox} combo This combo box
22597              * @param {String} query The query
22598              * @param {Boolean} forceAll true to force "all" query
22599              * @param {Boolean} cancel true to cancel the query
22600              * @param {Object} e The query event object
22601              */
22602         'beforequery': true,
22603          /**
22604          * @event add
22605          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22606              * @param {Roo.form.ComboBox} combo This combo box
22607              */
22608         'add' : true,
22609         /**
22610          * @event edit
22611          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22612              * @param {Roo.form.ComboBox} combo This combo box
22613              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22614              */
22615         'edit' : true
22616         
22617         
22618     });
22619     if(this.transform){
22620         this.allowDomMove = false;
22621         var s = Roo.getDom(this.transform);
22622         if(!this.hiddenName){
22623             this.hiddenName = s.name;
22624         }
22625         if(!this.store){
22626             this.mode = 'local';
22627             var d = [], opts = s.options;
22628             for(var i = 0, len = opts.length;i < len; i++){
22629                 var o = opts[i];
22630                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22631                 if(o.selected) {
22632                     this.value = value;
22633                 }
22634                 d.push([value, o.text]);
22635             }
22636             this.store = new Roo.data.SimpleStore({
22637                 'id': 0,
22638                 fields: ['value', 'text'],
22639                 data : d
22640             });
22641             this.valueField = 'value';
22642             this.displayField = 'text';
22643         }
22644         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22645         if(!this.lazyRender){
22646             this.target = true;
22647             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22648             s.parentNode.removeChild(s); // remove it
22649             this.render(this.el.parentNode);
22650         }else{
22651             s.parentNode.removeChild(s); // remove it
22652         }
22653
22654     }
22655     if (this.store) {
22656         this.store = Roo.factory(this.store, Roo.data);
22657     }
22658     
22659     this.selectedIndex = -1;
22660     if(this.mode == 'local'){
22661         if(config.queryDelay === undefined){
22662             this.queryDelay = 10;
22663         }
22664         if(config.minChars === undefined){
22665             this.minChars = 0;
22666         }
22667     }
22668 };
22669
22670 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22671     /**
22672      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22673      */
22674     /**
22675      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22676      * rendering into an Roo.Editor, defaults to false)
22677      */
22678     /**
22679      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
22680      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
22681      */
22682     /**
22683      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
22684      */
22685     /**
22686      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
22687      * the dropdown list (defaults to undefined, with no header element)
22688      */
22689
22690      /**
22691      * @cfg {String/Roo.Template} tpl The template to use to render the output
22692      */
22693      
22694     // private
22695     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
22696     /**
22697      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
22698      */
22699     listWidth: undefined,
22700     /**
22701      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
22702      * mode = 'remote' or 'text' if mode = 'local')
22703      */
22704     displayField: undefined,
22705     /**
22706      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
22707      * mode = 'remote' or 'value' if mode = 'local'). 
22708      * Note: use of a valueField requires the user make a selection
22709      * in order for a value to be mapped.
22710      */
22711     valueField: undefined,
22712     /**
22713      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
22714      * field's data value (defaults to the underlying DOM element's name)
22715      */
22716     hiddenName: undefined,
22717     /**
22718      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
22719      */
22720     listClass: '',
22721     /**
22722      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
22723      */
22724     selectedClass: 'x-combo-selected',
22725     /**
22726      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22727      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
22728      * which displays a downward arrow icon).
22729      */
22730     triggerClass : 'x-form-arrow-trigger',
22731     /**
22732      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
22733      */
22734     shadow:'sides',
22735     /**
22736      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
22737      * anchor positions (defaults to 'tl-bl')
22738      */
22739     listAlign: 'tl-bl?',
22740     /**
22741      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
22742      */
22743     maxHeight: 300,
22744     /**
22745      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
22746      * query specified by the allQuery config option (defaults to 'query')
22747      */
22748     triggerAction: 'query',
22749     /**
22750      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
22751      * (defaults to 4, does not apply if editable = false)
22752      */
22753     minChars : 4,
22754     /**
22755      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
22756      * delay (typeAheadDelay) if it matches a known value (defaults to false)
22757      */
22758     typeAhead: false,
22759     /**
22760      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
22761      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
22762      */
22763     queryDelay: 500,
22764     /**
22765      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
22766      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
22767      */
22768     pageSize: 0,
22769     /**
22770      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
22771      * when editable = true (defaults to false)
22772      */
22773     selectOnFocus:false,
22774     /**
22775      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
22776      */
22777     queryParam: 'query',
22778     /**
22779      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
22780      * when mode = 'remote' (defaults to 'Loading...')
22781      */
22782     loadingText: 'Loading...',
22783     /**
22784      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
22785      */
22786     resizable: false,
22787     /**
22788      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
22789      */
22790     handleHeight : 8,
22791     /**
22792      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
22793      * traditional select (defaults to true)
22794      */
22795     editable: true,
22796     /**
22797      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
22798      */
22799     allQuery: '',
22800     /**
22801      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
22802      */
22803     mode: 'remote',
22804     /**
22805      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
22806      * listWidth has a higher value)
22807      */
22808     minListWidth : 70,
22809     /**
22810      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
22811      * allow the user to set arbitrary text into the field (defaults to false)
22812      */
22813     forceSelection:false,
22814     /**
22815      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
22816      * if typeAhead = true (defaults to 250)
22817      */
22818     typeAheadDelay : 250,
22819     /**
22820      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
22821      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
22822      */
22823     valueNotFoundText : undefined,
22824     /**
22825      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
22826      */
22827     blockFocus : false,
22828     
22829     /**
22830      * @cfg {Boolean} disableClear Disable showing of clear button.
22831      */
22832     disableClear : false,
22833     /**
22834      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
22835      */
22836     alwaysQuery : false,
22837     
22838     //private
22839     addicon : false,
22840     editicon: false,
22841     
22842     
22843     // private
22844     onRender : function(ct, position){
22845         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
22846         if(this.hiddenName){
22847             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
22848                     'before', true);
22849             this.hiddenField.value =
22850                 this.hiddenValue !== undefined ? this.hiddenValue :
22851                 this.value !== undefined ? this.value : '';
22852
22853             // prevent input submission
22854             this.el.dom.removeAttribute('name');
22855         }
22856         if(Roo.isGecko){
22857             this.el.dom.setAttribute('autocomplete', 'off');
22858         }
22859
22860         var cls = 'x-combo-list';
22861
22862         this.list = new Roo.Layer({
22863             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
22864         });
22865
22866         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
22867         this.list.setWidth(lw);
22868         this.list.swallowEvent('mousewheel');
22869         this.assetHeight = 0;
22870
22871         if(this.title){
22872             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
22873             this.assetHeight += this.header.getHeight();
22874         }
22875
22876         this.innerList = this.list.createChild({cls:cls+'-inner'});
22877         this.innerList.on('mouseover', this.onViewOver, this);
22878         this.innerList.on('mousemove', this.onViewMove, this);
22879         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
22880         
22881         if(this.allowBlank && !this.pageSize && !this.disableClear){
22882             this.footer = this.list.createChild({cls:cls+'-ft'});
22883             this.pageTb = new Roo.Toolbar(this.footer);
22884            
22885         }
22886         if(this.pageSize){
22887             this.footer = this.list.createChild({cls:cls+'-ft'});
22888             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
22889                     {pageSize: this.pageSize});
22890             
22891         }
22892         
22893         if (this.pageTb && this.allowBlank && !this.disableClear) {
22894             var _this = this;
22895             this.pageTb.add(new Roo.Toolbar.Fill(), {
22896                 cls: 'x-btn-icon x-btn-clear',
22897                 text: '&#160;',
22898                 handler: function()
22899                 {
22900                     _this.collapse();
22901                     _this.clearValue();
22902                     _this.onSelect(false, -1);
22903                 }
22904             });
22905         }
22906         if (this.footer) {
22907             this.assetHeight += this.footer.getHeight();
22908         }
22909         
22910
22911         if(!this.tpl){
22912             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
22913         }
22914
22915         this.view = new Roo.View(this.innerList, this.tpl, {
22916             singleSelect:true, store: this.store, selectedClass: this.selectedClass
22917         });
22918
22919         this.view.on('click', this.onViewClick, this);
22920
22921         this.store.on('beforeload', this.onBeforeLoad, this);
22922         this.store.on('load', this.onLoad, this);
22923         this.store.on('loadexception', this.collapse, this);
22924
22925         if(this.resizable){
22926             this.resizer = new Roo.Resizable(this.list,  {
22927                pinned:true, handles:'se'
22928             });
22929             this.resizer.on('resize', function(r, w, h){
22930                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
22931                 this.listWidth = w;
22932                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
22933                 this.restrictHeight();
22934             }, this);
22935             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
22936         }
22937         if(!this.editable){
22938             this.editable = true;
22939             this.setEditable(false);
22940         }  
22941         
22942         
22943         if (typeof(this.events.add.listeners) != 'undefined') {
22944             
22945             this.addicon = this.wrap.createChild(
22946                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
22947        
22948             this.addicon.on('click', function(e) {
22949                 this.fireEvent('add', this);
22950             }, this);
22951         }
22952         if (typeof(this.events.edit.listeners) != 'undefined') {
22953             
22954             this.editicon = this.wrap.createChild(
22955                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
22956             if (this.addicon) {
22957                 this.editicon.setStyle('margin-left', '40px');
22958             }
22959             this.editicon.on('click', function(e) {
22960                 
22961                 // we fire even  if inothing is selected..
22962                 this.fireEvent('edit', this, this.lastData );
22963                 
22964             }, this);
22965         }
22966         
22967         
22968         
22969     },
22970
22971     // private
22972     initEvents : function(){
22973         Roo.form.ComboBox.superclass.initEvents.call(this);
22974
22975         this.keyNav = new Roo.KeyNav(this.el, {
22976             "up" : function(e){
22977                 this.inKeyMode = true;
22978                 this.selectPrev();
22979             },
22980
22981             "down" : function(e){
22982                 if(!this.isExpanded()){
22983                     this.onTriggerClick();
22984                 }else{
22985                     this.inKeyMode = true;
22986                     this.selectNext();
22987                 }
22988             },
22989
22990             "enter" : function(e){
22991                 this.onViewClick();
22992                 //return true;
22993             },
22994
22995             "esc" : function(e){
22996                 this.collapse();
22997             },
22998
22999             "tab" : function(e){
23000                 this.onViewClick(false);
23001                 return true;
23002             },
23003
23004             scope : this,
23005
23006             doRelay : function(foo, bar, hname){
23007                 if(hname == 'down' || this.scope.isExpanded()){
23008                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23009                 }
23010                 return true;
23011             },
23012
23013             forceKeyDown: true
23014         });
23015         this.queryDelay = Math.max(this.queryDelay || 10,
23016                 this.mode == 'local' ? 10 : 250);
23017         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23018         if(this.typeAhead){
23019             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23020         }
23021         if(this.editable !== false){
23022             this.el.on("keyup", this.onKeyUp, this);
23023         }
23024         if(this.forceSelection){
23025             this.on('blur', this.doForce, this);
23026         }
23027     },
23028
23029     onDestroy : function(){
23030         if(this.view){
23031             this.view.setStore(null);
23032             this.view.el.removeAllListeners();
23033             this.view.el.remove();
23034             this.view.purgeListeners();
23035         }
23036         if(this.list){
23037             this.list.destroy();
23038         }
23039         if(this.store){
23040             this.store.un('beforeload', this.onBeforeLoad, this);
23041             this.store.un('load', this.onLoad, this);
23042             this.store.un('loadexception', this.collapse, this);
23043         }
23044         Roo.form.ComboBox.superclass.onDestroy.call(this);
23045     },
23046
23047     // private
23048     fireKey : function(e){
23049         if(e.isNavKeyPress() && !this.list.isVisible()){
23050             this.fireEvent("specialkey", this, e);
23051         }
23052     },
23053
23054     // private
23055     onResize: function(w, h){
23056         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23057         
23058         if(typeof w != 'number'){
23059             // we do not handle it!?!?
23060             return;
23061         }
23062         var tw = this.trigger.getWidth();
23063         tw += this.addicon ? this.addicon.getWidth() : 0;
23064         tw += this.editicon ? this.editicon.getWidth() : 0;
23065         var x = w - tw;
23066         this.el.setWidth( this.adjustWidth('input', x));
23067             
23068         this.trigger.setStyle('left', x+'px');
23069         
23070         if(this.list && this.listWidth === undefined){
23071             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23072             this.list.setWidth(lw);
23073             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23074         }
23075         
23076     
23077         
23078     },
23079
23080     /**
23081      * Allow or prevent the user from directly editing the field text.  If false is passed,
23082      * the user will only be able to select from the items defined in the dropdown list.  This method
23083      * is the runtime equivalent of setting the 'editable' config option at config time.
23084      * @param {Boolean} value True to allow the user to directly edit the field text
23085      */
23086     setEditable : function(value){
23087         if(value == this.editable){
23088             return;
23089         }
23090         this.editable = value;
23091         if(!value){
23092             this.el.dom.setAttribute('readOnly', true);
23093             this.el.on('mousedown', this.onTriggerClick,  this);
23094             this.el.addClass('x-combo-noedit');
23095         }else{
23096             this.el.dom.setAttribute('readOnly', false);
23097             this.el.un('mousedown', this.onTriggerClick,  this);
23098             this.el.removeClass('x-combo-noedit');
23099         }
23100     },
23101
23102     // private
23103     onBeforeLoad : function(){
23104         if(!this.hasFocus){
23105             return;
23106         }
23107         this.innerList.update(this.loadingText ?
23108                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23109         this.restrictHeight();
23110         this.selectedIndex = -1;
23111     },
23112
23113     // private
23114     onLoad : function(){
23115         if(!this.hasFocus){
23116             return;
23117         }
23118         if(this.store.getCount() > 0){
23119             this.expand();
23120             this.restrictHeight();
23121             if(this.lastQuery == this.allQuery){
23122                 if(this.editable){
23123                     this.el.dom.select();
23124                 }
23125                 if(!this.selectByValue(this.value, true)){
23126                     this.select(0, true);
23127                 }
23128             }else{
23129                 this.selectNext();
23130                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23131                     this.taTask.delay(this.typeAheadDelay);
23132                 }
23133             }
23134         }else{
23135             this.onEmptyResults();
23136         }
23137         //this.el.focus();
23138     },
23139
23140     // private
23141     onTypeAhead : function(){
23142         if(this.store.getCount() > 0){
23143             var r = this.store.getAt(0);
23144             var newValue = r.data[this.displayField];
23145             var len = newValue.length;
23146             var selStart = this.getRawValue().length;
23147             if(selStart != len){
23148                 this.setRawValue(newValue);
23149                 this.selectText(selStart, newValue.length);
23150             }
23151         }
23152     },
23153
23154     // private
23155     onSelect : function(record, index){
23156         if(this.fireEvent('beforeselect', this, record, index) !== false){
23157             this.setFromData(index > -1 ? record.data : false);
23158             this.collapse();
23159             this.fireEvent('select', this, record, index);
23160         }
23161     },
23162
23163     /**
23164      * Returns the currently selected field value or empty string if no value is set.
23165      * @return {String} value The selected value
23166      */
23167     getValue : function(){
23168         if(this.valueField){
23169             return typeof this.value != 'undefined' ? this.value : '';
23170         }else{
23171             return Roo.form.ComboBox.superclass.getValue.call(this);
23172         }
23173     },
23174
23175     /**
23176      * Clears any text/value currently set in the field
23177      */
23178     clearValue : function(){
23179         if(this.hiddenField){
23180             this.hiddenField.value = '';
23181         }
23182         this.value = '';
23183         this.setRawValue('');
23184         this.lastSelectionText = '';
23185         this.applyEmptyText();
23186     },
23187
23188     /**
23189      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23190      * will be displayed in the field.  If the value does not match the data value of an existing item,
23191      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23192      * Otherwise the field will be blank (although the value will still be set).
23193      * @param {String} value The value to match
23194      */
23195     setValue : function(v){
23196         var text = v;
23197         if(this.valueField){
23198             var r = this.findRecord(this.valueField, v);
23199             if(r){
23200                 text = r.data[this.displayField];
23201             }else if(this.valueNotFoundText !== undefined){
23202                 text = this.valueNotFoundText;
23203             }
23204         }
23205         this.lastSelectionText = text;
23206         if(this.hiddenField){
23207             this.hiddenField.value = v;
23208         }
23209         Roo.form.ComboBox.superclass.setValue.call(this, text);
23210         this.value = v;
23211     },
23212     /**
23213      * @property {Object} the last set data for the element
23214      */
23215     
23216     lastData : false,
23217     /**
23218      * Sets the value of the field based on a object which is related to the record format for the store.
23219      * @param {Object} value the value to set as. or false on reset?
23220      */
23221     setFromData : function(o){
23222         var dv = ''; // display value
23223         var vv = ''; // value value..
23224         this.lastData = o;
23225         if (this.displayField) {
23226             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23227         } else {
23228             // this is an error condition!!!
23229             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23230         }
23231         
23232         if(this.valueField){
23233             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23234         }
23235         if(this.hiddenField){
23236             this.hiddenField.value = vv;
23237             
23238             this.lastSelectionText = dv;
23239             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23240             this.value = vv;
23241             return;
23242         }
23243         // no hidden field.. - we store the value in 'value', but still display
23244         // display field!!!!
23245         this.lastSelectionText = dv;
23246         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23247         this.value = vv;
23248         
23249         
23250     },
23251     // private
23252     reset : function(){
23253         // overridden so that last data is reset..
23254         this.setValue(this.originalValue);
23255         this.clearInvalid();
23256         this.lastData = false;
23257     },
23258     // private
23259     findRecord : function(prop, value){
23260         var record;
23261         if(this.store.getCount() > 0){
23262             this.store.each(function(r){
23263                 if(r.data[prop] == value){
23264                     record = r;
23265                     return false;
23266                 }
23267             });
23268         }
23269         return record;
23270     },
23271
23272     // private
23273     onViewMove : function(e, t){
23274         this.inKeyMode = false;
23275     },
23276
23277     // private
23278     onViewOver : function(e, t){
23279         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23280             return;
23281         }
23282         var item = this.view.findItemFromChild(t);
23283         if(item){
23284             var index = this.view.indexOf(item);
23285             this.select(index, false);
23286         }
23287     },
23288
23289     // private
23290     onViewClick : function(doFocus){
23291         var index = this.view.getSelectedIndexes()[0];
23292         var r = this.store.getAt(index);
23293         if(r){
23294             this.onSelect(r, index);
23295         }
23296         if(doFocus !== false && !this.blockFocus){
23297             this.el.focus();
23298         }
23299     },
23300
23301     // private
23302     restrictHeight : function(){
23303         this.innerList.dom.style.height = '';
23304         var inner = this.innerList.dom;
23305         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23306         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23307         this.list.beginUpdate();
23308         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23309         this.list.alignTo(this.el, this.listAlign);
23310         this.list.endUpdate();
23311     },
23312
23313     // private
23314     onEmptyResults : function(){
23315         this.collapse();
23316     },
23317
23318     /**
23319      * Returns true if the dropdown list is expanded, else false.
23320      */
23321     isExpanded : function(){
23322         return this.list.isVisible();
23323     },
23324
23325     /**
23326      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23327      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23328      * @param {String} value The data value of the item to select
23329      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23330      * selected item if it is not currently in view (defaults to true)
23331      * @return {Boolean} True if the value matched an item in the list, else false
23332      */
23333     selectByValue : function(v, scrollIntoView){
23334         if(v !== undefined && v !== null){
23335             var r = this.findRecord(this.valueField || this.displayField, v);
23336             if(r){
23337                 this.select(this.store.indexOf(r), scrollIntoView);
23338                 return true;
23339             }
23340         }
23341         return false;
23342     },
23343
23344     /**
23345      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23346      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23347      * @param {Number} index The zero-based index of the list item to select
23348      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23349      * selected item if it is not currently in view (defaults to true)
23350      */
23351     select : function(index, scrollIntoView){
23352         this.selectedIndex = index;
23353         this.view.select(index);
23354         if(scrollIntoView !== false){
23355             var el = this.view.getNode(index);
23356             if(el){
23357                 this.innerList.scrollChildIntoView(el, false);
23358             }
23359         }
23360     },
23361
23362     // private
23363     selectNext : function(){
23364         var ct = this.store.getCount();
23365         if(ct > 0){
23366             if(this.selectedIndex == -1){
23367                 this.select(0);
23368             }else if(this.selectedIndex < ct-1){
23369                 this.select(this.selectedIndex+1);
23370             }
23371         }
23372     },
23373
23374     // private
23375     selectPrev : function(){
23376         var ct = this.store.getCount();
23377         if(ct > 0){
23378             if(this.selectedIndex == -1){
23379                 this.select(0);
23380             }else if(this.selectedIndex != 0){
23381                 this.select(this.selectedIndex-1);
23382             }
23383         }
23384     },
23385
23386     // private
23387     onKeyUp : function(e){
23388         if(this.editable !== false && !e.isSpecialKey()){
23389             this.lastKey = e.getKey();
23390             this.dqTask.delay(this.queryDelay);
23391         }
23392     },
23393
23394     // private
23395     validateBlur : function(){
23396         return !this.list || !this.list.isVisible();   
23397     },
23398
23399     // private
23400     initQuery : function(){
23401         this.doQuery(this.getRawValue());
23402     },
23403
23404     // private
23405     doForce : function(){
23406         if(this.el.dom.value.length > 0){
23407             this.el.dom.value =
23408                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23409             this.applyEmptyText();
23410         }
23411     },
23412
23413     /**
23414      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23415      * query allowing the query action to be canceled if needed.
23416      * @param {String} query The SQL query to execute
23417      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23418      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23419      * saved in the current store (defaults to false)
23420      */
23421     doQuery : function(q, forceAll){
23422         if(q === undefined || q === null){
23423             q = '';
23424         }
23425         var qe = {
23426             query: q,
23427             forceAll: forceAll,
23428             combo: this,
23429             cancel:false
23430         };
23431         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23432             return false;
23433         }
23434         q = qe.query;
23435         forceAll = qe.forceAll;
23436         if(forceAll === true || (q.length >= this.minChars)){
23437             if(this.lastQuery != q || this.alwaysQuery){
23438                 this.lastQuery = q;
23439                 if(this.mode == 'local'){
23440                     this.selectedIndex = -1;
23441                     if(forceAll){
23442                         this.store.clearFilter();
23443                     }else{
23444                         this.store.filter(this.displayField, q);
23445                     }
23446                     this.onLoad();
23447                 }else{
23448                     this.store.baseParams[this.queryParam] = q;
23449                     this.store.load({
23450                         params: this.getParams(q)
23451                     });
23452                     this.expand();
23453                 }
23454             }else{
23455                 this.selectedIndex = -1;
23456                 this.onLoad();   
23457             }
23458         }
23459     },
23460
23461     // private
23462     getParams : function(q){
23463         var p = {};
23464         //p[this.queryParam] = q;
23465         if(this.pageSize){
23466             p.start = 0;
23467             p.limit = this.pageSize;
23468         }
23469         return p;
23470     },
23471
23472     /**
23473      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23474      */
23475     collapse : function(){
23476         if(!this.isExpanded()){
23477             return;
23478         }
23479         this.list.hide();
23480         Roo.get(document).un('mousedown', this.collapseIf, this);
23481         Roo.get(document).un('mousewheel', this.collapseIf, this);
23482         if (!this.editable) {
23483             Roo.get(document).un('keydown', this.listKeyPress, this);
23484         }
23485         this.fireEvent('collapse', this);
23486     },
23487
23488     // private
23489     collapseIf : function(e){
23490         if(!e.within(this.wrap) && !e.within(this.list)){
23491             this.collapse();
23492         }
23493     },
23494
23495     /**
23496      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23497      */
23498     expand : function(){
23499         if(this.isExpanded() || !this.hasFocus){
23500             return;
23501         }
23502         this.list.alignTo(this.el, this.listAlign);
23503         this.list.show();
23504         Roo.get(document).on('mousedown', this.collapseIf, this);
23505         Roo.get(document).on('mousewheel', this.collapseIf, this);
23506         if (!this.editable) {
23507             Roo.get(document).on('keydown', this.listKeyPress, this);
23508         }
23509         
23510         this.fireEvent('expand', this);
23511     },
23512
23513     // private
23514     // Implements the default empty TriggerField.onTriggerClick function
23515     onTriggerClick : function(){
23516         if(this.disabled){
23517             return;
23518         }
23519         if(this.isExpanded()){
23520             this.collapse();
23521             if (!this.blockFocus) {
23522                 this.el.focus();
23523             }
23524             
23525         }else {
23526             this.hasFocus = true;
23527             if(this.triggerAction == 'all') {
23528                 this.doQuery(this.allQuery, true);
23529             } else {
23530                 this.doQuery(this.getRawValue());
23531             }
23532             if (!this.blockFocus) {
23533                 this.el.focus();
23534             }
23535         }
23536     },
23537     listKeyPress : function(e)
23538     {
23539         //Roo.log('listkeypress');
23540         // scroll to first matching element based on key pres..
23541         if (e.isSpecialKey()) {
23542             return false;
23543         }
23544         var k = String.fromCharCode(e.getKey()).toUpperCase();
23545         //Roo.log(k);
23546         var match  = false;
23547         var csel = this.view.getSelectedNodes();
23548         var cselitem = false;
23549         if (csel.length) {
23550             var ix = this.view.indexOf(csel[0]);
23551             cselitem  = this.store.getAt(ix);
23552             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23553                 cselitem = false;
23554             }
23555             
23556         }
23557         
23558         this.store.each(function(v) { 
23559             if (cselitem) {
23560                 // start at existing selection.
23561                 if (cselitem.id == v.id) {
23562                     cselitem = false;
23563                 }
23564                 return;
23565             }
23566                 
23567             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23568                 match = this.store.indexOf(v);
23569                 return false;
23570             }
23571         }, this);
23572         
23573         if (match === false) {
23574             return true; // no more action?
23575         }
23576         // scroll to?
23577         this.view.select(match);
23578         var sn = Roo.get(this.view.getSelectedNodes()[0])
23579         sn.scrollIntoView(sn.dom.parentNode, false);
23580     }
23581
23582     /** 
23583     * @cfg {Boolean} grow 
23584     * @hide 
23585     */
23586     /** 
23587     * @cfg {Number} growMin 
23588     * @hide 
23589     */
23590     /** 
23591     * @cfg {Number} growMax 
23592     * @hide 
23593     */
23594     /**
23595      * @hide
23596      * @method autoSize
23597      */
23598 });/*
23599  * Based on:
23600  * Ext JS Library 1.1.1
23601  * Copyright(c) 2006-2007, Ext JS, LLC.
23602  *
23603  * Originally Released Under LGPL - original licence link has changed is not relivant.
23604  *
23605  * Fork - LGPL
23606  * <script type="text/javascript">
23607  */
23608 /**
23609  * @class Roo.form.Checkbox
23610  * @extends Roo.form.Field
23611  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
23612  * @constructor
23613  * Creates a new Checkbox
23614  * @param {Object} config Configuration options
23615  */
23616 Roo.form.Checkbox = function(config){
23617     Roo.form.Checkbox.superclass.constructor.call(this, config);
23618     this.addEvents({
23619         /**
23620          * @event check
23621          * Fires when the checkbox is checked or unchecked.
23622              * @param {Roo.form.Checkbox} this This checkbox
23623              * @param {Boolean} checked The new checked value
23624              */
23625         check : true
23626     });
23627 };
23628
23629 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
23630     /**
23631      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
23632      */
23633     focusClass : undefined,
23634     /**
23635      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
23636      */
23637     fieldClass: "x-form-field",
23638     /**
23639      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
23640      */
23641     checked: false,
23642     /**
23643      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
23644      * {tag: "input", type: "checkbox", autocomplete: "off"})
23645      */
23646     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
23647     /**
23648      * @cfg {String} boxLabel The text that appears beside the checkbox
23649      */
23650     boxLabel : "",
23651     /**
23652      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
23653      */  
23654     inputValue : '1',
23655     /**
23656      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23657      */
23658      valueOff: '0', // value when not checked..
23659
23660     actionMode : 'viewEl', 
23661     //
23662     // private
23663     itemCls : 'x-menu-check-item x-form-item',
23664     groupClass : 'x-menu-group-item',
23665     inputType : 'hidden',
23666     
23667     
23668     inSetChecked: false, // check that we are not calling self...
23669     
23670     inputElement: false, // real input element?
23671     basedOn: false, // ????
23672     
23673     isFormField: true, // not sure where this is needed!!!!
23674
23675     onResize : function(){
23676         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
23677         if(!this.boxLabel){
23678             this.el.alignTo(this.wrap, 'c-c');
23679         }
23680     },
23681
23682     initEvents : function(){
23683         Roo.form.Checkbox.superclass.initEvents.call(this);
23684         this.el.on("click", this.onClick,  this);
23685         this.el.on("change", this.onClick,  this);
23686     },
23687
23688
23689     getResizeEl : function(){
23690         return this.wrap;
23691     },
23692
23693     getPositionEl : function(){
23694         return this.wrap;
23695     },
23696
23697     // private
23698     onRender : function(ct, position){
23699         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
23700         /*
23701         if(this.inputValue !== undefined){
23702             this.el.dom.value = this.inputValue;
23703         }
23704         */
23705         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
23706         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
23707         var viewEl = this.wrap.createChild({ 
23708             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
23709         this.viewEl = viewEl;   
23710         this.wrap.on('click', this.onClick,  this); 
23711         
23712         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
23713         this.el.on('propertychange', this.setFromHidden,  this);  //ie
23714         
23715         
23716         
23717         if(this.boxLabel){
23718             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
23719         //    viewEl.on('click', this.onClick,  this); 
23720         }
23721         //if(this.checked){
23722             this.setChecked(this.checked);
23723         //}else{
23724             //this.checked = this.el.dom;
23725         //}
23726
23727     },
23728
23729     // private
23730     initValue : Roo.emptyFn,
23731
23732     /**
23733      * Returns the checked state of the checkbox.
23734      * @return {Boolean} True if checked, else false
23735      */
23736     getValue : function(){
23737         if(this.el){
23738             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
23739         }
23740         return this.valueOff;
23741         
23742     },
23743
23744         // private
23745     onClick : function(){ 
23746         this.setChecked(!this.checked);
23747
23748         //if(this.el.dom.checked != this.checked){
23749         //    this.setValue(this.el.dom.checked);
23750        // }
23751     },
23752
23753     /**
23754      * Sets the checked state of the checkbox.
23755      * On is always based on a string comparison between inputValue and the param.
23756      * @param {Boolean/String} value - the value to set 
23757      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
23758      */
23759     setValue : function(v,suppressEvent){
23760         
23761         
23762         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
23763         //if(this.el && this.el.dom){
23764         //    this.el.dom.checked = this.checked;
23765         //    this.el.dom.defaultChecked = this.checked;
23766         //}
23767         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
23768         //this.fireEvent("check", this, this.checked);
23769     },
23770     // private..
23771     setChecked : function(state,suppressEvent)
23772     {
23773         if (this.inSetChecked) {
23774             this.checked = state;
23775             return;
23776         }
23777         
23778     
23779         if(this.wrap){
23780             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
23781         }
23782         this.checked = state;
23783         if(suppressEvent !== true){
23784             this.fireEvent('check', this, state);
23785         }
23786         this.inSetChecked = true;
23787         this.el.dom.value = state ? this.inputValue : this.valueOff;
23788         this.inSetChecked = false;
23789         
23790     },
23791     // handle setting of hidden value by some other method!!?!?
23792     setFromHidden: function()
23793     {
23794         if(!this.el){
23795             return;
23796         }
23797         //console.log("SET FROM HIDDEN");
23798         //alert('setFrom hidden');
23799         this.setValue(this.el.dom.value);
23800     },
23801     
23802     onDestroy : function()
23803     {
23804         if(this.viewEl){
23805             Roo.get(this.viewEl).remove();
23806         }
23807          
23808         Roo.form.Checkbox.superclass.onDestroy.call(this);
23809     }
23810
23811 });/*
23812  * Based on:
23813  * Ext JS Library 1.1.1
23814  * Copyright(c) 2006-2007, Ext JS, LLC.
23815  *
23816  * Originally Released Under LGPL - original licence link has changed is not relivant.
23817  *
23818  * Fork - LGPL
23819  * <script type="text/javascript">
23820  */
23821  
23822 /**
23823  * @class Roo.form.Radio
23824  * @extends Roo.form.Checkbox
23825  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
23826  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
23827  * @constructor
23828  * Creates a new Radio
23829  * @param {Object} config Configuration options
23830  */
23831 Roo.form.Radio = function(){
23832     Roo.form.Radio.superclass.constructor.apply(this, arguments);
23833 };
23834 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
23835     inputType: 'radio',
23836
23837     /**
23838      * If this radio is part of a group, it will return the selected value
23839      * @return {String}
23840      */
23841     getGroupValue : function(){
23842         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
23843     }
23844 });//<script type="text/javascript">
23845
23846 /*
23847  * Ext JS Library 1.1.1
23848  * Copyright(c) 2006-2007, Ext JS, LLC.
23849  * licensing@extjs.com
23850  * 
23851  * http://www.extjs.com/license
23852  */
23853  
23854  /*
23855   * 
23856   * Known bugs:
23857   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
23858   * - IE ? - no idea how much works there.
23859   * 
23860   * 
23861   * 
23862   */
23863  
23864
23865 /**
23866  * @class Ext.form.HtmlEditor
23867  * @extends Ext.form.Field
23868  * Provides a lightweight HTML Editor component.
23869  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
23870  * 
23871  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
23872  * supported by this editor.</b><br/><br/>
23873  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
23874  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23875  */
23876 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
23877       /**
23878      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23879      */
23880     toolbars : false,
23881     /**
23882      * @cfg {String} createLinkText The default text for the create link prompt
23883      */
23884     createLinkText : 'Please enter the URL for the link:',
23885     /**
23886      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
23887      */
23888     defaultLinkValue : 'http:/'+'/',
23889    
23890     
23891     // id of frame..
23892     frameId: false,
23893     
23894     // private properties
23895     validationEvent : false,
23896     deferHeight: true,
23897     initialized : false,
23898     activated : false,
23899     sourceEditMode : false,
23900     onFocus : Roo.emptyFn,
23901     iframePad:3,
23902     hideMode:'offsets',
23903     defaultAutoCreate : {
23904         tag: "textarea",
23905         style:"width:500px;height:300px;",
23906         autocomplete: "off"
23907     },
23908
23909     // private
23910     initComponent : function(){
23911         this.addEvents({
23912             /**
23913              * @event initialize
23914              * Fires when the editor is fully initialized (including the iframe)
23915              * @param {HtmlEditor} this
23916              */
23917             initialize: true,
23918             /**
23919              * @event activate
23920              * Fires when the editor is first receives the focus. Any insertion must wait
23921              * until after this event.
23922              * @param {HtmlEditor} this
23923              */
23924             activate: true,
23925              /**
23926              * @event beforesync
23927              * Fires before the textarea is updated with content from the editor iframe. Return false
23928              * to cancel the sync.
23929              * @param {HtmlEditor} this
23930              * @param {String} html
23931              */
23932             beforesync: true,
23933              /**
23934              * @event beforepush
23935              * Fires before the iframe editor is updated with content from the textarea. Return false
23936              * to cancel the push.
23937              * @param {HtmlEditor} this
23938              * @param {String} html
23939              */
23940             beforepush: true,
23941              /**
23942              * @event sync
23943              * Fires when the textarea is updated with content from the editor iframe.
23944              * @param {HtmlEditor} this
23945              * @param {String} html
23946              */
23947             sync: true,
23948              /**
23949              * @event push
23950              * Fires when the iframe editor is updated with content from the textarea.
23951              * @param {HtmlEditor} this
23952              * @param {String} html
23953              */
23954             push: true,
23955              /**
23956              * @event editmodechange
23957              * Fires when the editor switches edit modes
23958              * @param {HtmlEditor} this
23959              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23960              */
23961             editmodechange: true,
23962             /**
23963              * @event editorevent
23964              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23965              * @param {HtmlEditor} this
23966              */
23967             editorevent: true
23968         })
23969     },
23970
23971     /**
23972      * Protected method that will not generally be called directly. It
23973      * is called when the editor creates its toolbar. Override this method if you need to
23974      * add custom toolbar buttons.
23975      * @param {HtmlEditor} editor
23976      */
23977     createToolbar : function(editor){
23978         if (!editor.toolbars || !editor.toolbars.length) {
23979             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
23980         }
23981         
23982         for (var i =0 ; i < editor.toolbars.length;i++) {
23983             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
23984             editor.toolbars[i].init(editor);
23985         }
23986          
23987         
23988     },
23989
23990     /**
23991      * Protected method that will not generally be called directly. It
23992      * is called when the editor initializes the iframe with HTML contents. Override this method if you
23993      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23994      */
23995     getDocMarkup : function(){
23996         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
23997     },
23998
23999     // private
24000     onRender : function(ct, position){
24001         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
24002         this.el.dom.style.border = '0 none';
24003         this.el.dom.setAttribute('tabIndex', -1);
24004         this.el.addClass('x-hidden');
24005         if(Roo.isIE){ // fix IE 1px bogus margin
24006             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24007         }
24008         this.wrap = this.el.wrap({
24009             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24010         });
24011
24012         this.frameId = Roo.id();
24013         this.createToolbar(this);
24014         
24015         
24016         
24017         
24018       
24019         
24020         var iframe = this.wrap.createChild({
24021             tag: 'iframe',
24022             id: this.frameId,
24023             name: this.frameId,
24024             frameBorder : 'no',
24025             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24026         });
24027         
24028        // console.log(iframe);
24029         //this.wrap.dom.appendChild(iframe);
24030
24031         this.iframe = iframe.dom;
24032
24033          this.assignDocWin();
24034         
24035         this.doc.designMode = 'on';
24036        
24037         this.doc.open();
24038         this.doc.write(this.getDocMarkup());
24039         this.doc.close();
24040
24041         
24042         var task = { // must defer to wait for browser to be ready
24043             run : function(){
24044                 //console.log("run task?" + this.doc.readyState);
24045                 this.assignDocWin();
24046                 if(this.doc.body || this.doc.readyState == 'complete'){
24047                     try {
24048                         this.doc.designMode="on";
24049                     } catch (e) {
24050                         return;
24051                     }
24052                     Roo.TaskMgr.stop(task);
24053                     this.initEditor.defer(10, this);
24054                 }
24055             },
24056             interval : 10,
24057             duration:10000,
24058             scope: this
24059         };
24060         Roo.TaskMgr.start(task);
24061
24062         if(!this.width){
24063             this.setSize(this.el.getSize());
24064         }
24065     },
24066
24067     // private
24068     onResize : function(w, h){
24069         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
24070         if(this.el && this.iframe){
24071             if(typeof w == 'number'){
24072                 var aw = w - this.wrap.getFrameWidth('lr');
24073                 this.el.setWidth(this.adjustWidth('textarea', aw));
24074                 this.iframe.style.width = aw + 'px';
24075             }
24076             if(typeof h == 'number'){
24077                 var tbh = 0;
24078                 for (var i =0; i < this.toolbars.length;i++) {
24079                     // fixme - ask toolbars for heights?
24080                     tbh += this.toolbars[i].tb.el.getHeight();
24081                 }
24082                 
24083                 
24084                 
24085                 
24086                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24087                 this.el.setHeight(this.adjustWidth('textarea', ah));
24088                 this.iframe.style.height = ah + 'px';
24089                 if(this.doc){
24090                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
24091                 }
24092             }
24093         }
24094     },
24095
24096     /**
24097      * Toggles the editor between standard and source edit mode.
24098      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24099      */
24100     toggleSourceEdit : function(sourceEditMode){
24101         
24102         this.sourceEditMode = sourceEditMode === true;
24103         
24104         if(this.sourceEditMode){
24105           
24106             this.syncValue();
24107             this.iframe.className = 'x-hidden';
24108             this.el.removeClass('x-hidden');
24109             this.el.dom.removeAttribute('tabIndex');
24110             this.el.focus();
24111         }else{
24112              
24113             this.pushValue();
24114             this.iframe.className = '';
24115             this.el.addClass('x-hidden');
24116             this.el.dom.setAttribute('tabIndex', -1);
24117             this.deferFocus();
24118         }
24119         this.setSize(this.wrap.getSize());
24120         this.fireEvent('editmodechange', this, this.sourceEditMode);
24121     },
24122
24123     // private used internally
24124     createLink : function(){
24125         var url = prompt(this.createLinkText, this.defaultLinkValue);
24126         if(url && url != 'http:/'+'/'){
24127             this.relayCmd('createlink', url);
24128         }
24129     },
24130
24131     // private (for BoxComponent)
24132     adjustSize : Roo.BoxComponent.prototype.adjustSize,
24133
24134     // private (for BoxComponent)
24135     getResizeEl : function(){
24136         return this.wrap;
24137     },
24138
24139     // private (for BoxComponent)
24140     getPositionEl : function(){
24141         return this.wrap;
24142     },
24143
24144     // private
24145     initEvents : function(){
24146         this.originalValue = this.getValue();
24147     },
24148
24149     /**
24150      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24151      * @method
24152      */
24153     markInvalid : Roo.emptyFn,
24154     /**
24155      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24156      * @method
24157      */
24158     clearInvalid : Roo.emptyFn,
24159
24160     setValue : function(v){
24161         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
24162         this.pushValue();
24163     },
24164
24165     /**
24166      * Protected method that will not generally be called directly. If you need/want
24167      * custom HTML cleanup, this is the method you should override.
24168      * @param {String} html The HTML to be cleaned
24169      * return {String} The cleaned HTML
24170      */
24171     cleanHtml : function(html){
24172         html = String(html);
24173         if(html.length > 5){
24174             if(Roo.isSafari){ // strip safari nonsense
24175                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24176             }
24177         }
24178         if(html == '&nbsp;'){
24179             html = '';
24180         }
24181         return html;
24182     },
24183
24184     /**
24185      * Protected method that will not generally be called directly. Syncs the contents
24186      * of the editor iframe with the textarea.
24187      */
24188     syncValue : function(){
24189         if(this.initialized){
24190             var bd = (this.doc.body || this.doc.documentElement);
24191             var html = bd.innerHTML;
24192             if(Roo.isSafari){
24193                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24194                 var m = bs.match(/text-align:(.*?);/i);
24195                 if(m && m[1]){
24196                     html = '<div style="'+m[0]+'">' + html + '</div>';
24197                 }
24198             }
24199             html = this.cleanHtml(html);
24200             if(this.fireEvent('beforesync', this, html) !== false){
24201                 this.el.dom.value = html;
24202                 this.fireEvent('sync', this, html);
24203             }
24204         }
24205     },
24206
24207     /**
24208      * Protected method that will not generally be called directly. Pushes the value of the textarea
24209      * into the iframe editor.
24210      */
24211     pushValue : function(){
24212         if(this.initialized){
24213             var v = this.el.dom.value;
24214             if(v.length < 1){
24215                 v = '&#160;';
24216             }
24217             if(this.fireEvent('beforepush', this, v) !== false){
24218                 (this.doc.body || this.doc.documentElement).innerHTML = v;
24219                 this.fireEvent('push', this, v);
24220             }
24221         }
24222     },
24223
24224     // private
24225     deferFocus : function(){
24226         this.focus.defer(10, this);
24227     },
24228
24229     // doc'ed in Field
24230     focus : function(){
24231         if(this.win && !this.sourceEditMode){
24232             this.win.focus();
24233         }else{
24234             this.el.focus();
24235         }
24236     },
24237     
24238     assignDocWin: function()
24239     {
24240         var iframe = this.iframe;
24241         
24242          if(Roo.isIE){
24243             this.doc = iframe.contentWindow.document;
24244             this.win = iframe.contentWindow;
24245         } else {
24246             if (!Roo.get(this.frameId)) {
24247                 return;
24248             }
24249             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24250             this.win = Roo.get(this.frameId).dom.contentWindow;
24251         }
24252     },
24253     
24254     // private
24255     initEditor : function(){
24256         //console.log("INIT EDITOR");
24257         this.assignDocWin();
24258         
24259         
24260         
24261         this.doc.designMode="on";
24262         this.doc.open();
24263         this.doc.write(this.getDocMarkup());
24264         this.doc.close();
24265         
24266         var dbody = (this.doc.body || this.doc.documentElement);
24267         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24268         // this copies styles from the containing element into thsi one..
24269         // not sure why we need all of this..
24270         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24271         ss['background-attachment'] = 'fixed'; // w3c
24272         dbody.bgProperties = 'fixed'; // ie
24273         Roo.DomHelper.applyStyles(dbody, ss);
24274         Roo.EventManager.on(this.doc, {
24275             'mousedown': this.onEditorEvent,
24276             'dblclick': this.onEditorEvent,
24277             'click': this.onEditorEvent,
24278             'keyup': this.onEditorEvent,
24279             buffer:100,
24280             scope: this
24281         });
24282         if(Roo.isGecko){
24283             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24284         }
24285         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24286             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24287         }
24288         this.initialized = true;
24289
24290         this.fireEvent('initialize', this);
24291         this.pushValue();
24292     },
24293
24294     // private
24295     onDestroy : function(){
24296         
24297         
24298         
24299         if(this.rendered){
24300             
24301             for (var i =0; i < this.toolbars.length;i++) {
24302                 // fixme - ask toolbars for heights?
24303                 this.toolbars[i].onDestroy();
24304             }
24305             
24306             this.wrap.dom.innerHTML = '';
24307             this.wrap.remove();
24308         }
24309     },
24310
24311     // private
24312     onFirstFocus : function(){
24313         
24314         this.assignDocWin();
24315         
24316         
24317         this.activated = true;
24318         for (var i =0; i < this.toolbars.length;i++) {
24319             this.toolbars[i].onFirstFocus();
24320         }
24321        
24322         if(Roo.isGecko){ // prevent silly gecko errors
24323             this.win.focus();
24324             var s = this.win.getSelection();
24325             if(!s.focusNode || s.focusNode.nodeType != 3){
24326                 var r = s.getRangeAt(0);
24327                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24328                 r.collapse(true);
24329                 this.deferFocus();
24330             }
24331             try{
24332                 this.execCmd('useCSS', true);
24333                 this.execCmd('styleWithCSS', false);
24334             }catch(e){}
24335         }
24336         this.fireEvent('activate', this);
24337     },
24338
24339     // private
24340     adjustFont: function(btn){
24341         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24342         //if(Roo.isSafari){ // safari
24343         //    adjust *= 2;
24344        // }
24345         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24346         if(Roo.isSafari){ // safari
24347             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24348             v =  (v < 10) ? 10 : v;
24349             v =  (v > 48) ? 48 : v;
24350             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24351             
24352         }
24353         
24354         
24355         v = Math.max(1, v+adjust);
24356         
24357         this.execCmd('FontSize', v  );
24358     },
24359
24360     onEditorEvent : function(e){
24361         this.fireEvent('editorevent', this, e);
24362       //  this.updateToolbar();
24363         this.syncValue();
24364     },
24365
24366     insertTag : function(tg)
24367     {
24368         // could be a bit smarter... -> wrap the current selected tRoo..
24369         
24370         this.execCmd("formatblock",   tg);
24371         
24372     },
24373     
24374     insertText : function(txt)
24375     {
24376         
24377         
24378         range = this.createRange();
24379         range.deleteContents();
24380                //alert(Sender.getAttribute('label'));
24381                
24382         range.insertNode(this.doc.createTextNode(txt));
24383     } ,
24384     
24385     // private
24386     relayBtnCmd : function(btn){
24387         this.relayCmd(btn.cmd);
24388     },
24389
24390     /**
24391      * Executes a Midas editor command on the editor document and performs necessary focus and
24392      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24393      * @param {String} cmd The Midas command
24394      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24395      */
24396     relayCmd : function(cmd, value){
24397         this.win.focus();
24398         this.execCmd(cmd, value);
24399         this.fireEvent('editorevent', this);
24400         //this.updateToolbar();
24401         this.deferFocus();
24402     },
24403
24404     /**
24405      * Executes a Midas editor command directly on the editor document.
24406      * For visual commands, you should use {@link #relayCmd} instead.
24407      * <b>This should only be called after the editor is initialized.</b>
24408      * @param {String} cmd The Midas command
24409      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24410      */
24411     execCmd : function(cmd, value){
24412         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24413         this.syncValue();
24414     },
24415
24416    
24417     /**
24418      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24419      * to insert tRoo.
24420      * @param {String} text
24421      */
24422     insertAtCursor : function(text){
24423         if(!this.activated){
24424             return;
24425         }
24426         if(Roo.isIE){
24427             this.win.focus();
24428             var r = this.doc.selection.createRange();
24429             if(r){
24430                 r.collapse(true);
24431                 r.pasteHTML(text);
24432                 this.syncValue();
24433                 this.deferFocus();
24434             }
24435         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24436             this.win.focus();
24437             this.execCmd('InsertHTML', text);
24438             this.deferFocus();
24439         }
24440     },
24441  // private
24442     mozKeyPress : function(e){
24443         if(e.ctrlKey){
24444             var c = e.getCharCode(), cmd;
24445           
24446             if(c > 0){
24447                 c = String.fromCharCode(c).toLowerCase();
24448                 switch(c){
24449                     case 'b':
24450                         cmd = 'bold';
24451                     break;
24452                     case 'i':
24453                         cmd = 'italic';
24454                     break;
24455                     case 'u':
24456                         cmd = 'underline';
24457                     case 'v':
24458                         this.cleanUpPaste.defer(100, this);
24459                         return;
24460                     break;
24461                 }
24462                 if(cmd){
24463                     this.win.focus();
24464                     this.execCmd(cmd);
24465                     this.deferFocus();
24466                     e.preventDefault();
24467                 }
24468                 
24469             }
24470         }
24471     },
24472
24473     // private
24474     fixKeys : function(){ // load time branching for fastest keydown performance
24475         if(Roo.isIE){
24476             return function(e){
24477                 var k = e.getKey(), r;
24478                 if(k == e.TAB){
24479                     e.stopEvent();
24480                     r = this.doc.selection.createRange();
24481                     if(r){
24482                         r.collapse(true);
24483                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24484                         this.deferFocus();
24485                     }
24486                     return;
24487                 }
24488                 
24489                 if(k == e.ENTER){
24490                     r = this.doc.selection.createRange();
24491                     if(r){
24492                         var target = r.parentElement();
24493                         if(!target || target.tagName.toLowerCase() != 'li'){
24494                             e.stopEvent();
24495                             r.pasteHTML('<br />');
24496                             r.collapse(false);
24497                             r.select();
24498                         }
24499                     }
24500                 }
24501                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24502                     this.cleanUpPaste.defer(100, this);
24503                     return;
24504                 }
24505                 
24506                 
24507             };
24508         }else if(Roo.isOpera){
24509             return function(e){
24510                 var k = e.getKey();
24511                 if(k == e.TAB){
24512                     e.stopEvent();
24513                     this.win.focus();
24514                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24515                     this.deferFocus();
24516                 }
24517                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24518                     this.cleanUpPaste.defer(100, this);
24519                     return;
24520                 }
24521                 
24522             };
24523         }else if(Roo.isSafari){
24524             return function(e){
24525                 var k = e.getKey();
24526                 
24527                 if(k == e.TAB){
24528                     e.stopEvent();
24529                     this.execCmd('InsertText','\t');
24530                     this.deferFocus();
24531                     return;
24532                 }
24533                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24534                     this.cleanUpPaste.defer(100, this);
24535                     return;
24536                 }
24537                 
24538              };
24539         }
24540     }(),
24541     
24542     getAllAncestors: function()
24543     {
24544         var p = this.getSelectedNode();
24545         var a = [];
24546         if (!p) {
24547             a.push(p); // push blank onto stack..
24548             p = this.getParentElement();
24549         }
24550         
24551         
24552         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24553             a.push(p);
24554             p = p.parentNode;
24555         }
24556         a.push(this.doc.body);
24557         return a;
24558     },
24559     lastSel : false,
24560     lastSelNode : false,
24561     
24562     
24563     getSelection : function() 
24564     {
24565         this.assignDocWin();
24566         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24567     },
24568     
24569     getSelectedNode: function() 
24570     {
24571         // this may only work on Gecko!!!
24572         
24573         // should we cache this!!!!
24574         
24575         
24576         
24577          
24578         var range = this.createRange(this.getSelection());
24579         
24580         if (Roo.isIE) {
24581             var parent = range.parentElement();
24582             while (true) {
24583                 var testRange = range.duplicate();
24584                 testRange.moveToElementText(parent);
24585                 if (testRange.inRange(range)) {
24586                     break;
24587                 }
24588                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24589                     break;
24590                 }
24591                 parent = parent.parentElement;
24592             }
24593             return parent;
24594         }
24595         
24596         
24597         var ar = range.endContainer.childNodes;
24598         if (!ar.length) {
24599             ar = range.commonAncestorContainer.childNodes;
24600             //alert(ar.length);
24601         }
24602         var nodes = [];
24603         var other_nodes = [];
24604         var has_other_nodes = false;
24605         for (var i=0;i<ar.length;i++) {
24606             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24607                 continue;
24608             }
24609             // fullly contained node.
24610             
24611             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24612                 nodes.push(ar[i]);
24613                 continue;
24614             }
24615             
24616             // probably selected..
24617             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24618                 other_nodes.push(ar[i]);
24619                 continue;
24620             }
24621             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24622                 continue;
24623             }
24624             
24625             
24626             has_other_nodes = true;
24627         }
24628         if (!nodes.length && other_nodes.length) {
24629             nodes= other_nodes;
24630         }
24631         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24632             return false;
24633         }
24634         
24635         return nodes[0];
24636     },
24637     createRange: function(sel)
24638     {
24639         // this has strange effects when using with 
24640         // top toolbar - not sure if it's a great idea.
24641         //this.editor.contentWindow.focus();
24642         if (typeof sel != "undefined") {
24643             try {
24644                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24645             } catch(e) {
24646                 return this.doc.createRange();
24647             }
24648         } else {
24649             return this.doc.createRange();
24650         }
24651     },
24652     getParentElement: function()
24653     {
24654         
24655         this.assignDocWin();
24656         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24657         
24658         var range = this.createRange(sel);
24659          
24660         try {
24661             var p = range.commonAncestorContainer;
24662             while (p.nodeType == 3) { // text node
24663                 p = p.parentNode;
24664             }
24665             return p;
24666         } catch (e) {
24667             return null;
24668         }
24669     
24670     },
24671     
24672     
24673     
24674     // BC Hacks - cause I cant work out what i was trying to do..
24675     rangeIntersectsNode : function(range, node)
24676     {
24677         var nodeRange = node.ownerDocument.createRange();
24678         try {
24679             nodeRange.selectNode(node);
24680         }
24681         catch (e) {
24682             nodeRange.selectNodeContents(node);
24683         }
24684
24685         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
24686                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
24687     },
24688     rangeCompareNode : function(range, node) {
24689         var nodeRange = node.ownerDocument.createRange();
24690         try {
24691             nodeRange.selectNode(node);
24692         } catch (e) {
24693             nodeRange.selectNodeContents(node);
24694         }
24695         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
24696         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
24697
24698         if (nodeIsBefore && !nodeIsAfter)
24699             return 0;
24700         if (!nodeIsBefore && nodeIsAfter)
24701             return 1;
24702         if (nodeIsBefore && nodeIsAfter)
24703             return 2;
24704
24705         return 3;
24706     },
24707
24708     // private? - in a new class?
24709     cleanUpPaste :  function()
24710     {
24711         // cleans up the whole document..
24712       //  console.log('cleanuppaste');
24713         this.cleanUpChildren(this.doc.body)
24714         
24715         
24716     },
24717     cleanUpChildren : function (n)
24718     {
24719         if (!n.childNodes.length) {
24720             return;
24721         }
24722         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24723            this.cleanUpChild(n.childNodes[i]);
24724         }
24725     },
24726     
24727     
24728         
24729     
24730     cleanUpChild : function (node)
24731     {
24732         //console.log(node);
24733         if (node.nodeName == "#text") {
24734             // clean up silly Windows -- stuff?
24735             return; 
24736         }
24737         if (node.nodeName == "#comment") {
24738             node.parentNode.removeChild(node);
24739             // clean up silly Windows -- stuff?
24740             return; 
24741         }
24742         
24743         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
24744             // remove node.
24745             node.parentNode.removeChild(node);
24746             return;
24747             
24748         }
24749         if (!node.attributes || !node.attributes.length) {
24750             this.cleanUpChildren(node);
24751             return;
24752         }
24753         
24754         function cleanAttr(n,v)
24755         {
24756             
24757             if (v.match(/^\./) || v.match(/^\//)) {
24758                 return;
24759             }
24760             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
24761                 return;
24762             }
24763             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
24764             node.removeAttribute(n);
24765             
24766         }
24767         
24768         function cleanStyle(n,v)
24769         {
24770             if (v.match(/expression/)) { //XSS?? should we even bother..
24771                 node.removeAttribute(n);
24772                 return;
24773             }
24774             
24775             
24776             var parts = v.split(/;/);
24777             Roo.each(parts, function(p) {
24778                 p = p.replace(/\s+/g,'');
24779                 if (!p.length) {
24780                     return;
24781                 }
24782                 var l = p.split(':').shift().replace(/\s+/g,'');
24783                 
24784                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
24785                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
24786                     node.removeAttribute(n);
24787                     return false;
24788                 }
24789             });
24790             
24791             
24792         }
24793         
24794         
24795         for (var i = node.attributes.length-1; i > -1 ; i--) {
24796             var a = node.attributes[i];
24797             //console.log(a);
24798             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
24799                 node.removeAttribute(a.name);
24800                 return;
24801             }
24802             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
24803                 cleanAttr(a.name,a.value); // fixme..
24804                 return;
24805             }
24806             if (a.name == 'style') {
24807                 cleanStyle(a.name,a.value);
24808             }
24809             /// clean up MS crap..
24810             if (a.name == 'class') {
24811                 if (a.value.match(/^Mso/)) {
24812                     node.className = '';
24813                 }
24814             }
24815             
24816             // style cleanup!?
24817             // class cleanup?
24818             
24819         }
24820         
24821         
24822         this.cleanUpChildren(node);
24823         
24824         
24825     }
24826     
24827     
24828     // hide stuff that is not compatible
24829     /**
24830      * @event blur
24831      * @hide
24832      */
24833     /**
24834      * @event change
24835      * @hide
24836      */
24837     /**
24838      * @event focus
24839      * @hide
24840      */
24841     /**
24842      * @event specialkey
24843      * @hide
24844      */
24845     /**
24846      * @cfg {String} fieldClass @hide
24847      */
24848     /**
24849      * @cfg {String} focusClass @hide
24850      */
24851     /**
24852      * @cfg {String} autoCreate @hide
24853      */
24854     /**
24855      * @cfg {String} inputType @hide
24856      */
24857     /**
24858      * @cfg {String} invalidClass @hide
24859      */
24860     /**
24861      * @cfg {String} invalidText @hide
24862      */
24863     /**
24864      * @cfg {String} msgFx @hide
24865      */
24866     /**
24867      * @cfg {String} validateOnBlur @hide
24868      */
24869 });
24870
24871 Roo.form.HtmlEditor.white = [
24872         'area', 'br', 'img', 'input', 'hr', 'wbr',
24873         
24874        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
24875        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
24876        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
24877        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
24878        'table',   'ul',         'xmp', 
24879        
24880        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
24881       'thead',   'tr', 
24882      
24883       'dir', 'menu', 'ol', 'ul', 'dl',
24884        
24885       'embed',  'object'
24886 ];
24887
24888
24889 Roo.form.HtmlEditor.black = [
24890     //    'embed',  'object', // enable - backend responsiblity to clean thiese
24891         'applet', // 
24892         'base',   'basefont', 'bgsound', 'blink',  'body', 
24893         'frame',  'frameset', 'head',    'html',   'ilayer', 
24894         'iframe', 'layer',  'link',     'meta',    'object',   
24895         'script', 'style' ,'title',  'xml' // clean later..
24896 ];
24897 Roo.form.HtmlEditor.clean = [
24898     'script', 'style', 'title', 'xml'
24899 ];
24900
24901 // attributes..
24902
24903 Roo.form.HtmlEditor.ablack = [
24904     'on'
24905 ];
24906     
24907 Roo.form.HtmlEditor.aclean = [ 
24908     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
24909 ];
24910
24911 // protocols..
24912 Roo.form.HtmlEditor.pwhite= [
24913         'http',  'https',  'mailto'
24914 ];
24915
24916 Roo.form.HtmlEditor.cwhite= [
24917         'text-align',
24918         'font-size'
24919 ];
24920
24921 // <script type="text/javascript">
24922 /*
24923  * Based on
24924  * Ext JS Library 1.1.1
24925  * Copyright(c) 2006-2007, Ext JS, LLC.
24926  *  
24927  
24928  */
24929
24930 /**
24931  * @class Roo.form.HtmlEditorToolbar1
24932  * Basic Toolbar
24933  * 
24934  * Usage:
24935  *
24936  new Roo.form.HtmlEditor({
24937     ....
24938     toolbars : [
24939         new Roo.form.HtmlEditorToolbar1({
24940             disable : { fonts: 1 , format: 1, ..., ... , ...],
24941             btns : [ .... ]
24942         })
24943     }
24944      
24945  * 
24946  * @cfg {Object} disable List of elements to disable..
24947  * @cfg {Array} btns List of additional buttons.
24948  * 
24949  * 
24950  * NEEDS Extra CSS? 
24951  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24952  */
24953  
24954 Roo.form.HtmlEditor.ToolbarStandard = function(config)
24955 {
24956     
24957     Roo.apply(this, config);
24958     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24959     // dont call parent... till later.
24960 }
24961
24962 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
24963     
24964     tb: false,
24965     
24966     rendered: false,
24967     
24968     editor : false,
24969     /**
24970      * @cfg {Object} disable  List of toolbar elements to disable
24971          
24972      */
24973     disable : false,
24974       /**
24975      * @cfg {Array} fontFamilies An array of available font families
24976      */
24977     fontFamilies : [
24978         'Arial',
24979         'Courier New',
24980         'Tahoma',
24981         'Times New Roman',
24982         'Verdana'
24983     ],
24984     
24985     specialChars : [
24986            "&#169;",
24987           "&#174;",     
24988           "&#8482;",    
24989           "&#163;" ,    
24990          // "&#8212;",    
24991           "&#8230;",    
24992           "&#247;" ,    
24993         //  "&#225;" ,     ?? a acute?
24994            "&#8364;"    , //Euro
24995        //   "&#8220;"    ,
24996         //  "&#8221;"    ,
24997         //  "&#8226;"    ,
24998           "&#176;"  //   , // degrees
24999
25000          // "&#233;"     , // e ecute
25001          // "&#250;"     , // u ecute?
25002     ],
25003     inputElements : [ 
25004             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
25005             "input:submit", "input:button", "select", "textarea", "label" ],
25006     formats : [
25007         ["p"] ,  
25008         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
25009         ["pre"],[ "code"], 
25010         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
25011     ],
25012      /**
25013      * @cfg {String} defaultFont default font to use.
25014      */
25015     defaultFont: 'tahoma',
25016    
25017     fontSelect : false,
25018     
25019     
25020     formatCombo : false,
25021     
25022     init : function(editor)
25023     {
25024         this.editor = editor;
25025         
25026         
25027         var fid = editor.frameId;
25028         var etb = this;
25029         function btn(id, toggle, handler){
25030             var xid = fid + '-'+ id ;
25031             return {
25032                 id : xid,
25033                 cmd : id,
25034                 cls : 'x-btn-icon x-edit-'+id,
25035                 enableToggle:toggle !== false,
25036                 scope: editor, // was editor...
25037                 handler:handler||editor.relayBtnCmd,
25038                 clickEvent:'mousedown',
25039                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25040                 tabIndex:-1
25041             };
25042         }
25043         
25044         
25045         
25046         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
25047         this.tb = tb;
25048          // stop form submits
25049         tb.el.on('click', function(e){
25050             e.preventDefault(); // what does this do?
25051         });
25052
25053         if(!this.disable.font && !Roo.isSafari){
25054             /* why no safari for fonts
25055             editor.fontSelect = tb.el.createChild({
25056                 tag:'select',
25057                 tabIndex: -1,
25058                 cls:'x-font-select',
25059                 html: editor.createFontOptions()
25060             });
25061             editor.fontSelect.on('change', function(){
25062                 var font = editor.fontSelect.dom.value;
25063                 editor.relayCmd('fontname', font);
25064                 editor.deferFocus();
25065             }, editor);
25066             tb.add(
25067                 editor.fontSelect.dom,
25068                 '-'
25069             );
25070             */
25071         };
25072         if(!this.disable.formats){
25073             this.formatCombo = new Roo.form.ComboBox({
25074                 store: new Roo.data.SimpleStore({
25075                     id : 'tag',
25076                     fields: ['tag'],
25077                     data : this.formats // from states.js
25078                 }),
25079                 blockFocus : true,
25080                 //autoCreate : {tag: "div",  size: "20"},
25081                 displayField:'tag',
25082                 typeAhead: false,
25083                 mode: 'local',
25084                 editable : false,
25085                 triggerAction: 'all',
25086                 emptyText:'Add tag',
25087                 selectOnFocus:true,
25088                 width:135,
25089                 listeners : {
25090                     'select': function(c, r, i) {
25091                         editor.insertTag(r.get('tag'));
25092                         editor.focus();
25093                     }
25094                 }
25095
25096             });
25097             tb.addField(this.formatCombo);
25098             
25099         }
25100         
25101         if(!this.disable.format){
25102             tb.add(
25103                 btn('bold'),
25104                 btn('italic'),
25105                 btn('underline')
25106             );
25107         };
25108         if(!this.disable.fontSize){
25109             tb.add(
25110                 '-',
25111                 
25112                 
25113                 btn('increasefontsize', false, editor.adjustFont),
25114                 btn('decreasefontsize', false, editor.adjustFont)
25115             );
25116         };
25117         
25118         
25119         if(this.disable.colors){
25120             tb.add(
25121                 '-', {
25122                     id:editor.frameId +'-forecolor',
25123                     cls:'x-btn-icon x-edit-forecolor',
25124                     clickEvent:'mousedown',
25125                     tooltip: this.buttonTips['forecolor'] || undefined,
25126                     tabIndex:-1,
25127                     menu : new Roo.menu.ColorMenu({
25128                         allowReselect: true,
25129                         focus: Roo.emptyFn,
25130                         value:'000000',
25131                         plain:true,
25132                         selectHandler: function(cp, color){
25133                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
25134                             editor.deferFocus();
25135                         },
25136                         scope: editor,
25137                         clickEvent:'mousedown'
25138                     })
25139                 }, {
25140                     id:editor.frameId +'backcolor',
25141                     cls:'x-btn-icon x-edit-backcolor',
25142                     clickEvent:'mousedown',
25143                     tooltip: this.buttonTips['backcolor'] || undefined,
25144                     tabIndex:-1,
25145                     menu : new Roo.menu.ColorMenu({
25146                         focus: Roo.emptyFn,
25147                         value:'FFFFFF',
25148                         plain:true,
25149                         allowReselect: true,
25150                         selectHandler: function(cp, color){
25151                             if(Roo.isGecko){
25152                                 editor.execCmd('useCSS', false);
25153                                 editor.execCmd('hilitecolor', color);
25154                                 editor.execCmd('useCSS', true);
25155                                 editor.deferFocus();
25156                             }else{
25157                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
25158                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
25159                                 editor.deferFocus();
25160                             }
25161                         },
25162                         scope:editor,
25163                         clickEvent:'mousedown'
25164                     })
25165                 }
25166             );
25167         };
25168         // now add all the items...
25169         
25170
25171         if(!this.disable.alignments){
25172             tb.add(
25173                 '-',
25174                 btn('justifyleft'),
25175                 btn('justifycenter'),
25176                 btn('justifyright')
25177             );
25178         };
25179
25180         //if(!Roo.isSafari){
25181             if(!this.disable.links){
25182                 tb.add(
25183                     '-',
25184                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
25185                 );
25186             };
25187
25188             if(!this.disable.lists){
25189                 tb.add(
25190                     '-',
25191                     btn('insertorderedlist'),
25192                     btn('insertunorderedlist')
25193                 );
25194             }
25195             if(!this.disable.sourceEdit){
25196                 tb.add(
25197                     '-',
25198                     btn('sourceedit', true, function(btn){
25199                         this.toggleSourceEdit(btn.pressed);
25200                     })
25201                 );
25202             }
25203         //}
25204         
25205         var smenu = { };
25206         // special menu.. - needs to be tidied up..
25207         if (!this.disable.special) {
25208             smenu = {
25209                 text: "&#169;",
25210                 cls: 'x-edit-none',
25211                 menu : {
25212                     items : []
25213                    }
25214             };
25215             for (var i =0; i < this.specialChars.length; i++) {
25216                 smenu.menu.items.push({
25217                     
25218                     html: this.specialChars[i],
25219                     handler: function(a,b) {
25220                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
25221                         
25222                     },
25223                     tabIndex:-1
25224                 });
25225             }
25226             
25227             
25228             tb.add(smenu);
25229             
25230             
25231         }
25232         if (this.btns) {
25233             for(var i =0; i< this.btns.length;i++) {
25234                 var b = this.btns[i];
25235                 b.cls =  'x-edit-none';
25236                 b.scope = editor;
25237                 tb.add(b);
25238             }
25239         
25240         }
25241         
25242         
25243         
25244         // disable everything...
25245         
25246         this.tb.items.each(function(item){
25247            if(item.id != editor.frameId+ '-sourceedit'){
25248                 item.disable();
25249             }
25250         });
25251         this.rendered = true;
25252         
25253         // the all the btns;
25254         editor.on('editorevent', this.updateToolbar, this);
25255         // other toolbars need to implement this..
25256         //editor.on('editmodechange', this.updateToolbar, this);
25257     },
25258     
25259     
25260     
25261     /**
25262      * Protected method that will not generally be called directly. It triggers
25263      * a toolbar update by reading the markup state of the current selection in the editor.
25264      */
25265     updateToolbar: function(){
25266
25267         if(!this.editor.activated){
25268             this.editor.onFirstFocus();
25269             return;
25270         }
25271
25272         var btns = this.tb.items.map, 
25273             doc = this.editor.doc,
25274             frameId = this.editor.frameId;
25275
25276         if(!this.disable.font && !Roo.isSafari){
25277             /*
25278             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
25279             if(name != this.fontSelect.dom.value){
25280                 this.fontSelect.dom.value = name;
25281             }
25282             */
25283         }
25284         if(!this.disable.format){
25285             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
25286             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
25287             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
25288         }
25289         if(!this.disable.alignments){
25290             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
25291             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
25292             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
25293         }
25294         if(!Roo.isSafari && !this.disable.lists){
25295             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
25296             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
25297         }
25298         
25299         var ans = this.editor.getAllAncestors();
25300         if (this.formatCombo) {
25301             
25302             
25303             var store = this.formatCombo.store;
25304             this.formatCombo.setValue("");
25305             for (var i =0; i < ans.length;i++) {
25306                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25307                     // select it..
25308                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
25309                     break;
25310                 }
25311             }
25312         }
25313         
25314         
25315         
25316         // hides menus... - so this cant be on a menu...
25317         Roo.menu.MenuMgr.hideAll();
25318
25319         //this.editorsyncValue();
25320     },
25321    
25322     
25323     createFontOptions : function(){
25324         var buf = [], fs = this.fontFamilies, ff, lc;
25325         for(var i = 0, len = fs.length; i< len; i++){
25326             ff = fs[i];
25327             lc = ff.toLowerCase();
25328             buf.push(
25329                 '<option value="',lc,'" style="font-family:',ff,';"',
25330                     (this.defaultFont == lc ? ' selected="true">' : '>'),
25331                     ff,
25332                 '</option>'
25333             );
25334         }
25335         return buf.join('');
25336     },
25337     
25338     toggleSourceEdit : function(sourceEditMode){
25339         if(sourceEditMode === undefined){
25340             sourceEditMode = !this.sourceEditMode;
25341         }
25342         this.sourceEditMode = sourceEditMode === true;
25343         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
25344         // just toggle the button?
25345         if(btn.pressed !== this.editor.sourceEditMode){
25346             btn.toggle(this.editor.sourceEditMode);
25347             return;
25348         }
25349         
25350         if(this.sourceEditMode){
25351             this.tb.items.each(function(item){
25352                 if(item.cmd != 'sourceedit'){
25353                     item.disable();
25354                 }
25355             });
25356           
25357         }else{
25358             if(this.initialized){
25359                 this.tb.items.each(function(item){
25360                     item.enable();
25361                 });
25362             }
25363             
25364         }
25365         // tell the editor that it's been pressed..
25366         this.editor.toggleSourceEdit(sourceEditMode);
25367        
25368     },
25369      /**
25370      * Object collection of toolbar tooltips for the buttons in the editor. The key
25371      * is the command id associated with that button and the value is a valid QuickTips object.
25372      * For example:
25373 <pre><code>
25374 {
25375     bold : {
25376         title: 'Bold (Ctrl+B)',
25377         text: 'Make the selected text bold.',
25378         cls: 'x-html-editor-tip'
25379     },
25380     italic : {
25381         title: 'Italic (Ctrl+I)',
25382         text: 'Make the selected text italic.',
25383         cls: 'x-html-editor-tip'
25384     },
25385     ...
25386 </code></pre>
25387     * @type Object
25388      */
25389     buttonTips : {
25390         bold : {
25391             title: 'Bold (Ctrl+B)',
25392             text: 'Make the selected text bold.',
25393             cls: 'x-html-editor-tip'
25394         },
25395         italic : {
25396             title: 'Italic (Ctrl+I)',
25397             text: 'Make the selected text italic.',
25398             cls: 'x-html-editor-tip'
25399         },
25400         underline : {
25401             title: 'Underline (Ctrl+U)',
25402             text: 'Underline the selected text.',
25403             cls: 'x-html-editor-tip'
25404         },
25405         increasefontsize : {
25406             title: 'Grow Text',
25407             text: 'Increase the font size.',
25408             cls: 'x-html-editor-tip'
25409         },
25410         decreasefontsize : {
25411             title: 'Shrink Text',
25412             text: 'Decrease the font size.',
25413             cls: 'x-html-editor-tip'
25414         },
25415         backcolor : {
25416             title: 'Text Highlight Color',
25417             text: 'Change the background color of the selected text.',
25418             cls: 'x-html-editor-tip'
25419         },
25420         forecolor : {
25421             title: 'Font Color',
25422             text: 'Change the color of the selected text.',
25423             cls: 'x-html-editor-tip'
25424         },
25425         justifyleft : {
25426             title: 'Align Text Left',
25427             text: 'Align text to the left.',
25428             cls: 'x-html-editor-tip'
25429         },
25430         justifycenter : {
25431             title: 'Center Text',
25432             text: 'Center text in the editor.',
25433             cls: 'x-html-editor-tip'
25434         },
25435         justifyright : {
25436             title: 'Align Text Right',
25437             text: 'Align text to the right.',
25438             cls: 'x-html-editor-tip'
25439         },
25440         insertunorderedlist : {
25441             title: 'Bullet List',
25442             text: 'Start a bulleted list.',
25443             cls: 'x-html-editor-tip'
25444         },
25445         insertorderedlist : {
25446             title: 'Numbered List',
25447             text: 'Start a numbered list.',
25448             cls: 'x-html-editor-tip'
25449         },
25450         createlink : {
25451             title: 'Hyperlink',
25452             text: 'Make the selected text a hyperlink.',
25453             cls: 'x-html-editor-tip'
25454         },
25455         sourceedit : {
25456             title: 'Source Edit',
25457             text: 'Switch to source editing mode.',
25458             cls: 'x-html-editor-tip'
25459         }
25460     },
25461     // private
25462     onDestroy : function(){
25463         if(this.rendered){
25464             
25465             this.tb.items.each(function(item){
25466                 if(item.menu){
25467                     item.menu.removeAll();
25468                     if(item.menu.el){
25469                         item.menu.el.destroy();
25470                     }
25471                 }
25472                 item.destroy();
25473             });
25474              
25475         }
25476     },
25477     onFirstFocus: function() {
25478         this.tb.items.each(function(item){
25479            item.enable();
25480         });
25481     }
25482 });
25483
25484
25485
25486
25487 // <script type="text/javascript">
25488 /*
25489  * Based on
25490  * Ext JS Library 1.1.1
25491  * Copyright(c) 2006-2007, Ext JS, LLC.
25492  *  
25493  
25494  */
25495
25496  
25497 /**
25498  * @class Roo.form.HtmlEditor.ToolbarContext
25499  * Context Toolbar
25500  * 
25501  * Usage:
25502  *
25503  new Roo.form.HtmlEditor({
25504     ....
25505     toolbars : [
25506         new Roo.form.HtmlEditor.ToolbarStandard(),
25507         new Roo.form.HtmlEditor.ToolbarContext()
25508         })
25509     }
25510      
25511  * 
25512  * @config : {Object} disable List of elements to disable.. (not done yet.)
25513  * 
25514  * 
25515  */
25516
25517 Roo.form.HtmlEditor.ToolbarContext = function(config)
25518 {
25519     
25520     Roo.apply(this, config);
25521     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25522     // dont call parent... till later.
25523 }
25524 Roo.form.HtmlEditor.ToolbarContext.types = {
25525     'IMG' : {
25526         width : {
25527             title: "Width",
25528             width: 40
25529         },
25530         height:  {
25531             title: "Height",
25532             width: 40
25533         },
25534         align: {
25535             title: "Align",
25536             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
25537             width : 80
25538             
25539         },
25540         border: {
25541             title: "Border",
25542             width: 40
25543         },
25544         alt: {
25545             title: "Alt",
25546             width: 120
25547         },
25548         src : {
25549             title: "Src",
25550             width: 220
25551         }
25552         
25553     },
25554     'A' : {
25555         name : {
25556             title: "Name",
25557             width: 50
25558         },
25559         href:  {
25560             title: "Href",
25561             width: 220
25562         } // border?
25563         
25564     },
25565     'TABLE' : {
25566         rows : {
25567             title: "Rows",
25568             width: 20
25569         },
25570         cols : {
25571             title: "Cols",
25572             width: 20
25573         },
25574         width : {
25575             title: "Width",
25576             width: 40
25577         },
25578         height : {
25579             title: "Height",
25580             width: 40
25581         },
25582         border : {
25583             title: "Border",
25584             width: 20
25585         }
25586     },
25587     'TD' : {
25588         width : {
25589             title: "Width",
25590             width: 40
25591         },
25592         height : {
25593             title: "Height",
25594             width: 40
25595         },   
25596         align: {
25597             title: "Align",
25598             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
25599             width: 40
25600         },
25601         valign: {
25602             title: "Valign",
25603             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
25604             width: 40
25605         },
25606         colspan: {
25607             title: "Colspan",
25608             width: 20
25609             
25610         }
25611     },
25612     'INPUT' : {
25613         name : {
25614             title: "name",
25615             width: 120
25616         },
25617         value : {
25618             title: "Value",
25619             width: 120
25620         },
25621         width : {
25622             title: "Width",
25623             width: 40
25624         }
25625     },
25626     'LABEL' : {
25627         'for' : {
25628             title: "For",
25629             width: 120
25630         }
25631     },
25632     'TEXTAREA' : {
25633           name : {
25634             title: "name",
25635             width: 120
25636         },
25637         rows : {
25638             title: "Rows",
25639             width: 20
25640         },
25641         cols : {
25642             title: "Cols",
25643             width: 20
25644         }
25645     },
25646     'SELECT' : {
25647         name : {
25648             title: "name",
25649             width: 120
25650         },
25651         selectoptions : {
25652             title: "Options",
25653             width: 200
25654         }
25655     },
25656     'BODY' : {
25657         title : {
25658             title: "title",
25659             width: 120,
25660             disabled : true
25661         }
25662     }
25663 };
25664
25665
25666
25667 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
25668     
25669     tb: false,
25670     
25671     rendered: false,
25672     
25673     editor : false,
25674     /**
25675      * @cfg {Object} disable  List of toolbar elements to disable
25676          
25677      */
25678     disable : false,
25679     
25680     
25681     
25682     toolbars : false,
25683     
25684     init : function(editor)
25685     {
25686         this.editor = editor;
25687         
25688         
25689         var fid = editor.frameId;
25690         var etb = this;
25691         function btn(id, toggle, handler){
25692             var xid = fid + '-'+ id ;
25693             return {
25694                 id : xid,
25695                 cmd : id,
25696                 cls : 'x-btn-icon x-edit-'+id,
25697                 enableToggle:toggle !== false,
25698                 scope: editor, // was editor...
25699                 handler:handler||editor.relayBtnCmd,
25700                 clickEvent:'mousedown',
25701                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25702                 tabIndex:-1
25703             };
25704         }
25705         // create a new element.
25706         var wdiv = editor.wrap.createChild({
25707                 tag: 'div'
25708             }, editor.wrap.dom.firstChild.nextSibling, true);
25709         
25710         // can we do this more than once??
25711         
25712          // stop form submits
25713       
25714  
25715         // disable everything...
25716         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25717         this.toolbars = {};
25718            
25719         for (var i in  ty) {
25720           
25721             this.toolbars[i] = this.buildToolbar(ty[i],i);
25722         }
25723         this.tb = this.toolbars.BODY;
25724         this.tb.el.show();
25725         
25726          
25727         this.rendered = true;
25728         
25729         // the all the btns;
25730         editor.on('editorevent', this.updateToolbar, this);
25731         // other toolbars need to implement this..
25732         //editor.on('editmodechange', this.updateToolbar, this);
25733     },
25734     
25735     
25736     
25737     /**
25738      * Protected method that will not generally be called directly. It triggers
25739      * a toolbar update by reading the markup state of the current selection in the editor.
25740      */
25741     updateToolbar: function(){
25742
25743         if(!this.editor.activated){
25744             this.editor.onFirstFocus();
25745             return;
25746         }
25747
25748         
25749         var ans = this.editor.getAllAncestors();
25750         
25751         // pick
25752         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25753         var sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
25754         sel = sel ? sel : this.editor.doc.body;
25755         sel = sel.tagName.length ? sel : this.editor.doc.body;
25756         var tn = sel.tagName.toUpperCase();
25757         sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
25758         tn = sel.tagName.toUpperCase();
25759         if (this.tb.name  == tn) {
25760             return; // no change
25761         }
25762         this.tb.el.hide();
25763         ///console.log("show: " + tn);
25764         this.tb =  this.toolbars[tn];
25765         this.tb.el.show();
25766         this.tb.fields.each(function(e) {
25767             e.setValue(sel.getAttribute(e.name));
25768         });
25769         this.tb.selectedNode = sel;
25770         
25771         
25772         Roo.menu.MenuMgr.hideAll();
25773
25774         //this.editorsyncValue();
25775     },
25776    
25777        
25778     // private
25779     onDestroy : function(){
25780         if(this.rendered){
25781             
25782             this.tb.items.each(function(item){
25783                 if(item.menu){
25784                     item.menu.removeAll();
25785                     if(item.menu.el){
25786                         item.menu.el.destroy();
25787                     }
25788                 }
25789                 item.destroy();
25790             });
25791              
25792         }
25793     },
25794     onFirstFocus: function() {
25795         // need to do this for all the toolbars..
25796         this.tb.items.each(function(item){
25797            item.enable();
25798         });
25799     },
25800     buildToolbar: function(tlist, nm)
25801     {
25802         var editor = this.editor;
25803          // create a new element.
25804         var wdiv = editor.wrap.createChild({
25805                 tag: 'div'
25806             }, editor.wrap.dom.firstChild.nextSibling, true);
25807         
25808        
25809         var tb = new Roo.Toolbar(wdiv);
25810         tb.add(nm+ ":&nbsp;");
25811         for (var i in tlist) {
25812             var item = tlist[i];
25813             tb.add(item.title + ":&nbsp;");
25814             if (item.opts) {
25815                 // fixme
25816                 
25817               
25818                 tb.addField( new Roo.form.ComboBox({
25819                     store: new Roo.data.SimpleStore({
25820                         id : 'val',
25821                         fields: ['val'],
25822                         data : item.opts // from states.js
25823                     }),
25824                     name : i,
25825                     displayField:'val',
25826                     typeAhead: false,
25827                     mode: 'local',
25828                     editable : false,
25829                     triggerAction: 'all',
25830                     emptyText:'Select',
25831                     selectOnFocus:true,
25832                     width: item.width ? item.width  : 130,
25833                     listeners : {
25834                         'select': function(c, r, i) {
25835                             tb.selectedNode.setAttribute(c.name, r.get('val'));
25836                         }
25837                     }
25838
25839                 }));
25840                 continue;
25841                     
25842                 
25843                 
25844                 
25845                 
25846                 tb.addField( new Roo.form.TextField({
25847                     name: i,
25848                     width: 100,
25849                     //allowBlank:false,
25850                     value: ''
25851                 }));
25852                 continue;
25853             }
25854             tb.addField( new Roo.form.TextField({
25855                 name: i,
25856                 width: item.width,
25857                 //allowBlank:true,
25858                 value: '',
25859                 listeners: {
25860                     'change' : function(f, nv, ov) {
25861                         tb.selectedNode.setAttribute(f.name, nv);
25862                     }
25863                 }
25864             }));
25865              
25866         }
25867         tb.el.on('click', function(e){
25868             e.preventDefault(); // what does this do?
25869         });
25870         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
25871         tb.el.hide();
25872         tb.name = nm;
25873         // dont need to disable them... as they will get hidden
25874         return tb;
25875          
25876         
25877     }
25878     
25879     
25880     
25881     
25882 });
25883
25884
25885
25886
25887
25888 /*
25889  * Based on:
25890  * Ext JS Library 1.1.1
25891  * Copyright(c) 2006-2007, Ext JS, LLC.
25892  *
25893  * Originally Released Under LGPL - original licence link has changed is not relivant.
25894  *
25895  * Fork - LGPL
25896  * <script type="text/javascript">
25897  */
25898  
25899 /**
25900  * @class Roo.form.BasicForm
25901  * @extends Roo.util.Observable
25902  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
25903  * @constructor
25904  * @param {String/HTMLElement/Roo.Element} el The form element or its id
25905  * @param {Object} config Configuration options
25906  */
25907 Roo.form.BasicForm = function(el, config){
25908     this.allItems = [];
25909     this.childForms = [];
25910     Roo.apply(this, config);
25911     /*
25912      * The Roo.form.Field items in this form.
25913      * @type MixedCollection
25914      */
25915      
25916      
25917     this.items = new Roo.util.MixedCollection(false, function(o){
25918         return o.id || (o.id = Roo.id());
25919     });
25920     this.addEvents({
25921         /**
25922          * @event beforeaction
25923          * Fires before any action is performed. Return false to cancel the action.
25924          * @param {Form} this
25925          * @param {Action} action The action to be performed
25926          */
25927         beforeaction: true,
25928         /**
25929          * @event actionfailed
25930          * Fires when an action fails.
25931          * @param {Form} this
25932          * @param {Action} action The action that failed
25933          */
25934         actionfailed : true,
25935         /**
25936          * @event actioncomplete
25937          * Fires when an action is completed.
25938          * @param {Form} this
25939          * @param {Action} action The action that completed
25940          */
25941         actioncomplete : true
25942     });
25943     if(el){
25944         this.initEl(el);
25945     }
25946     Roo.form.BasicForm.superclass.constructor.call(this);
25947 };
25948
25949 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
25950     /**
25951      * @cfg {String} method
25952      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
25953      */
25954     /**
25955      * @cfg {DataReader} reader
25956      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
25957      * This is optional as there is built-in support for processing JSON.
25958      */
25959     /**
25960      * @cfg {DataReader} errorReader
25961      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
25962      * This is completely optional as there is built-in support for processing JSON.
25963      */
25964     /**
25965      * @cfg {String} url
25966      * The URL to use for form actions if one isn't supplied in the action options.
25967      */
25968     /**
25969      * @cfg {Boolean} fileUpload
25970      * Set to true if this form is a file upload.
25971      */
25972     /**
25973      * @cfg {Object} baseParams
25974      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
25975      */
25976     /**
25977      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
25978      */
25979     timeout: 30,
25980
25981     // private
25982     activeAction : null,
25983
25984     /**
25985      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
25986      * or setValues() data instead of when the form was first created.
25987      */
25988     trackResetOnLoad : false,
25989     
25990     
25991     /**
25992      * childForms - used for multi-tab forms
25993      * @type {Array}
25994      */
25995     childForms : false,
25996     
25997     /**
25998      * allItems - full list of fields.
25999      * @type {Array}
26000      */
26001     allItems : false,
26002     
26003     /**
26004      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
26005      * element by passing it or its id or mask the form itself by passing in true.
26006      * @type Mixed
26007      */
26008     waitMsgTarget : undefined,
26009
26010     // private
26011     initEl : function(el){
26012         this.el = Roo.get(el);
26013         this.id = this.el.id || Roo.id();
26014         this.el.on('submit', this.onSubmit, this);
26015         this.el.addClass('x-form');
26016     },
26017
26018     // private
26019     onSubmit : function(e){
26020         e.stopEvent();
26021     },
26022
26023     /**
26024      * Returns true if client-side validation on the form is successful.
26025      * @return Boolean
26026      */
26027     isValid : function(){
26028         var valid = true;
26029         this.items.each(function(f){
26030            if(!f.validate()){
26031                valid = false;
26032            }
26033         });
26034         return valid;
26035     },
26036
26037     /**
26038      * Returns true if any fields in this form have changed since their original load.
26039      * @return Boolean
26040      */
26041     isDirty : function(){
26042         var dirty = false;
26043         this.items.each(function(f){
26044            if(f.isDirty()){
26045                dirty = true;
26046                return false;
26047            }
26048         });
26049         return dirty;
26050     },
26051
26052     /**
26053      * Performs a predefined action (submit or load) or custom actions you define on this form.
26054      * @param {String} actionName The name of the action type
26055      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
26056      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
26057      * accept other config options):
26058      * <pre>
26059 Property          Type             Description
26060 ----------------  ---------------  ----------------------------------------------------------------------------------
26061 url               String           The url for the action (defaults to the form's url)
26062 method            String           The form method to use (defaults to the form's method, or POST if not defined)
26063 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
26064 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
26065                                    validate the form on the client (defaults to false)
26066      * </pre>
26067      * @return {BasicForm} this
26068      */
26069     doAction : function(action, options){
26070         if(typeof action == 'string'){
26071             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
26072         }
26073         if(this.fireEvent('beforeaction', this, action) !== false){
26074             this.beforeAction(action);
26075             action.run.defer(100, action);
26076         }
26077         return this;
26078     },
26079
26080     /**
26081      * Shortcut to do a submit action.
26082      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26083      * @return {BasicForm} this
26084      */
26085     submit : function(options){
26086         this.doAction('submit', options);
26087         return this;
26088     },
26089
26090     /**
26091      * Shortcut to do a load action.
26092      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26093      * @return {BasicForm} this
26094      */
26095     load : function(options){
26096         this.doAction('load', options);
26097         return this;
26098     },
26099
26100     /**
26101      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
26102      * @param {Record} record The record to edit
26103      * @return {BasicForm} this
26104      */
26105     updateRecord : function(record){
26106         record.beginEdit();
26107         var fs = record.fields;
26108         fs.each(function(f){
26109             var field = this.findField(f.name);
26110             if(field){
26111                 record.set(f.name, field.getValue());
26112             }
26113         }, this);
26114         record.endEdit();
26115         return this;
26116     },
26117
26118     /**
26119      * Loads an Roo.data.Record into this form.
26120      * @param {Record} record The record to load
26121      * @return {BasicForm} this
26122      */
26123     loadRecord : function(record){
26124         this.setValues(record.data);
26125         return this;
26126     },
26127
26128     // private
26129     beforeAction : function(action){
26130         var o = action.options;
26131         if(o.waitMsg){
26132             if(this.waitMsgTarget === true){
26133                 this.el.mask(o.waitMsg, 'x-mask-loading');
26134             }else if(this.waitMsgTarget){
26135                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
26136                 this.waitMsgTarget.mask(o.waitMsg, 'x-mask-loading');
26137             }else{
26138                 Roo.MessageBox.wait(o.waitMsg, o.waitTitle || this.waitTitle || 'Please Wait...');
26139             }
26140         }
26141     },
26142
26143     // private
26144     afterAction : function(action, success){
26145         this.activeAction = null;
26146         var o = action.options;
26147         if(o.waitMsg){
26148             if(this.waitMsgTarget === true){
26149                 this.el.unmask();
26150             }else if(this.waitMsgTarget){
26151                 this.waitMsgTarget.unmask();
26152             }else{
26153                 Roo.MessageBox.updateProgress(1);
26154                 Roo.MessageBox.hide();
26155             }
26156         }
26157         if(success){
26158             if(o.reset){
26159                 this.reset();
26160             }
26161             Roo.callback(o.success, o.scope, [this, action]);
26162             this.fireEvent('actioncomplete', this, action);
26163         }else{
26164             Roo.callback(o.failure, o.scope, [this, action]);
26165             this.fireEvent('actionfailed', this, action);
26166         }
26167     },
26168
26169     /**
26170      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
26171      * @param {String} id The value to search for
26172      * @return Field
26173      */
26174     findField : function(id){
26175         var field = this.items.get(id);
26176         if(!field){
26177             this.items.each(function(f){
26178                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
26179                     field = f;
26180                     return false;
26181                 }
26182             });
26183         }
26184         return field || null;
26185     },
26186
26187     /**
26188      * Add a secondary form to this one, 
26189      * Used to provide tabbed forms. One form is primary, with hidden values 
26190      * which mirror the elements from the other forms.
26191      * 
26192      * @param {Roo.form.Form} form to add.
26193      * 
26194      */
26195     addForm : function(form)
26196     {
26197        
26198         if (this.childForms.indexOf(form) > -1) {
26199             // already added..
26200             return;
26201         }
26202         this.childForms.push(form);
26203         var n = '';
26204         Roo.each(form.allItems, function (fe) {
26205             
26206             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
26207             if (this.findField(n)) { // already added..
26208                 return;
26209             }
26210             var add = new Roo.form.Hidden({
26211                 name : n
26212             });
26213             add.render(this.el);
26214             
26215             this.add( add );
26216         }, this);
26217         
26218     },
26219     /**
26220      * Mark fields in this form invalid in bulk.
26221      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
26222      * @return {BasicForm} this
26223      */
26224     markInvalid : function(errors){
26225         if(errors instanceof Array){
26226             for(var i = 0, len = errors.length; i < len; i++){
26227                 var fieldError = errors[i];
26228                 var f = this.findField(fieldError.id);
26229                 if(f){
26230                     f.markInvalid(fieldError.msg);
26231                 }
26232             }
26233         }else{
26234             var field, id;
26235             for(id in errors){
26236                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
26237                     field.markInvalid(errors[id]);
26238                 }
26239             }
26240         }
26241         Roo.each(this.childForms || [], function (f) {
26242             f.markInvalid(errors);
26243         });
26244         
26245         return this;
26246     },
26247
26248     /**
26249      * Set values for fields in this form in bulk.
26250      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
26251      * @return {BasicForm} this
26252      */
26253     setValues : function(values){
26254         if(values instanceof Array){ // array of objects
26255             for(var i = 0, len = values.length; i < len; i++){
26256                 var v = values[i];
26257                 var f = this.findField(v.id);
26258                 if(f){
26259                     f.setValue(v.value);
26260                     if(this.trackResetOnLoad){
26261                         f.originalValue = f.getValue();
26262                     }
26263                 }
26264             }
26265         }else{ // object hash
26266             var field, id;
26267             for(id in values){
26268                 if(typeof values[id] != 'function' && (field = this.findField(id))){
26269                     
26270                     if (field.setFromData && 
26271                         field.valueField && 
26272                         field.displayField &&
26273                         // combos' with local stores can 
26274                         // be queried via setValue()
26275                         // to set their value..
26276                         (field.store && !field.store.isLocal)
26277                         ) {
26278                         // it's a combo
26279                         var sd = { };
26280                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
26281                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
26282                         field.setFromData(sd);
26283                         
26284                     } else {
26285                         field.setValue(values[id]);
26286                     }
26287                     
26288                     
26289                     if(this.trackResetOnLoad){
26290                         field.originalValue = field.getValue();
26291                     }
26292                 }
26293             }
26294         }
26295          
26296         Roo.each(this.childForms || [], function (f) {
26297             f.setValues(values);
26298         });
26299                 
26300         return this;
26301     },
26302
26303     /**
26304      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
26305      * they are returned as an array.
26306      * @param {Boolean} asString
26307      * @return {Object}
26308      */
26309     getValues : function(asString){
26310         if (this.childForms) {
26311             // copy values from the child forms
26312             Roo.each(this.childForms, function (f) {
26313                 this.setValues(f.getValues());
26314             }, this);
26315         }
26316         
26317         
26318         
26319         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
26320         if(asString === true){
26321             return fs;
26322         }
26323         return Roo.urlDecode(fs);
26324     },
26325     
26326     /**
26327      * Returns the fields in this form as an object with key/value pairs. 
26328      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
26329      * @return {Object}
26330      */
26331     getFieldValues : function()
26332     {
26333         if (this.childForms) {
26334             // copy values from the child forms
26335             Roo.each(this.childForms, function (f) {
26336                 this.setValues(f.getValues());
26337             }, this);
26338         }
26339         
26340         var ret = {};
26341         this.items.each(function(f){
26342             if (!f.getName()) {
26343                 return;
26344             }
26345             var v = f.getValue();
26346             if ((typeof(v) == 'object') && f.getRawValue) {
26347                 v = f.getRawValue() ; // dates..
26348             }
26349             ret[f.getName()] = v;
26350         });
26351         
26352         return ret;
26353     },
26354
26355     /**
26356      * Clears all invalid messages in this form.
26357      * @return {BasicForm} this
26358      */
26359     clearInvalid : function(){
26360         this.items.each(function(f){
26361            f.clearInvalid();
26362         });
26363         
26364         Roo.each(this.childForms || [], function (f) {
26365             f.clearInvalid();
26366         });
26367         
26368         
26369         return this;
26370     },
26371
26372     /**
26373      * Resets this form.
26374      * @return {BasicForm} this
26375      */
26376     reset : function(){
26377         this.items.each(function(f){
26378             f.reset();
26379         });
26380         
26381         Roo.each(this.childForms || [], function (f) {
26382             f.reset();
26383         });
26384        
26385         
26386         return this;
26387     },
26388
26389     /**
26390      * Add Roo.form components to this form.
26391      * @param {Field} field1
26392      * @param {Field} field2 (optional)
26393      * @param {Field} etc (optional)
26394      * @return {BasicForm} this
26395      */
26396     add : function(){
26397         this.items.addAll(Array.prototype.slice.call(arguments, 0));
26398         return this;
26399     },
26400
26401
26402     /**
26403      * Removes a field from the items collection (does NOT remove its markup).
26404      * @param {Field} field
26405      * @return {BasicForm} this
26406      */
26407     remove : function(field){
26408         this.items.remove(field);
26409         return this;
26410     },
26411
26412     /**
26413      * Looks at the fields in this form, checks them for an id attribute,
26414      * and calls applyTo on the existing dom element with that id.
26415      * @return {BasicForm} this
26416      */
26417     render : function(){
26418         this.items.each(function(f){
26419             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
26420                 f.applyTo(f.id);
26421             }
26422         });
26423         return this;
26424     },
26425
26426     /**
26427      * Calls {@link Ext#apply} for all fields in this form with the passed object.
26428      * @param {Object} values
26429      * @return {BasicForm} this
26430      */
26431     applyToFields : function(o){
26432         this.items.each(function(f){
26433            Roo.apply(f, o);
26434         });
26435         return this;
26436     },
26437
26438     /**
26439      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
26440      * @param {Object} values
26441      * @return {BasicForm} this
26442      */
26443     applyIfToFields : function(o){
26444         this.items.each(function(f){
26445            Roo.applyIf(f, o);
26446         });
26447         return this;
26448     }
26449 });
26450
26451 // back compat
26452 Roo.BasicForm = Roo.form.BasicForm;/*
26453  * Based on:
26454  * Ext JS Library 1.1.1
26455  * Copyright(c) 2006-2007, Ext JS, LLC.
26456  *
26457  * Originally Released Under LGPL - original licence link has changed is not relivant.
26458  *
26459  * Fork - LGPL
26460  * <script type="text/javascript">
26461  */
26462
26463 /**
26464  * @class Roo.form.Form
26465  * @extends Roo.form.BasicForm
26466  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
26467  * @constructor
26468  * @param {Object} config Configuration options
26469  */
26470 Roo.form.Form = function(config){
26471     var xitems =  [];
26472     if (config.items) {
26473         xitems = config.items;
26474         delete config.items;
26475     }
26476    
26477     
26478     Roo.form.Form.superclass.constructor.call(this, null, config);
26479     this.url = this.url || this.action;
26480     if(!this.root){
26481         this.root = new Roo.form.Layout(Roo.applyIf({
26482             id: Roo.id()
26483         }, config));
26484     }
26485     this.active = this.root;
26486     /**
26487      * Array of all the buttons that have been added to this form via {@link addButton}
26488      * @type Array
26489      */
26490     this.buttons = [];
26491     this.allItems = [];
26492     this.addEvents({
26493         /**
26494          * @event clientvalidation
26495          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
26496          * @param {Form} this
26497          * @param {Boolean} valid true if the form has passed client-side validation
26498          */
26499         clientvalidation: true,
26500         /**
26501          * @event rendered
26502          * Fires when the form is rendered
26503          * @param {Roo.form.Form} form
26504          */
26505         rendered : true
26506     });
26507     
26508     if (this.progressUrl) {
26509             // push a hidden field onto the list of fields..
26510             this.addxtype( {
26511                     xns: Roo.form, 
26512                     xtype : 'Hidden', 
26513                     name : 'UPLOAD_IDENTIFIER' 
26514             });
26515         }
26516         
26517     
26518     Roo.each(xitems, this.addxtype, this);
26519     
26520     
26521     
26522 };
26523
26524 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
26525     /**
26526      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
26527      */
26528     /**
26529      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
26530      */
26531     /**
26532      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
26533      */
26534     buttonAlign:'center',
26535
26536     /**
26537      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
26538      */
26539     minButtonWidth:75,
26540
26541     /**
26542      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
26543      * This property cascades to child containers if not set.
26544      */
26545     labelAlign:'left',
26546
26547     /**
26548      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
26549      * fires a looping event with that state. This is required to bind buttons to the valid
26550      * state using the config value formBind:true on the button.
26551      */
26552     monitorValid : false,
26553
26554     /**
26555      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
26556      */
26557     monitorPoll : 200,
26558     
26559     /**
26560      * @cfg {String} progressUrl - Url to return progress data 
26561      */
26562     
26563     progressUrl : false,
26564   
26565     /**
26566      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
26567      * fields are added and the column is closed. If no fields are passed the column remains open
26568      * until end() is called.
26569      * @param {Object} config The config to pass to the column
26570      * @param {Field} field1 (optional)
26571      * @param {Field} field2 (optional)
26572      * @param {Field} etc (optional)
26573      * @return Column The column container object
26574      */
26575     column : function(c){
26576         var col = new Roo.form.Column(c);
26577         this.start(col);
26578         if(arguments.length > 1){ // duplicate code required because of Opera
26579             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26580             this.end();
26581         }
26582         return col;
26583     },
26584
26585     /**
26586      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
26587      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
26588      * until end() is called.
26589      * @param {Object} config The config to pass to the fieldset
26590      * @param {Field} field1 (optional)
26591      * @param {Field} field2 (optional)
26592      * @param {Field} etc (optional)
26593      * @return FieldSet The fieldset container object
26594      */
26595     fieldset : function(c){
26596         var fs = new Roo.form.FieldSet(c);
26597         this.start(fs);
26598         if(arguments.length > 1){ // duplicate code required because of Opera
26599             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26600             this.end();
26601         }
26602         return fs;
26603     },
26604
26605     /**
26606      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
26607      * fields are added and the container is closed. If no fields are passed the container remains open
26608      * until end() is called.
26609      * @param {Object} config The config to pass to the Layout
26610      * @param {Field} field1 (optional)
26611      * @param {Field} field2 (optional)
26612      * @param {Field} etc (optional)
26613      * @return Layout The container object
26614      */
26615     container : function(c){
26616         var l = new Roo.form.Layout(c);
26617         this.start(l);
26618         if(arguments.length > 1){ // duplicate code required because of Opera
26619             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26620             this.end();
26621         }
26622         return l;
26623     },
26624
26625     /**
26626      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
26627      * @param {Object} container A Roo.form.Layout or subclass of Layout
26628      * @return {Form} this
26629      */
26630     start : function(c){
26631         // cascade label info
26632         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
26633         this.active.stack.push(c);
26634         c.ownerCt = this.active;
26635         this.active = c;
26636         return this;
26637     },
26638
26639     /**
26640      * Closes the current open container
26641      * @return {Form} this
26642      */
26643     end : function(){
26644         if(this.active == this.root){
26645             return this;
26646         }
26647         this.active = this.active.ownerCt;
26648         return this;
26649     },
26650
26651     /**
26652      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
26653      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
26654      * as the label of the field.
26655      * @param {Field} field1
26656      * @param {Field} field2 (optional)
26657      * @param {Field} etc. (optional)
26658      * @return {Form} this
26659      */
26660     add : function(){
26661         this.active.stack.push.apply(this.active.stack, arguments);
26662         this.allItems.push.apply(this.allItems,arguments);
26663         var r = [];
26664         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
26665             if(a[i].isFormField){
26666                 r.push(a[i]);
26667             }
26668         }
26669         if(r.length > 0){
26670             Roo.form.Form.superclass.add.apply(this, r);
26671         }
26672         return this;
26673     },
26674     
26675
26676     
26677     
26678     
26679      /**
26680      * Find any element that has been added to a form, using it's ID or name
26681      * This can include framesets, columns etc. along with regular fields..
26682      * @param {String} id - id or name to find.
26683      
26684      * @return {Element} e - or false if nothing found.
26685      */
26686     findbyId : function(id)
26687     {
26688         var ret = false;
26689         if (!id) {
26690             return ret;
26691         }
26692         Ext.each(this.allItems, function(f){
26693             if (f.id == id || f.name == id ){
26694                 ret = f;
26695                 return false;
26696             }
26697         });
26698         return ret;
26699     },
26700
26701     
26702     
26703     /**
26704      * Render this form into the passed container. This should only be called once!
26705      * @param {String/HTMLElement/Element} container The element this component should be rendered into
26706      * @return {Form} this
26707      */
26708     render : function(ct)
26709     {
26710         
26711         
26712         
26713         ct = Roo.get(ct);
26714         var o = this.autoCreate || {
26715             tag: 'form',
26716             method : this.method || 'POST',
26717             id : this.id || Roo.id()
26718         };
26719         this.initEl(ct.createChild(o));
26720
26721         this.root.render(this.el);
26722         
26723        
26724              
26725         this.items.each(function(f){
26726             f.render('x-form-el-'+f.id);
26727         });
26728
26729         if(this.buttons.length > 0){
26730             // tables are required to maintain order and for correct IE layout
26731             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
26732                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
26733                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
26734             }}, null, true);
26735             var tr = tb.getElementsByTagName('tr')[0];
26736             for(var i = 0, len = this.buttons.length; i < len; i++) {
26737                 var b = this.buttons[i];
26738                 var td = document.createElement('td');
26739                 td.className = 'x-form-btn-td';
26740                 b.render(tr.appendChild(td));
26741             }
26742         }
26743         if(this.monitorValid){ // initialize after render
26744             this.startMonitoring();
26745         }
26746         this.fireEvent('rendered', this);
26747         return this;
26748     },
26749
26750     /**
26751      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
26752      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
26753      * object or a valid Roo.DomHelper element config
26754      * @param {Function} handler The function called when the button is clicked
26755      * @param {Object} scope (optional) The scope of the handler function
26756      * @return {Roo.Button}
26757      */
26758     addButton : function(config, handler, scope){
26759         var bc = {
26760             handler: handler,
26761             scope: scope,
26762             minWidth: this.minButtonWidth,
26763             hideParent:true
26764         };
26765         if(typeof config == "string"){
26766             bc.text = config;
26767         }else{
26768             Roo.apply(bc, config);
26769         }
26770         var btn = new Roo.Button(null, bc);
26771         this.buttons.push(btn);
26772         return btn;
26773     },
26774
26775      /**
26776      * Adds a series of form elements (using the xtype property as the factory method.
26777      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
26778      * @param {Object} config 
26779      */
26780     
26781     addxtype : function()
26782     {
26783         var ar = Array.prototype.slice.call(arguments, 0);
26784         var ret = false;
26785         for(var i = 0; i < ar.length; i++) {
26786             if (!ar[i]) {
26787                 continue; // skip -- if this happends something invalid got sent, we 
26788                 // should ignore it, as basically that interface element will not show up
26789                 // and that should be pretty obvious!!
26790             }
26791             
26792             if (Roo.form[ar[i].xtype]) {
26793                 ar[i].form = this;
26794                 var fe = Roo.factory(ar[i], Roo.form);
26795                 if (!ret) {
26796                     ret = fe;
26797                 }
26798                 fe.form = this;
26799                 if (fe.store) {
26800                     fe.store.form = this;
26801                 }
26802                 if (fe.isLayout) {  
26803                          
26804                     this.start(fe);
26805                     this.allItems.push(fe);
26806                     if (fe.items && fe.addxtype) {
26807                         fe.addxtype.apply(fe, fe.items);
26808                         delete fe.items;
26809                     }
26810                      this.end();
26811                     continue;
26812                 }
26813                 
26814                 
26815                  
26816                 this.add(fe);
26817               //  console.log('adding ' + ar[i].xtype);
26818             }
26819             if (ar[i].xtype == 'Button') {  
26820                 //console.log('adding button');
26821                 //console.log(ar[i]);
26822                 this.addButton(ar[i]);
26823                 this.allItems.push(fe);
26824                 continue;
26825             }
26826             
26827             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
26828                 alert('end is not supported on xtype any more, use items');
26829             //    this.end();
26830             //    //console.log('adding end');
26831             }
26832             
26833         }
26834         return ret;
26835     },
26836     
26837     /**
26838      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
26839      * option "monitorValid"
26840      */
26841     startMonitoring : function(){
26842         if(!this.bound){
26843             this.bound = true;
26844             Roo.TaskMgr.start({
26845                 run : this.bindHandler,
26846                 interval : this.monitorPoll || 200,
26847                 scope: this
26848             });
26849         }
26850     },
26851
26852     /**
26853      * Stops monitoring of the valid state of this form
26854      */
26855     stopMonitoring : function(){
26856         this.bound = false;
26857     },
26858
26859     // private
26860     bindHandler : function(){
26861         if(!this.bound){
26862             return false; // stops binding
26863         }
26864         var valid = true;
26865         this.items.each(function(f){
26866             if(!f.isValid(true)){
26867                 valid = false;
26868                 return false;
26869             }
26870         });
26871         for(var i = 0, len = this.buttons.length; i < len; i++){
26872             var btn = this.buttons[i];
26873             if(btn.formBind === true && btn.disabled === valid){
26874                 btn.setDisabled(!valid);
26875             }
26876         }
26877         this.fireEvent('clientvalidation', this, valid);
26878     }
26879     
26880     
26881     
26882     
26883     
26884     
26885     
26886     
26887 });
26888
26889
26890 // back compat
26891 Roo.Form = Roo.form.Form;
26892 /*
26893  * Based on:
26894  * Ext JS Library 1.1.1
26895  * Copyright(c) 2006-2007, Ext JS, LLC.
26896  *
26897  * Originally Released Under LGPL - original licence link has changed is not relivant.
26898  *
26899  * Fork - LGPL
26900  * <script type="text/javascript">
26901  */
26902  
26903  /**
26904  * @class Roo.form.Action
26905  * Internal Class used to handle form actions
26906  * @constructor
26907  * @param {Roo.form.BasicForm} el The form element or its id
26908  * @param {Object} config Configuration options
26909  */
26910  
26911  
26912 // define the action interface
26913 Roo.form.Action = function(form, options){
26914     this.form = form;
26915     this.options = options || {};
26916 };
26917 /**
26918  * Client Validation Failed
26919  * @const 
26920  */
26921 Roo.form.Action.CLIENT_INVALID = 'client';
26922 /**
26923  * Server Validation Failed
26924  * @const 
26925  */
26926  Roo.form.Action.SERVER_INVALID = 'server';
26927  /**
26928  * Connect to Server Failed
26929  * @const 
26930  */
26931 Roo.form.Action.CONNECT_FAILURE = 'connect';
26932 /**
26933  * Reading Data from Server Failed
26934  * @const 
26935  */
26936 Roo.form.Action.LOAD_FAILURE = 'load';
26937
26938 Roo.form.Action.prototype = {
26939     type : 'default',
26940     failureType : undefined,
26941     response : undefined,
26942     result : undefined,
26943
26944     // interface method
26945     run : function(options){
26946
26947     },
26948
26949     // interface method
26950     success : function(response){
26951
26952     },
26953
26954     // interface method
26955     handleResponse : function(response){
26956
26957     },
26958
26959     // default connection failure
26960     failure : function(response){
26961         this.response = response;
26962         this.failureType = Roo.form.Action.CONNECT_FAILURE;
26963         this.form.afterAction(this, false);
26964     },
26965
26966     processResponse : function(response){
26967         this.response = response;
26968         if(!response.responseText){
26969             return true;
26970         }
26971         this.result = this.handleResponse(response);
26972         return this.result;
26973     },
26974
26975     // utility functions used internally
26976     getUrl : function(appendParams){
26977         var url = this.options.url || this.form.url || this.form.el.dom.action;
26978         if(appendParams){
26979             var p = this.getParams();
26980             if(p){
26981                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
26982             }
26983         }
26984         return url;
26985     },
26986
26987     getMethod : function(){
26988         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
26989     },
26990
26991     getParams : function(){
26992         var bp = this.form.baseParams;
26993         var p = this.options.params;
26994         if(p){
26995             if(typeof p == "object"){
26996                 p = Roo.urlEncode(Roo.applyIf(p, bp));
26997             }else if(typeof p == 'string' && bp){
26998                 p += '&' + Roo.urlEncode(bp);
26999             }
27000         }else if(bp){
27001             p = Roo.urlEncode(bp);
27002         }
27003         return p;
27004     },
27005
27006     createCallback : function(){
27007         return {
27008             success: this.success,
27009             failure: this.failure,
27010             scope: this,
27011             timeout: (this.form.timeout*1000),
27012             upload: this.form.fileUpload ? this.success : undefined
27013         };
27014     }
27015 };
27016
27017 Roo.form.Action.Submit = function(form, options){
27018     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
27019 };
27020
27021 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
27022     type : 'submit',
27023
27024     haveProgress : false,
27025     uploadComplete : false,
27026     
27027     // uploadProgress indicator.
27028     uploadProgress : function()
27029     {
27030         if (!this.form.progressUrl) {
27031             return;
27032         }
27033         
27034         if (!this.haveProgress) {
27035             Roo.MessageBox.progress("Uploading", "Uploading");
27036         }
27037         if (this.uploadComplete) {
27038            Roo.MessageBox.hide();
27039            return;
27040         }
27041         
27042         this.haveProgress = true;
27043    
27044         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
27045         
27046         var c = new Roo.data.Connection();
27047         c.request({
27048             url : this.form.progressUrl,
27049             params: {
27050                 id : uid
27051             },
27052             method: 'GET',
27053             success : function(req){
27054                //console.log(data);
27055                 var rdata = false;
27056                 var edata;
27057                 try  {
27058                    rdata = Roo.decode(req.responseText)
27059                 } catch (e) {
27060                     Roo.log("Invalid data from server..");
27061                     Roo.log(edata);
27062                     return;
27063                 }
27064                 if (!rdata || !rdata.success) {
27065                     Roo.log(rdata);
27066                     return;
27067                 }
27068                 var data = rdata.data;
27069                 
27070                 if (this.uploadComplete) {
27071                    Roo.MessageBox.hide();
27072                    return;
27073                 }
27074                    
27075                 if (data){
27076                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
27077                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
27078                     );
27079                 }
27080                 this.uploadProgress.defer(2000,this);
27081             },
27082        
27083             failure: function(data) {
27084                 Roo.log('progress url failed ');
27085                 Roo.log(data);
27086             },
27087             scope : this
27088         });
27089            
27090     },
27091     
27092     
27093     run : function()
27094     {
27095         // run get Values on the form, so it syncs any secondary forms.
27096         this.form.getValues();
27097         
27098         var o = this.options;
27099         var method = this.getMethod();
27100         var isPost = method == 'POST';
27101         if(o.clientValidation === false || this.form.isValid()){
27102             
27103             if (this.form.progressUrl) {
27104                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
27105                     (new Date() * 1) + '' + Math.random());
27106                     
27107             } 
27108             
27109             Roo.Ajax.request(Roo.apply(this.createCallback(), {
27110                 form:this.form.el.dom,
27111                 url:this.getUrl(!isPost),
27112                 method: method,
27113                 params:isPost ? this.getParams() : null,
27114                 isUpload: this.form.fileUpload
27115             }));
27116             
27117             this.uploadProgress();
27118
27119         }else if (o.clientValidation !== false){ // client validation failed
27120             this.failureType = Roo.form.Action.CLIENT_INVALID;
27121             this.form.afterAction(this, false);
27122         }
27123     },
27124
27125     success : function(response)
27126     {
27127         this.uploadComplete= true;
27128         if (this.haveProgress) {
27129             Roo.MessageBox.hide();
27130         }
27131         
27132         var result = this.processResponse(response);
27133         if(result === true || result.success){
27134             this.form.afterAction(this, true);
27135             return;
27136         }
27137         if(result.errors){
27138             this.form.markInvalid(result.errors);
27139             this.failureType = Roo.form.Action.SERVER_INVALID;
27140         }
27141         this.form.afterAction(this, false);
27142     },
27143     failure : function(response)
27144     {
27145         this.uploadComplete= true;
27146         if (this.haveProgress) {
27147             Roo.MessageBox.hide();
27148         }
27149         
27150         this.response = response;
27151         this.failureType = Roo.form.Action.CONNECT_FAILURE;
27152         this.form.afterAction(this, false);
27153     },
27154     
27155     handleResponse : function(response){
27156         if(this.form.errorReader){
27157             var rs = this.form.errorReader.read(response);
27158             var errors = [];
27159             if(rs.records){
27160                 for(var i = 0, len = rs.records.length; i < len; i++) {
27161                     var r = rs.records[i];
27162                     errors[i] = r.data;
27163                 }
27164             }
27165             if(errors.length < 1){
27166                 errors = null;
27167             }
27168             return {
27169                 success : rs.success,
27170                 errors : errors
27171             };
27172         }
27173         var ret = false;
27174         try {
27175             ret = Roo.decode(response.responseText);
27176         } catch (e) {
27177             ret = {
27178                 success: false,
27179                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
27180                 errors : []
27181             };
27182         }
27183         return ret;
27184         
27185     }
27186 });
27187
27188
27189 Roo.form.Action.Load = function(form, options){
27190     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
27191     this.reader = this.form.reader;
27192 };
27193
27194 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
27195     type : 'load',
27196
27197     run : function(){
27198         Roo.Ajax.request(Roo.apply(
27199                 this.createCallback(), {
27200                     method:this.getMethod(),
27201                     url:this.getUrl(false),
27202                     params:this.getParams()
27203         }));
27204     },
27205
27206     success : function(response){
27207         var result = this.processResponse(response);
27208         if(result === true || !result.success || !result.data){
27209             this.failureType = Roo.form.Action.LOAD_FAILURE;
27210             this.form.afterAction(this, false);
27211             return;
27212         }
27213         this.form.clearInvalid();
27214         this.form.setValues(result.data);
27215         this.form.afterAction(this, true);
27216     },
27217
27218     handleResponse : function(response){
27219         if(this.form.reader){
27220             var rs = this.form.reader.read(response);
27221             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
27222             return {
27223                 success : rs.success,
27224                 data : data
27225             };
27226         }
27227         return Roo.decode(response.responseText);
27228     }
27229 });
27230
27231 Roo.form.Action.ACTION_TYPES = {
27232     'load' : Roo.form.Action.Load,
27233     'submit' : Roo.form.Action.Submit
27234 };/*
27235  * Based on:
27236  * Ext JS Library 1.1.1
27237  * Copyright(c) 2006-2007, Ext JS, LLC.
27238  *
27239  * Originally Released Under LGPL - original licence link has changed is not relivant.
27240  *
27241  * Fork - LGPL
27242  * <script type="text/javascript">
27243  */
27244  
27245 /**
27246  * @class Roo.form.Layout
27247  * @extends Roo.Component
27248  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
27249  * @constructor
27250  * @param {Object} config Configuration options
27251  */
27252 Roo.form.Layout = function(config){
27253     var xitems = [];
27254     if (config.items) {
27255         xitems = config.items;
27256         delete config.items;
27257     }
27258     Roo.form.Layout.superclass.constructor.call(this, config);
27259     this.stack = [];
27260     Roo.each(xitems, this.addxtype, this);
27261      
27262 };
27263
27264 Roo.extend(Roo.form.Layout, Roo.Component, {
27265     /**
27266      * @cfg {String/Object} autoCreate
27267      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
27268      */
27269     /**
27270      * @cfg {String/Object/Function} style
27271      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
27272      * a function which returns such a specification.
27273      */
27274     /**
27275      * @cfg {String} labelAlign
27276      * Valid values are "left," "top" and "right" (defaults to "left")
27277      */
27278     /**
27279      * @cfg {Number} labelWidth
27280      * Fixed width in pixels of all field labels (defaults to undefined)
27281      */
27282     /**
27283      * @cfg {Boolean} clear
27284      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
27285      */
27286     clear : true,
27287     /**
27288      * @cfg {String} labelSeparator
27289      * The separator to use after field labels (defaults to ':')
27290      */
27291     labelSeparator : ':',
27292     /**
27293      * @cfg {Boolean} hideLabels
27294      * True to suppress the display of field labels in this layout (defaults to false)
27295      */
27296     hideLabels : false,
27297
27298     // private
27299     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
27300     
27301     isLayout : true,
27302     
27303     // private
27304     onRender : function(ct, position){
27305         if(this.el){ // from markup
27306             this.el = Roo.get(this.el);
27307         }else {  // generate
27308             var cfg = this.getAutoCreate();
27309             this.el = ct.createChild(cfg, position);
27310         }
27311         if(this.style){
27312             this.el.applyStyles(this.style);
27313         }
27314         if(this.labelAlign){
27315             this.el.addClass('x-form-label-'+this.labelAlign);
27316         }
27317         if(this.hideLabels){
27318             this.labelStyle = "display:none";
27319             this.elementStyle = "padding-left:0;";
27320         }else{
27321             if(typeof this.labelWidth == 'number'){
27322                 this.labelStyle = "width:"+this.labelWidth+"px;";
27323                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
27324             }
27325             if(this.labelAlign == 'top'){
27326                 this.labelStyle = "width:auto;";
27327                 this.elementStyle = "padding-left:0;";
27328             }
27329         }
27330         var stack = this.stack;
27331         var slen = stack.length;
27332         if(slen > 0){
27333             if(!this.fieldTpl){
27334                 var t = new Roo.Template(
27335                     '<div class="x-form-item {5}">',
27336                         '<label for="{0}" style="{2}">{1}{4}</label>',
27337                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
27338                         '</div>',
27339                     '</div><div class="x-form-clear-left"></div>'
27340                 );
27341                 t.disableFormats = true;
27342                 t.compile();
27343                 Roo.form.Layout.prototype.fieldTpl = t;
27344             }
27345             for(var i = 0; i < slen; i++) {
27346                 if(stack[i].isFormField){
27347                     this.renderField(stack[i]);
27348                 }else{
27349                     this.renderComponent(stack[i]);
27350                 }
27351             }
27352         }
27353         if(this.clear){
27354             this.el.createChild({cls:'x-form-clear'});
27355         }
27356     },
27357
27358     // private
27359     renderField : function(f){
27360         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
27361                f.id, //0
27362                f.fieldLabel, //1
27363                f.labelStyle||this.labelStyle||'', //2
27364                this.elementStyle||'', //3
27365                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
27366                f.itemCls||this.itemCls||''  //5
27367        ], true).getPrevSibling());
27368     },
27369
27370     // private
27371     renderComponent : function(c){
27372         c.render(c.isLayout ? this.el : this.el.createChild());    
27373     },
27374     /**
27375      * Adds a object form elements (using the xtype property as the factory method.)
27376      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
27377      * @param {Object} config 
27378      */
27379     addxtype : function(o)
27380     {
27381         // create the lement.
27382         o.form = this.form;
27383         var fe = Roo.factory(o, Roo.form);
27384         this.form.allItems.push(fe);
27385         this.stack.push(fe);
27386         
27387         if (fe.isFormField) {
27388             this.form.items.add(fe);
27389         }
27390          
27391         return fe;
27392     }
27393 });
27394
27395 /**
27396  * @class Roo.form.Column
27397  * @extends Roo.form.Layout
27398  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
27399  * @constructor
27400  * @param {Object} config Configuration options
27401  */
27402 Roo.form.Column = function(config){
27403     Roo.form.Column.superclass.constructor.call(this, config);
27404 };
27405
27406 Roo.extend(Roo.form.Column, Roo.form.Layout, {
27407     /**
27408      * @cfg {Number/String} width
27409      * The fixed width of the column in pixels or CSS value (defaults to "auto")
27410      */
27411     /**
27412      * @cfg {String/Object} autoCreate
27413      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
27414      */
27415
27416     // private
27417     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
27418
27419     // private
27420     onRender : function(ct, position){
27421         Roo.form.Column.superclass.onRender.call(this, ct, position);
27422         if(this.width){
27423             this.el.setWidth(this.width);
27424         }
27425     }
27426 });
27427
27428
27429 /**
27430  * @class Roo.form.Row
27431  * @extends Roo.form.Layout
27432  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
27433  * @constructor
27434  * @param {Object} config Configuration options
27435  */
27436
27437  
27438 Roo.form.Row = function(config){
27439     Roo.form.Row.superclass.constructor.call(this, config);
27440 };
27441  
27442 Roo.extend(Roo.form.Row, Roo.form.Layout, {
27443       /**
27444      * @cfg {Number/String} width
27445      * The fixed width of the column in pixels or CSS value (defaults to "auto")
27446      */
27447     /**
27448      * @cfg {Number/String} height
27449      * The fixed height of the column in pixels or CSS value (defaults to "auto")
27450      */
27451     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
27452     
27453     padWidth : 20,
27454     // private
27455     onRender : function(ct, position){
27456         //console.log('row render');
27457         if(!this.rowTpl){
27458             var t = new Roo.Template(
27459                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
27460                     '<label for="{0}" style="{2}">{1}{4}</label>',
27461                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
27462                     '</div>',
27463                 '</div>'
27464             );
27465             t.disableFormats = true;
27466             t.compile();
27467             Roo.form.Layout.prototype.rowTpl = t;
27468         }
27469         this.fieldTpl = this.rowTpl;
27470         
27471         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
27472         var labelWidth = 100;
27473         
27474         if ((this.labelAlign != 'top')) {
27475             if (typeof this.labelWidth == 'number') {
27476                 labelWidth = this.labelWidth
27477             }
27478             this.padWidth =  20 + labelWidth;
27479             
27480         }
27481         
27482         Roo.form.Column.superclass.onRender.call(this, ct, position);
27483         if(this.width){
27484             this.el.setWidth(this.width);
27485         }
27486         if(this.height){
27487             this.el.setHeight(this.height);
27488         }
27489     },
27490     
27491     // private
27492     renderField : function(f){
27493         f.fieldEl = this.fieldTpl.append(this.el, [
27494                f.id, f.fieldLabel,
27495                f.labelStyle||this.labelStyle||'',
27496                this.elementStyle||'',
27497                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
27498                f.itemCls||this.itemCls||'',
27499                f.width ? f.width + this.padWidth : 160 + this.padWidth
27500        ],true);
27501     }
27502 });
27503  
27504
27505 /**
27506  * @class Roo.form.FieldSet
27507  * @extends Roo.form.Layout
27508  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
27509  * @constructor
27510  * @param {Object} config Configuration options
27511  */
27512 Roo.form.FieldSet = function(config){
27513     Roo.form.FieldSet.superclass.constructor.call(this, config);
27514 };
27515
27516 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
27517     /**
27518      * @cfg {String} legend
27519      * The text to display as the legend for the FieldSet (defaults to '')
27520      */
27521     /**
27522      * @cfg {String/Object} autoCreate
27523      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
27524      */
27525
27526     // private
27527     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
27528
27529     // private
27530     onRender : function(ct, position){
27531         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
27532         if(this.legend){
27533             this.setLegend(this.legend);
27534         }
27535     },
27536
27537     // private
27538     setLegend : function(text){
27539         if(this.rendered){
27540             this.el.child('legend').update(text);
27541         }
27542     }
27543 });/*
27544  * Based on:
27545  * Ext JS Library 1.1.1
27546  * Copyright(c) 2006-2007, Ext JS, LLC.
27547  *
27548  * Originally Released Under LGPL - original licence link has changed is not relivant.
27549  *
27550  * Fork - LGPL
27551  * <script type="text/javascript">
27552  */
27553 /**
27554  * @class Roo.form.VTypes
27555  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
27556  * @singleton
27557  */
27558 Roo.form.VTypes = function(){
27559     // closure these in so they are only created once.
27560     var alpha = /^[a-zA-Z_]+$/;
27561     var alphanum = /^[a-zA-Z0-9_]+$/;
27562     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
27563     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
27564
27565     // All these messages and functions are configurable
27566     return {
27567         /**
27568          * The function used to validate email addresses
27569          * @param {String} value The email address
27570          */
27571         'email' : function(v){
27572             return email.test(v);
27573         },
27574         /**
27575          * The error text to display when the email validation function returns false
27576          * @type String
27577          */
27578         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
27579         /**
27580          * The keystroke filter mask to be applied on email input
27581          * @type RegExp
27582          */
27583         'emailMask' : /[a-z0-9_\.\-@]/i,
27584
27585         /**
27586          * The function used to validate URLs
27587          * @param {String} value The URL
27588          */
27589         'url' : function(v){
27590             return url.test(v);
27591         },
27592         /**
27593          * The error text to display when the url validation function returns false
27594          * @type String
27595          */
27596         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
27597         
27598         /**
27599          * The function used to validate alpha values
27600          * @param {String} value The value
27601          */
27602         'alpha' : function(v){
27603             return alpha.test(v);
27604         },
27605         /**
27606          * The error text to display when the alpha validation function returns false
27607          * @type String
27608          */
27609         'alphaText' : 'This field should only contain letters and _',
27610         /**
27611          * The keystroke filter mask to be applied on alpha input
27612          * @type RegExp
27613          */
27614         'alphaMask' : /[a-z_]/i,
27615
27616         /**
27617          * The function used to validate alphanumeric values
27618          * @param {String} value The value
27619          */
27620         'alphanum' : function(v){
27621             return alphanum.test(v);
27622         },
27623         /**
27624          * The error text to display when the alphanumeric validation function returns false
27625          * @type String
27626          */
27627         'alphanumText' : 'This field should only contain letters, numbers and _',
27628         /**
27629          * The keystroke filter mask to be applied on alphanumeric input
27630          * @type RegExp
27631          */
27632         'alphanumMask' : /[a-z0-9_]/i
27633     };
27634 }();//<script type="text/javascript">
27635
27636 /**
27637  * @class Roo.form.FCKeditor
27638  * @extends Roo.form.TextArea
27639  * Wrapper around the FCKEditor http://www.fckeditor.net
27640  * @constructor
27641  * Creates a new FCKeditor
27642  * @param {Object} config Configuration options
27643  */
27644 Roo.form.FCKeditor = function(config){
27645     Roo.form.FCKeditor.superclass.constructor.call(this, config);
27646     this.addEvents({
27647          /**
27648          * @event editorinit
27649          * Fired when the editor is initialized - you can add extra handlers here..
27650          * @param {FCKeditor} this
27651          * @param {Object} the FCK object.
27652          */
27653         editorinit : true
27654     });
27655     
27656     
27657 };
27658 Roo.form.FCKeditor.editors = { };
27659 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
27660 {
27661     //defaultAutoCreate : {
27662     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
27663     //},
27664     // private
27665     /**
27666      * @cfg {Object} fck options - see fck manual for details.
27667      */
27668     fckconfig : false,
27669     
27670     /**
27671      * @cfg {Object} fck toolbar set (Basic or Default)
27672      */
27673     toolbarSet : 'Basic',
27674     /**
27675      * @cfg {Object} fck BasePath
27676      */ 
27677     basePath : '/fckeditor/',
27678     
27679     
27680     frame : false,
27681     
27682     value : '',
27683     
27684    
27685     onRender : function(ct, position)
27686     {
27687         if(!this.el){
27688             this.defaultAutoCreate = {
27689                 tag: "textarea",
27690                 style:"width:300px;height:60px;",
27691                 autocomplete: "off"
27692             };
27693         }
27694         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
27695         /*
27696         if(this.grow){
27697             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
27698             if(this.preventScrollbars){
27699                 this.el.setStyle("overflow", "hidden");
27700             }
27701             this.el.setHeight(this.growMin);
27702         }
27703         */
27704         //console.log('onrender' + this.getId() );
27705         Roo.form.FCKeditor.editors[this.getId()] = this;
27706          
27707
27708         this.replaceTextarea() ;
27709         
27710     },
27711     
27712     getEditor : function() {
27713         return this.fckEditor;
27714     },
27715     /**
27716      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
27717      * @param {Mixed} value The value to set
27718      */
27719     
27720     
27721     setValue : function(value)
27722     {
27723         //console.log('setValue: ' + value);
27724         
27725         if(typeof(value) == 'undefined') { // not sure why this is happending...
27726             return;
27727         }
27728         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
27729         
27730         //if(!this.el || !this.getEditor()) {
27731         //    this.value = value;
27732             //this.setValue.defer(100,this,[value]);    
27733         //    return;
27734         //} 
27735         
27736         if(!this.getEditor()) {
27737             return;
27738         }
27739         
27740         this.getEditor().SetData(value);
27741         
27742         //
27743
27744     },
27745
27746     /**
27747      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
27748      * @return {Mixed} value The field value
27749      */
27750     getValue : function()
27751     {
27752         
27753         if (this.frame && this.frame.dom.style.display == 'none') {
27754             return Roo.form.FCKeditor.superclass.getValue.call(this);
27755         }
27756         
27757         if(!this.el || !this.getEditor()) {
27758            
27759            // this.getValue.defer(100,this); 
27760             return this.value;
27761         }
27762        
27763         
27764         var value=this.getEditor().GetData();
27765         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
27766         return Roo.form.FCKeditor.superclass.getValue.call(this);
27767         
27768
27769     },
27770
27771     /**
27772      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
27773      * @return {Mixed} value The field value
27774      */
27775     getRawValue : function()
27776     {
27777         if (this.frame && this.frame.dom.style.display == 'none') {
27778             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
27779         }
27780         
27781         if(!this.el || !this.getEditor()) {
27782             //this.getRawValue.defer(100,this); 
27783             return this.value;
27784             return;
27785         }
27786         
27787         
27788         
27789         var value=this.getEditor().GetData();
27790         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
27791         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
27792          
27793     },
27794     
27795     setSize : function(w,h) {
27796         
27797         
27798         
27799         //if (this.frame && this.frame.dom.style.display == 'none') {
27800         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27801         //    return;
27802         //}
27803         //if(!this.el || !this.getEditor()) {
27804         //    this.setSize.defer(100,this, [w,h]); 
27805         //    return;
27806         //}
27807         
27808         
27809         
27810         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27811         
27812         this.frame.dom.setAttribute('width', w);
27813         this.frame.dom.setAttribute('height', h);
27814         this.frame.setSize(w,h);
27815         
27816     },
27817     
27818     toggleSourceEdit : function(value) {
27819         
27820       
27821          
27822         this.el.dom.style.display = value ? '' : 'none';
27823         this.frame.dom.style.display = value ?  'none' : '';
27824         
27825     },
27826     
27827     
27828     focus: function(tag)
27829     {
27830         if (this.frame.dom.style.display == 'none') {
27831             return Roo.form.FCKeditor.superclass.focus.call(this);
27832         }
27833         if(!this.el || !this.getEditor()) {
27834             this.focus.defer(100,this, [tag]); 
27835             return;
27836         }
27837         
27838         
27839         
27840         
27841         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
27842         this.getEditor().Focus();
27843         if (tgs.length) {
27844             if (!this.getEditor().Selection.GetSelection()) {
27845                 this.focus.defer(100,this, [tag]); 
27846                 return;
27847             }
27848             
27849             
27850             var r = this.getEditor().EditorDocument.createRange();
27851             r.setStart(tgs[0],0);
27852             r.setEnd(tgs[0],0);
27853             this.getEditor().Selection.GetSelection().removeAllRanges();
27854             this.getEditor().Selection.GetSelection().addRange(r);
27855             this.getEditor().Focus();
27856         }
27857         
27858     },
27859     
27860     
27861     
27862     replaceTextarea : function()
27863     {
27864         if ( document.getElementById( this.getId() + '___Frame' ) )
27865             return ;
27866         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
27867         //{
27868             // We must check the elements firstly using the Id and then the name.
27869         var oTextarea = document.getElementById( this.getId() );
27870         
27871         var colElementsByName = document.getElementsByName( this.getId() ) ;
27872          
27873         oTextarea.style.display = 'none' ;
27874
27875         if ( oTextarea.tabIndex ) {            
27876             this.TabIndex = oTextarea.tabIndex ;
27877         }
27878         
27879         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
27880         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
27881         this.frame = Roo.get(this.getId() + '___Frame')
27882     },
27883     
27884     _getConfigHtml : function()
27885     {
27886         var sConfig = '' ;
27887
27888         for ( var o in this.fckconfig ) {
27889             sConfig += sConfig.length > 0  ? '&amp;' : '';
27890             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
27891         }
27892
27893         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
27894     },
27895     
27896     
27897     _getIFrameHtml : function()
27898     {
27899         var sFile = 'fckeditor.html' ;
27900         /* no idea what this is about..
27901         try
27902         {
27903             if ( (/fcksource=true/i).test( window.top.location.search ) )
27904                 sFile = 'fckeditor.original.html' ;
27905         }
27906         catch (e) { 
27907         */
27908
27909         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
27910         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
27911         
27912         
27913         var html = '<iframe id="' + this.getId() +
27914             '___Frame" src="' + sLink +
27915             '" width="' + this.width +
27916             '" height="' + this.height + '"' +
27917             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
27918             ' frameborder="0" scrolling="no"></iframe>' ;
27919
27920         return html ;
27921     },
27922     
27923     _insertHtmlBefore : function( html, element )
27924     {
27925         if ( element.insertAdjacentHTML )       {
27926             // IE
27927             element.insertAdjacentHTML( 'beforeBegin', html ) ;
27928         } else { // Gecko
27929             var oRange = document.createRange() ;
27930             oRange.setStartBefore( element ) ;
27931             var oFragment = oRange.createContextualFragment( html );
27932             element.parentNode.insertBefore( oFragment, element ) ;
27933         }
27934     }
27935     
27936     
27937   
27938     
27939     
27940     
27941     
27942
27943 });
27944
27945 //Roo.reg('fckeditor', Roo.form.FCKeditor);
27946
27947 function FCKeditor_OnComplete(editorInstance){
27948     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
27949     f.fckEditor = editorInstance;
27950     //console.log("loaded");
27951     f.fireEvent('editorinit', f, editorInstance);
27952
27953   
27954
27955  
27956
27957
27958
27959
27960
27961
27962
27963
27964
27965
27966
27967
27968
27969
27970
27971 //<script type="text/javascript">
27972 /**
27973  * @class Roo.form.GridField
27974  * @extends Roo.form.Field
27975  * Embed a grid (or editable grid into a form)
27976  * STATUS ALPHA
27977  * 
27978  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
27979  * it needs 
27980  * xgrid.store = Roo.data.Store
27981  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
27982  * xgrid.store.reader = Roo.data.JsonReader 
27983  * 
27984  * 
27985  * @constructor
27986  * Creates a new GridField
27987  * @param {Object} config Configuration options
27988  */
27989 Roo.form.GridField = function(config){
27990     Roo.form.GridField.superclass.constructor.call(this, config);
27991      
27992 };
27993
27994 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
27995     /**
27996      * @cfg {Number} width  - used to restrict width of grid..
27997      */
27998     width : 100,
27999     /**
28000      * @cfg {Number} height - used to restrict height of grid..
28001      */
28002     height : 50,
28003      /**
28004      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
28005          * 
28006          *}
28007      */
28008     xgrid : false, 
28009     /**
28010      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28011      * {tag: "input", type: "checkbox", autocomplete: "off"})
28012      */
28013    // defaultAutoCreate : { tag: 'div' },
28014     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28015     /**
28016      * @cfg {String} addTitle Text to include for adding a title.
28017      */
28018     addTitle : false,
28019     //
28020     onResize : function(){
28021         Roo.form.Field.superclass.onResize.apply(this, arguments);
28022     },
28023
28024     initEvents : function(){
28025         // Roo.form.Checkbox.superclass.initEvents.call(this);
28026         // has no events...
28027        
28028     },
28029
28030
28031     getResizeEl : function(){
28032         return this.wrap;
28033     },
28034
28035     getPositionEl : function(){
28036         return this.wrap;
28037     },
28038
28039     // private
28040     onRender : function(ct, position){
28041         
28042         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
28043         var style = this.style;
28044         delete this.style;
28045         
28046         Roo.form.GridField.superclass.onRender.call(this, ct, position);
28047         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
28048         this.viewEl = this.wrap.createChild({ tag: 'div' });
28049         if (style) {
28050             this.viewEl.applyStyles(style);
28051         }
28052         if (this.width) {
28053             this.viewEl.setWidth(this.width);
28054         }
28055         if (this.height) {
28056             this.viewEl.setHeight(this.height);
28057         }
28058         //if(this.inputValue !== undefined){
28059         //this.setValue(this.value);
28060         
28061         
28062         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
28063         
28064         
28065         this.grid.render();
28066         this.grid.getDataSource().on('remove', this.refreshValue, this);
28067         this.grid.getDataSource().on('update', this.refreshValue, this);
28068         this.grid.on('afteredit', this.refreshValue, this);
28069  
28070     },
28071      
28072     
28073     /**
28074      * Sets the value of the item. 
28075      * @param {String} either an object  or a string..
28076      */
28077     setValue : function(v){
28078         //this.value = v;
28079         v = v || []; // empty set..
28080         // this does not seem smart - it really only affects memoryproxy grids..
28081         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
28082             var ds = this.grid.getDataSource();
28083             // assumes a json reader..
28084             var data = {}
28085             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
28086             ds.loadData( data);
28087         }
28088         Roo.form.GridField.superclass.setValue.call(this, v);
28089         this.refreshValue();
28090         // should load data in the grid really....
28091     },
28092     
28093     // private
28094     refreshValue: function() {
28095          var val = [];
28096         this.grid.getDataSource().each(function(r) {
28097             val.push(r.data);
28098         });
28099         this.el.dom.value = Roo.encode(val);
28100     }
28101     
28102      
28103     
28104     
28105 });/*
28106  * Based on:
28107  * Ext JS Library 1.1.1
28108  * Copyright(c) 2006-2007, Ext JS, LLC.
28109  *
28110  * Originally Released Under LGPL - original licence link has changed is not relivant.
28111  *
28112  * Fork - LGPL
28113  * <script type="text/javascript">
28114  */
28115 /**
28116  * @class Roo.form.DisplayField
28117  * @extends Roo.form.Field
28118  * A generic Field to display non-editable data.
28119  * @constructor
28120  * Creates a new Display Field item.
28121  * @param {Object} config Configuration options
28122  */
28123 Roo.form.DisplayField = function(config){
28124     Roo.form.DisplayField.superclass.constructor.call(this, config);
28125     
28126 };
28127
28128 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
28129     inputType:      'hidden',
28130     allowBlank:     true,
28131     readOnly:         true,
28132     
28133  
28134     /**
28135      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
28136      */
28137     focusClass : undefined,
28138     /**
28139      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
28140      */
28141     fieldClass: 'x-form-field',
28142     
28143      /**
28144      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
28145      */
28146     valueRenderer: undefined,
28147     
28148     width: 100,
28149     /**
28150      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28151      * {tag: "input", type: "checkbox", autocomplete: "off"})
28152      */
28153      
28154  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28155
28156     onResize : function(){
28157         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
28158         
28159     },
28160
28161     initEvents : function(){
28162         // Roo.form.Checkbox.superclass.initEvents.call(this);
28163         // has no events...
28164        
28165     },
28166
28167
28168     getResizeEl : function(){
28169         return this.wrap;
28170     },
28171
28172     getPositionEl : function(){
28173         return this.wrap;
28174     },
28175
28176     // private
28177     onRender : function(ct, position){
28178         
28179         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
28180         //if(this.inputValue !== undefined){
28181         this.wrap = this.el.wrap();
28182         
28183         this.viewEl = this.wrap.createChild({ tag: 'div'});
28184         
28185         if (this.bodyStyle) {
28186             this.viewEl.applyStyles(this.bodyStyle);
28187         }
28188         //this.viewEl.setStyle('padding', '2px');
28189         
28190         this.setValue(this.value);
28191         
28192     },
28193 /*
28194     // private
28195     initValue : Roo.emptyFn,
28196
28197   */
28198
28199         // private
28200     onClick : function(){
28201         
28202     },
28203
28204     /**
28205      * Sets the checked state of the checkbox.
28206      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
28207      */
28208     setValue : function(v){
28209         this.value = v;
28210         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
28211         // this might be called before we have a dom element..
28212         if (!this.viewEl) {
28213             return;
28214         }
28215         this.viewEl.dom.innerHTML = html;
28216         Roo.form.DisplayField.superclass.setValue.call(this, v);
28217
28218     }
28219 });//<script type="text/javasscript">
28220  
28221
28222 /**
28223  * @class Roo.DDView
28224  * A DnD enabled version of Roo.View.
28225  * @param {Element/String} container The Element in which to create the View.
28226  * @param {String} tpl The template string used to create the markup for each element of the View
28227  * @param {Object} config The configuration properties. These include all the config options of
28228  * {@link Roo.View} plus some specific to this class.<br>
28229  * <p>
28230  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28231  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28232  * <p>
28233  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28234 .x-view-drag-insert-above {
28235         border-top:1px dotted #3366cc;
28236 }
28237 .x-view-drag-insert-below {
28238         border-bottom:1px dotted #3366cc;
28239 }
28240 </code></pre>
28241  * 
28242  */
28243  
28244 Roo.DDView = function(container, tpl, config) {
28245     Roo.DDView.superclass.constructor.apply(this, arguments);
28246     this.getEl().setStyle("outline", "0px none");
28247     this.getEl().unselectable();
28248     if (this.dragGroup) {
28249                 this.setDraggable(this.dragGroup.split(","));
28250     }
28251     if (this.dropGroup) {
28252                 this.setDroppable(this.dropGroup.split(","));
28253     }
28254     if (this.deletable) {
28255         this.setDeletable();
28256     }
28257     this.isDirtyFlag = false;
28258         this.addEvents({
28259                 "drop" : true
28260         });
28261 };
28262
28263 Roo.extend(Roo.DDView, Roo.View, {
28264 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28265 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28266 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28267 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28268
28269         isFormField: true,
28270
28271         reset: Roo.emptyFn,
28272         
28273         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28274
28275         validate: function() {
28276                 return true;
28277         },
28278         
28279         destroy: function() {
28280                 this.purgeListeners();
28281                 this.getEl.removeAllListeners();
28282                 this.getEl().remove();
28283                 if (this.dragZone) {
28284                         if (this.dragZone.destroy) {
28285                                 this.dragZone.destroy();
28286                         }
28287                 }
28288                 if (this.dropZone) {
28289                         if (this.dropZone.destroy) {
28290                                 this.dropZone.destroy();
28291                         }
28292                 }
28293         },
28294
28295 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28296         getName: function() {
28297                 return this.name;
28298         },
28299
28300 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28301         setValue: function(v) {
28302                 if (!this.store) {
28303                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28304                 }
28305                 var data = {};
28306                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28307                 this.store.proxy = new Roo.data.MemoryProxy(data);
28308                 this.store.load();
28309         },
28310
28311 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28312         getValue: function() {
28313                 var result = '(';
28314                 this.store.each(function(rec) {
28315                         result += rec.id + ',';
28316                 });
28317                 return result.substr(0, result.length - 1) + ')';
28318         },
28319         
28320         getIds: function() {
28321                 var i = 0, result = new Array(this.store.getCount());
28322                 this.store.each(function(rec) {
28323                         result[i++] = rec.id;
28324                 });
28325                 return result;
28326         },
28327         
28328         isDirty: function() {
28329                 return this.isDirtyFlag;
28330         },
28331
28332 /**
28333  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28334  *      whole Element becomes the target, and this causes the drop gesture to append.
28335  */
28336     getTargetFromEvent : function(e) {
28337                 var target = e.getTarget();
28338                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28339                 target = target.parentNode;
28340                 }
28341                 if (!target) {
28342                         target = this.el.dom.lastChild || this.el.dom;
28343                 }
28344                 return target;
28345     },
28346
28347 /**
28348  *      Create the drag data which consists of an object which has the property "ddel" as
28349  *      the drag proxy element. 
28350  */
28351     getDragData : function(e) {
28352         var target = this.findItemFromChild(e.getTarget());
28353                 if(target) {
28354                         this.handleSelection(e);
28355                         var selNodes = this.getSelectedNodes();
28356             var dragData = {
28357                 source: this,
28358                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28359                 nodes: selNodes,
28360                 records: []
28361                         };
28362                         var selectedIndices = this.getSelectedIndexes();
28363                         for (var i = 0; i < selectedIndices.length; i++) {
28364                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28365                         }
28366                         if (selNodes.length == 1) {
28367                                 dragData.ddel = target.cloneNode(true); // the div element
28368                         } else {
28369                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28370                                 div.className = 'multi-proxy';
28371                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28372                                         div.appendChild(selNodes[i].cloneNode(true));
28373                                 }
28374                                 dragData.ddel = div;
28375                         }
28376             //console.log(dragData)
28377             //console.log(dragData.ddel.innerHTML)
28378                         return dragData;
28379                 }
28380         //console.log('nodragData')
28381                 return false;
28382     },
28383     
28384 /**     Specify to which ddGroup items in this DDView may be dragged. */
28385     setDraggable: function(ddGroup) {
28386         if (ddGroup instanceof Array) {
28387                 Roo.each(ddGroup, this.setDraggable, this);
28388                 return;
28389         }
28390         if (this.dragZone) {
28391                 this.dragZone.addToGroup(ddGroup);
28392         } else {
28393                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28394                                 containerScroll: true,
28395                                 ddGroup: ddGroup 
28396
28397                         });
28398 //                      Draggability implies selection. DragZone's mousedown selects the element.
28399                         if (!this.multiSelect) { this.singleSelect = true; }
28400
28401 //                      Wire the DragZone's handlers up to methods in *this*
28402                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28403                 }
28404     },
28405
28406 /**     Specify from which ddGroup this DDView accepts drops. */
28407     setDroppable: function(ddGroup) {
28408         if (ddGroup instanceof Array) {
28409                 Roo.each(ddGroup, this.setDroppable, this);
28410                 return;
28411         }
28412         if (this.dropZone) {
28413                 this.dropZone.addToGroup(ddGroup);
28414         } else {
28415                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28416                                 containerScroll: true,
28417                                 ddGroup: ddGroup
28418                         });
28419
28420 //                      Wire the DropZone's handlers up to methods in *this*
28421                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28422                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28423                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28424                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28425                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28426                 }
28427     },
28428
28429 /**     Decide whether to drop above or below a View node. */
28430     getDropPoint : function(e, n, dd){
28431         if (n == this.el.dom) { return "above"; }
28432                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28433                 var c = t + (b - t) / 2;
28434                 var y = Roo.lib.Event.getPageY(e);
28435                 if(y <= c) {
28436                         return "above";
28437                 }else{
28438                         return "below";
28439                 }
28440     },
28441
28442     onNodeEnter : function(n, dd, e, data){
28443                 return false;
28444     },
28445     
28446     onNodeOver : function(n, dd, e, data){
28447                 var pt = this.getDropPoint(e, n, dd);
28448                 // set the insert point style on the target node
28449                 var dragElClass = this.dropNotAllowed;
28450                 if (pt) {
28451                         var targetElClass;
28452                         if (pt == "above"){
28453                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28454                                 targetElClass = "x-view-drag-insert-above";
28455                         } else {
28456                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28457                                 targetElClass = "x-view-drag-insert-below";
28458                         }
28459                         if (this.lastInsertClass != targetElClass){
28460                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28461                                 this.lastInsertClass = targetElClass;
28462                         }
28463                 }
28464                 return dragElClass;
28465         },
28466
28467     onNodeOut : function(n, dd, e, data){
28468                 this.removeDropIndicators(n);
28469     },
28470
28471     onNodeDrop : function(n, dd, e, data){
28472         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28473                 return false;
28474         }
28475         var pt = this.getDropPoint(e, n, dd);
28476                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28477                 if (pt == "below") { insertAt++; }
28478                 for (var i = 0; i < data.records.length; i++) {
28479                         var r = data.records[i];
28480                         var dup = this.store.getById(r.id);
28481                         if (dup && (dd != this.dragZone)) {
28482                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28483                         } else {
28484                                 if (data.copy) {
28485                                         this.store.insert(insertAt++, r.copy());
28486                                 } else {
28487                                         data.source.isDirtyFlag = true;
28488                                         r.store.remove(r);
28489                                         this.store.insert(insertAt++, r);
28490                                 }
28491                                 this.isDirtyFlag = true;
28492                         }
28493                 }
28494                 this.dragZone.cachedTarget = null;
28495                 return true;
28496     },
28497
28498     removeDropIndicators : function(n){
28499                 if(n){
28500                         Roo.fly(n).removeClass([
28501                                 "x-view-drag-insert-above",
28502                                 "x-view-drag-insert-below"]);
28503                         this.lastInsertClass = "_noclass";
28504                 }
28505     },
28506
28507 /**
28508  *      Utility method. Add a delete option to the DDView's context menu.
28509  *      @param {String} imageUrl The URL of the "delete" icon image.
28510  */
28511         setDeletable: function(imageUrl) {
28512                 if (!this.singleSelect && !this.multiSelect) {
28513                         this.singleSelect = true;
28514                 }
28515                 var c = this.getContextMenu();
28516                 this.contextMenu.on("itemclick", function(item) {
28517                         switch (item.id) {
28518                                 case "delete":
28519                                         this.remove(this.getSelectedIndexes());
28520                                         break;
28521                         }
28522                 }, this);
28523                 this.contextMenu.add({
28524                         icon: imageUrl,
28525                         id: "delete",
28526                         text: 'Delete'
28527                 });
28528         },
28529         
28530 /**     Return the context menu for this DDView. */
28531         getContextMenu: function() {
28532                 if (!this.contextMenu) {
28533 //                      Create the View's context menu
28534                         this.contextMenu = new Roo.menu.Menu({
28535                                 id: this.id + "-contextmenu"
28536                         });
28537                         this.el.on("contextmenu", this.showContextMenu, this);
28538                 }
28539                 return this.contextMenu;
28540         },
28541         
28542         disableContextMenu: function() {
28543                 if (this.contextMenu) {
28544                         this.el.un("contextmenu", this.showContextMenu, this);
28545                 }
28546         },
28547
28548         showContextMenu: function(e, item) {
28549         item = this.findItemFromChild(e.getTarget());
28550                 if (item) {
28551                         e.stopEvent();
28552                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28553                         this.contextMenu.showAt(e.getXY());
28554             }
28555     },
28556
28557 /**
28558  *      Remove {@link Roo.data.Record}s at the specified indices.
28559  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28560  */
28561     remove: function(selectedIndices) {
28562                 selectedIndices = [].concat(selectedIndices);
28563                 for (var i = 0; i < selectedIndices.length; i++) {
28564                         var rec = this.store.getAt(selectedIndices[i]);
28565                         this.store.remove(rec);
28566                 }
28567     },
28568
28569 /**
28570  *      Double click fires the event, but also, if this is draggable, and there is only one other
28571  *      related DropZone, it transfers the selected node.
28572  */
28573     onDblClick : function(e){
28574         var item = this.findItemFromChild(e.getTarget());
28575         if(item){
28576             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28577                 return false;
28578             }
28579             if (this.dragGroup) {
28580                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28581                     while (targets.indexOf(this.dropZone) > -1) {
28582                             targets.remove(this.dropZone);
28583                                 }
28584                     if (targets.length == 1) {
28585                                         this.dragZone.cachedTarget = null;
28586                         var el = Roo.get(targets[0].getEl());
28587                         var box = el.getBox(true);
28588                         targets[0].onNodeDrop(el.dom, {
28589                                 target: el.dom,
28590                                 xy: [box.x, box.y + box.height - 1]
28591                         }, null, this.getDragData(e));
28592                     }
28593                 }
28594         }
28595     },
28596     
28597     handleSelection: function(e) {
28598                 this.dragZone.cachedTarget = null;
28599         var item = this.findItemFromChild(e.getTarget());
28600         if (!item) {
28601                 this.clearSelections(true);
28602                 return;
28603         }
28604                 if (item && (this.multiSelect || this.singleSelect)){
28605                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28606                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28607                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28608                                 this.unselect(item);
28609                         } else {
28610                                 this.select(item, this.multiSelect && e.ctrlKey);
28611                                 this.lastSelection = item;
28612                         }
28613                 }
28614     },
28615
28616     onItemClick : function(item, index, e){
28617                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28618                         return false;
28619                 }
28620                 return true;
28621     },
28622
28623     unselect : function(nodeInfo, suppressEvent){
28624                 var node = this.getNode(nodeInfo);
28625                 if(node && this.isSelected(node)){
28626                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28627                                 Roo.fly(node).removeClass(this.selectedClass);
28628                                 this.selections.remove(node);
28629                                 if(!suppressEvent){
28630                                         this.fireEvent("selectionchange", this, this.selections);
28631                                 }
28632                         }
28633                 }
28634     }
28635 });
28636 /*
28637  * Based on:
28638  * Ext JS Library 1.1.1
28639  * Copyright(c) 2006-2007, Ext JS, LLC.
28640  *
28641  * Originally Released Under LGPL - original licence link has changed is not relivant.
28642  *
28643  * Fork - LGPL
28644  * <script type="text/javascript">
28645  */
28646  
28647 /**
28648  * @class Roo.LayoutManager
28649  * @extends Roo.util.Observable
28650  * Base class for layout managers.
28651  */
28652 Roo.LayoutManager = function(container, config){
28653     Roo.LayoutManager.superclass.constructor.call(this);
28654     this.el = Roo.get(container);
28655     // ie scrollbar fix
28656     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28657         document.body.scroll = "no";
28658     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28659         this.el.position('relative');
28660     }
28661     this.id = this.el.id;
28662     this.el.addClass("x-layout-container");
28663     /** false to disable window resize monitoring @type Boolean */
28664     this.monitorWindowResize = true;
28665     this.regions = {};
28666     this.addEvents({
28667         /**
28668          * @event layout
28669          * Fires when a layout is performed. 
28670          * @param {Roo.LayoutManager} this
28671          */
28672         "layout" : true,
28673         /**
28674          * @event regionresized
28675          * Fires when the user resizes a region. 
28676          * @param {Roo.LayoutRegion} region The resized region
28677          * @param {Number} newSize The new size (width for east/west, height for north/south)
28678          */
28679         "regionresized" : true,
28680         /**
28681          * @event regioncollapsed
28682          * Fires when a region is collapsed. 
28683          * @param {Roo.LayoutRegion} region The collapsed region
28684          */
28685         "regioncollapsed" : true,
28686         /**
28687          * @event regionexpanded
28688          * Fires when a region is expanded.  
28689          * @param {Roo.LayoutRegion} region The expanded region
28690          */
28691         "regionexpanded" : true
28692     });
28693     this.updating = false;
28694     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28695 };
28696
28697 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28698     /**
28699      * Returns true if this layout is currently being updated
28700      * @return {Boolean}
28701      */
28702     isUpdating : function(){
28703         return this.updating; 
28704     },
28705     
28706     /**
28707      * Suspend the LayoutManager from doing auto-layouts while
28708      * making multiple add or remove calls
28709      */
28710     beginUpdate : function(){
28711         this.updating = true;    
28712     },
28713     
28714     /**
28715      * Restore auto-layouts and optionally disable the manager from performing a layout
28716      * @param {Boolean} noLayout true to disable a layout update 
28717      */
28718     endUpdate : function(noLayout){
28719         this.updating = false;
28720         if(!noLayout){
28721             this.layout();
28722         }    
28723     },
28724     
28725     layout: function(){
28726         
28727     },
28728     
28729     onRegionResized : function(region, newSize){
28730         this.fireEvent("regionresized", region, newSize);
28731         this.layout();
28732     },
28733     
28734     onRegionCollapsed : function(region){
28735         this.fireEvent("regioncollapsed", region);
28736     },
28737     
28738     onRegionExpanded : function(region){
28739         this.fireEvent("regionexpanded", region);
28740     },
28741         
28742     /**
28743      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
28744      * performs box-model adjustments.
28745      * @return {Object} The size as an object {width: (the width), height: (the height)}
28746      */
28747     getViewSize : function(){
28748         var size;
28749         if(this.el.dom != document.body){
28750             size = this.el.getSize();
28751         }else{
28752             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
28753         }
28754         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
28755         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28756         return size;
28757     },
28758     
28759     /**
28760      * Returns the Element this layout is bound to.
28761      * @return {Roo.Element}
28762      */
28763     getEl : function(){
28764         return this.el;
28765     },
28766     
28767     /**
28768      * Returns the specified region.
28769      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
28770      * @return {Roo.LayoutRegion}
28771      */
28772     getRegion : function(target){
28773         return this.regions[target.toLowerCase()];
28774     },
28775     
28776     onWindowResize : function(){
28777         if(this.monitorWindowResize){
28778             this.layout();
28779         }
28780     }
28781 });/*
28782  * Based on:
28783  * Ext JS Library 1.1.1
28784  * Copyright(c) 2006-2007, Ext JS, LLC.
28785  *
28786  * Originally Released Under LGPL - original licence link has changed is not relivant.
28787  *
28788  * Fork - LGPL
28789  * <script type="text/javascript">
28790  */
28791 /**
28792  * @class Roo.BorderLayout
28793  * @extends Roo.LayoutManager
28794  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
28795  * please see: <br><br>
28796  * <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>
28797  * <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>
28798  * Example:
28799  <pre><code>
28800  var layout = new Roo.BorderLayout(document.body, {
28801     north: {
28802         initialSize: 25,
28803         titlebar: false
28804     },
28805     west: {
28806         split:true,
28807         initialSize: 200,
28808         minSize: 175,
28809         maxSize: 400,
28810         titlebar: true,
28811         collapsible: true
28812     },
28813     east: {
28814         split:true,
28815         initialSize: 202,
28816         minSize: 175,
28817         maxSize: 400,
28818         titlebar: true,
28819         collapsible: true
28820     },
28821     south: {
28822         split:true,
28823         initialSize: 100,
28824         minSize: 100,
28825         maxSize: 200,
28826         titlebar: true,
28827         collapsible: true
28828     },
28829     center: {
28830         titlebar: true,
28831         autoScroll:true,
28832         resizeTabs: true,
28833         minTabWidth: 50,
28834         preferredTabWidth: 150
28835     }
28836 });
28837
28838 // shorthand
28839 var CP = Roo.ContentPanel;
28840
28841 layout.beginUpdate();
28842 layout.add("north", new CP("north", "North"));
28843 layout.add("south", new CP("south", {title: "South", closable: true}));
28844 layout.add("west", new CP("west", {title: "West"}));
28845 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
28846 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
28847 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
28848 layout.getRegion("center").showPanel("center1");
28849 layout.endUpdate();
28850 </code></pre>
28851
28852 <b>The container the layout is rendered into can be either the body element or any other element.
28853 If it is not the body element, the container needs to either be an absolute positioned element,
28854 or you will need to add "position:relative" to the css of the container.  You will also need to specify
28855 the container size if it is not the body element.</b>
28856
28857 * @constructor
28858 * Create a new BorderLayout
28859 * @param {String/HTMLElement/Element} container The container this layout is bound to
28860 * @param {Object} config Configuration options
28861  */
28862 Roo.BorderLayout = function(container, config){
28863     config = config || {};
28864     Roo.BorderLayout.superclass.constructor.call(this, container, config);
28865     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
28866     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
28867         var target = this.factory.validRegions[i];
28868         if(config[target]){
28869             this.addRegion(target, config[target]);
28870         }
28871     }
28872 };
28873
28874 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
28875     /**
28876      * Creates and adds a new region if it doesn't already exist.
28877      * @param {String} target The target region key (north, south, east, west or center).
28878      * @param {Object} config The regions config object
28879      * @return {BorderLayoutRegion} The new region
28880      */
28881     addRegion : function(target, config){
28882         if(!this.regions[target]){
28883             var r = this.factory.create(target, this, config);
28884             this.bindRegion(target, r);
28885         }
28886         return this.regions[target];
28887     },
28888
28889     // private (kinda)
28890     bindRegion : function(name, r){
28891         this.regions[name] = r;
28892         r.on("visibilitychange", this.layout, this);
28893         r.on("paneladded", this.layout, this);
28894         r.on("panelremoved", this.layout, this);
28895         r.on("invalidated", this.layout, this);
28896         r.on("resized", this.onRegionResized, this);
28897         r.on("collapsed", this.onRegionCollapsed, this);
28898         r.on("expanded", this.onRegionExpanded, this);
28899     },
28900
28901     /**
28902      * Performs a layout update.
28903      */
28904     layout : function(){
28905         if(this.updating) return;
28906         var size = this.getViewSize();
28907         var w = size.width;
28908         var h = size.height;
28909         var centerW = w;
28910         var centerH = h;
28911         var centerY = 0;
28912         var centerX = 0;
28913         //var x = 0, y = 0;
28914
28915         var rs = this.regions;
28916         var north = rs["north"];
28917         var south = rs["south"]; 
28918         var west = rs["west"];
28919         var east = rs["east"];
28920         var center = rs["center"];
28921         //if(this.hideOnLayout){ // not supported anymore
28922             //c.el.setStyle("display", "none");
28923         //}
28924         if(north && north.isVisible()){
28925             var b = north.getBox();
28926             var m = north.getMargins();
28927             b.width = w - (m.left+m.right);
28928             b.x = m.left;
28929             b.y = m.top;
28930             centerY = b.height + b.y + m.bottom;
28931             centerH -= centerY;
28932             north.updateBox(this.safeBox(b));
28933         }
28934         if(south && south.isVisible()){
28935             var b = south.getBox();
28936             var m = south.getMargins();
28937             b.width = w - (m.left+m.right);
28938             b.x = m.left;
28939             var totalHeight = (b.height + m.top + m.bottom);
28940             b.y = h - totalHeight + m.top;
28941             centerH -= totalHeight;
28942             south.updateBox(this.safeBox(b));
28943         }
28944         if(west && west.isVisible()){
28945             var b = west.getBox();
28946             var m = west.getMargins();
28947             b.height = centerH - (m.top+m.bottom);
28948             b.x = m.left;
28949             b.y = centerY + m.top;
28950             var totalWidth = (b.width + m.left + m.right);
28951             centerX += totalWidth;
28952             centerW -= totalWidth;
28953             west.updateBox(this.safeBox(b));
28954         }
28955         if(east && east.isVisible()){
28956             var b = east.getBox();
28957             var m = east.getMargins();
28958             b.height = centerH - (m.top+m.bottom);
28959             var totalWidth = (b.width + m.left + m.right);
28960             b.x = w - totalWidth + m.left;
28961             b.y = centerY + m.top;
28962             centerW -= totalWidth;
28963             east.updateBox(this.safeBox(b));
28964         }
28965         if(center){
28966             var m = center.getMargins();
28967             var centerBox = {
28968                 x: centerX + m.left,
28969                 y: centerY + m.top,
28970                 width: centerW - (m.left+m.right),
28971                 height: centerH - (m.top+m.bottom)
28972             };
28973             //if(this.hideOnLayout){
28974                 //center.el.setStyle("display", "block");
28975             //}
28976             center.updateBox(this.safeBox(centerBox));
28977         }
28978         this.el.repaint();
28979         this.fireEvent("layout", this);
28980     },
28981
28982     // private
28983     safeBox : function(box){
28984         box.width = Math.max(0, box.width);
28985         box.height = Math.max(0, box.height);
28986         return box;
28987     },
28988
28989     /**
28990      * Adds a ContentPanel (or subclass) to this layout.
28991      * @param {String} target The target region key (north, south, east, west or center).
28992      * @param {Roo.ContentPanel} panel The panel to add
28993      * @return {Roo.ContentPanel} The added panel
28994      */
28995     add : function(target, panel){
28996          
28997         target = target.toLowerCase();
28998         return this.regions[target].add(panel);
28999     },
29000
29001     /**
29002      * Remove a ContentPanel (or subclass) to this layout.
29003      * @param {String} target The target region key (north, south, east, west or center).
29004      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
29005      * @return {Roo.ContentPanel} The removed panel
29006      */
29007     remove : function(target, panel){
29008         target = target.toLowerCase();
29009         return this.regions[target].remove(panel);
29010     },
29011
29012     /**
29013      * Searches all regions for a panel with the specified id
29014      * @param {String} panelId
29015      * @return {Roo.ContentPanel} The panel or null if it wasn't found
29016      */
29017     findPanel : function(panelId){
29018         var rs = this.regions;
29019         for(var target in rs){
29020             if(typeof rs[target] != "function"){
29021                 var p = rs[target].getPanel(panelId);
29022                 if(p){
29023                     return p;
29024                 }
29025             }
29026         }
29027         return null;
29028     },
29029
29030     /**
29031      * Searches all regions for a panel with the specified id and activates (shows) it.
29032      * @param {String/ContentPanel} panelId The panels id or the panel itself
29033      * @return {Roo.ContentPanel} The shown panel or null
29034      */
29035     showPanel : function(panelId) {
29036       var rs = this.regions;
29037       for(var target in rs){
29038          var r = rs[target];
29039          if(typeof r != "function"){
29040             if(r.hasPanel(panelId)){
29041                return r.showPanel(panelId);
29042             }
29043          }
29044       }
29045       return null;
29046    },
29047
29048    /**
29049      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
29050      * @param {Roo.state.Provider} provider (optional) An alternate state provider
29051      */
29052     restoreState : function(provider){
29053         if(!provider){
29054             provider = Roo.state.Manager;
29055         }
29056         var sm = new Roo.LayoutStateManager();
29057         sm.init(this, provider);
29058     },
29059
29060     /**
29061      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
29062      * object should contain properties for each region to add ContentPanels to, and each property's value should be
29063      * a valid ContentPanel config object.  Example:
29064      * <pre><code>
29065 // Create the main layout
29066 var layout = new Roo.BorderLayout('main-ct', {
29067     west: {
29068         split:true,
29069         minSize: 175,
29070         titlebar: true
29071     },
29072     center: {
29073         title:'Components'
29074     }
29075 }, 'main-ct');
29076
29077 // Create and add multiple ContentPanels at once via configs
29078 layout.batchAdd({
29079    west: {
29080        id: 'source-files',
29081        autoCreate:true,
29082        title:'Ext Source Files',
29083        autoScroll:true,
29084        fitToFrame:true
29085    },
29086    center : {
29087        el: cview,
29088        autoScroll:true,
29089        fitToFrame:true,
29090        toolbar: tb,
29091        resizeEl:'cbody'
29092    }
29093 });
29094 </code></pre>
29095      * @param {Object} regions An object containing ContentPanel configs by region name
29096      */
29097     batchAdd : function(regions){
29098         this.beginUpdate();
29099         for(var rname in regions){
29100             var lr = this.regions[rname];
29101             if(lr){
29102                 this.addTypedPanels(lr, regions[rname]);
29103             }
29104         }
29105         this.endUpdate();
29106     },
29107
29108     // private
29109     addTypedPanels : function(lr, ps){
29110         if(typeof ps == 'string'){
29111             lr.add(new Roo.ContentPanel(ps));
29112         }
29113         else if(ps instanceof Array){
29114             for(var i =0, len = ps.length; i < len; i++){
29115                 this.addTypedPanels(lr, ps[i]);
29116             }
29117         }
29118         else if(!ps.events){ // raw config?
29119             var el = ps.el;
29120             delete ps.el; // prevent conflict
29121             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29122         }
29123         else {  // panel object assumed!
29124             lr.add(ps);
29125         }
29126     },
29127     /**
29128      * Adds a xtype elements to the layout.
29129      * <pre><code>
29130
29131 layout.addxtype({
29132        xtype : 'ContentPanel',
29133        region: 'west',
29134        items: [ .... ]
29135    }
29136 );
29137
29138 layout.addxtype({
29139         xtype : 'NestedLayoutPanel',
29140         region: 'west',
29141         layout: {
29142            center: { },
29143            west: { }   
29144         },
29145         items : [ ... list of content panels or nested layout panels.. ]
29146    }
29147 );
29148 </code></pre>
29149      * @param {Object} cfg Xtype definition of item to add.
29150      */
29151     addxtype : function(cfg)
29152     {
29153         // basically accepts a pannel...
29154         // can accept a layout region..!?!?
29155        // console.log('BorderLayout add ' + cfg.xtype)
29156         
29157         if (!cfg.xtype.match(/Panel$/)) {
29158             return false;
29159         }
29160         var ret = false;
29161         var region = cfg.region;
29162         delete cfg.region;
29163         
29164           
29165         var xitems = [];
29166         if (cfg.items) {
29167             xitems = cfg.items;
29168             delete cfg.items;
29169         }
29170         
29171         
29172         switch(cfg.xtype) 
29173         {
29174             case 'ContentPanel':  // ContentPanel (el, cfg)
29175             case 'ScrollPanel':  // ContentPanel (el, cfg)
29176                 if(cfg.autoCreate) {
29177                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29178                 } else {
29179                     var el = this.el.createChild();
29180                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29181                 }
29182                 
29183                 this.add(region, ret);
29184                 break;
29185             
29186             
29187             case 'TreePanel': // our new panel!
29188                 cfg.el = this.el.createChild();
29189                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29190                 this.add(region, ret);
29191                 break;
29192             
29193             case 'NestedLayoutPanel': 
29194                 // create a new Layout (which is  a Border Layout...
29195                 var el = this.el.createChild();
29196                 var clayout = cfg.layout;
29197                 delete cfg.layout;
29198                 clayout.items   = clayout.items  || [];
29199                 // replace this exitems with the clayout ones..
29200                 xitems = clayout.items;
29201                  
29202                 
29203                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29204                     cfg.background = false;
29205                 }
29206                 var layout = new Roo.BorderLayout(el, clayout);
29207                 
29208                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29209                 //console.log('adding nested layout panel '  + cfg.toSource());
29210                 this.add(region, ret);
29211                 
29212                 break;
29213                 
29214             case 'GridPanel': 
29215             
29216                 // needs grid and region
29217                 
29218                 //var el = this.getRegion(region).el.createChild();
29219                 var el = this.el.createChild();
29220                 // create the grid first...
29221                 
29222                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29223                 delete cfg.grid;
29224                 if (region == 'center' && this.active ) {
29225                     cfg.background = false;
29226                 }
29227                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29228                 
29229                 this.add(region, ret);
29230                 if (cfg.background) {
29231                     ret.on('activate', function(gp) {
29232                         if (!gp.grid.rendered) {
29233                             gp.grid.render();
29234                         }
29235                     });
29236                 } else {
29237                     grid.render();
29238                 }
29239                 break;
29240            
29241                
29242                 
29243                 
29244             default: 
29245                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29246                 return;
29247              // GridPanel (grid, cfg)
29248             
29249         }
29250         this.beginUpdate();
29251         // add children..
29252         Roo.each(xitems, function(i)  {
29253             ret.addxtype(i);
29254         });
29255         this.endUpdate();
29256         return ret;
29257         
29258     }
29259 });
29260
29261 /**
29262  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29263  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29264  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29265  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29266  * <pre><code>
29267 // shorthand
29268 var CP = Roo.ContentPanel;
29269
29270 var layout = Roo.BorderLayout.create({
29271     north: {
29272         initialSize: 25,
29273         titlebar: false,
29274         panels: [new CP("north", "North")]
29275     },
29276     west: {
29277         split:true,
29278         initialSize: 200,
29279         minSize: 175,
29280         maxSize: 400,
29281         titlebar: true,
29282         collapsible: true,
29283         panels: [new CP("west", {title: "West"})]
29284     },
29285     east: {
29286         split:true,
29287         initialSize: 202,
29288         minSize: 175,
29289         maxSize: 400,
29290         titlebar: true,
29291         collapsible: true,
29292         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29293     },
29294     south: {
29295         split:true,
29296         initialSize: 100,
29297         minSize: 100,
29298         maxSize: 200,
29299         titlebar: true,
29300         collapsible: true,
29301         panels: [new CP("south", {title: "South", closable: true})]
29302     },
29303     center: {
29304         titlebar: true,
29305         autoScroll:true,
29306         resizeTabs: true,
29307         minTabWidth: 50,
29308         preferredTabWidth: 150,
29309         panels: [
29310             new CP("center1", {title: "Close Me", closable: true}),
29311             new CP("center2", {title: "Center Panel", closable: false})
29312         ]
29313     }
29314 }, document.body);
29315
29316 layout.getRegion("center").showPanel("center1");
29317 </code></pre>
29318  * @param config
29319  * @param targetEl
29320  */
29321 Roo.BorderLayout.create = function(config, targetEl){
29322     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29323     layout.beginUpdate();
29324     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29325     for(var j = 0, jlen = regions.length; j < jlen; j++){
29326         var lr = regions[j];
29327         if(layout.regions[lr] && config[lr].panels){
29328             var r = layout.regions[lr];
29329             var ps = config[lr].panels;
29330             layout.addTypedPanels(r, ps);
29331         }
29332     }
29333     layout.endUpdate();
29334     return layout;
29335 };
29336
29337 // private
29338 Roo.BorderLayout.RegionFactory = {
29339     // private
29340     validRegions : ["north","south","east","west","center"],
29341
29342     // private
29343     create : function(target, mgr, config){
29344         target = target.toLowerCase();
29345         if(config.lightweight || config.basic){
29346             return new Roo.BasicLayoutRegion(mgr, config, target);
29347         }
29348         switch(target){
29349             case "north":
29350                 return new Roo.NorthLayoutRegion(mgr, config);
29351             case "south":
29352                 return new Roo.SouthLayoutRegion(mgr, config);
29353             case "east":
29354                 return new Roo.EastLayoutRegion(mgr, config);
29355             case "west":
29356                 return new Roo.WestLayoutRegion(mgr, config);
29357             case "center":
29358                 return new Roo.CenterLayoutRegion(mgr, config);
29359         }
29360         throw 'Layout region "'+target+'" not supported.';
29361     }
29362 };/*
29363  * Based on:
29364  * Ext JS Library 1.1.1
29365  * Copyright(c) 2006-2007, Ext JS, LLC.
29366  *
29367  * Originally Released Under LGPL - original licence link has changed is not relivant.
29368  *
29369  * Fork - LGPL
29370  * <script type="text/javascript">
29371  */
29372  
29373 /**
29374  * @class Roo.BasicLayoutRegion
29375  * @extends Roo.util.Observable
29376  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29377  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29378  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29379  */
29380 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29381     this.mgr = mgr;
29382     this.position  = pos;
29383     this.events = {
29384         /**
29385          * @scope Roo.BasicLayoutRegion
29386          */
29387         
29388         /**
29389          * @event beforeremove
29390          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29391          * @param {Roo.LayoutRegion} this
29392          * @param {Roo.ContentPanel} panel The panel
29393          * @param {Object} e The cancel event object
29394          */
29395         "beforeremove" : true,
29396         /**
29397          * @event invalidated
29398          * Fires when the layout for this region is changed.
29399          * @param {Roo.LayoutRegion} this
29400          */
29401         "invalidated" : true,
29402         /**
29403          * @event visibilitychange
29404          * Fires when this region is shown or hidden 
29405          * @param {Roo.LayoutRegion} this
29406          * @param {Boolean} visibility true or false
29407          */
29408         "visibilitychange" : true,
29409         /**
29410          * @event paneladded
29411          * Fires when a panel is added. 
29412          * @param {Roo.LayoutRegion} this
29413          * @param {Roo.ContentPanel} panel The panel
29414          */
29415         "paneladded" : true,
29416         /**
29417          * @event panelremoved
29418          * Fires when a panel is removed. 
29419          * @param {Roo.LayoutRegion} this
29420          * @param {Roo.ContentPanel} panel The panel
29421          */
29422         "panelremoved" : true,
29423         /**
29424          * @event collapsed
29425          * Fires when this region is collapsed.
29426          * @param {Roo.LayoutRegion} this
29427          */
29428         "collapsed" : true,
29429         /**
29430          * @event expanded
29431          * Fires when this region is expanded.
29432          * @param {Roo.LayoutRegion} this
29433          */
29434         "expanded" : true,
29435         /**
29436          * @event slideshow
29437          * Fires when this region is slid into view.
29438          * @param {Roo.LayoutRegion} this
29439          */
29440         "slideshow" : true,
29441         /**
29442          * @event slidehide
29443          * Fires when this region slides out of view. 
29444          * @param {Roo.LayoutRegion} this
29445          */
29446         "slidehide" : true,
29447         /**
29448          * @event panelactivated
29449          * Fires when a panel is activated. 
29450          * @param {Roo.LayoutRegion} this
29451          * @param {Roo.ContentPanel} panel The activated panel
29452          */
29453         "panelactivated" : true,
29454         /**
29455          * @event resized
29456          * Fires when the user resizes this region. 
29457          * @param {Roo.LayoutRegion} this
29458          * @param {Number} newSize The new size (width for east/west, height for north/south)
29459          */
29460         "resized" : true
29461     };
29462     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29463     this.panels = new Roo.util.MixedCollection();
29464     this.panels.getKey = this.getPanelId.createDelegate(this);
29465     this.box = null;
29466     this.activePanel = null;
29467     // ensure listeners are added...
29468     
29469     if (config.listeners || config.events) {
29470         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29471             listeners : config.listeners || {},
29472             events : config.events || {}
29473         });
29474     }
29475     
29476     if(skipConfig !== true){
29477         this.applyConfig(config);
29478     }
29479 };
29480
29481 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29482     getPanelId : function(p){
29483         return p.getId();
29484     },
29485     
29486     applyConfig : function(config){
29487         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29488         this.config = config;
29489         
29490     },
29491     
29492     /**
29493      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29494      * the width, for horizontal (north, south) the height.
29495      * @param {Number} newSize The new width or height
29496      */
29497     resizeTo : function(newSize){
29498         var el = this.el ? this.el :
29499                  (this.activePanel ? this.activePanel.getEl() : null);
29500         if(el){
29501             switch(this.position){
29502                 case "east":
29503                 case "west":
29504                     el.setWidth(newSize);
29505                     this.fireEvent("resized", this, newSize);
29506                 break;
29507                 case "north":
29508                 case "south":
29509                     el.setHeight(newSize);
29510                     this.fireEvent("resized", this, newSize);
29511                 break;                
29512             }
29513         }
29514     },
29515     
29516     getBox : function(){
29517         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29518     },
29519     
29520     getMargins : function(){
29521         return this.margins;
29522     },
29523     
29524     updateBox : function(box){
29525         this.box = box;
29526         var el = this.activePanel.getEl();
29527         el.dom.style.left = box.x + "px";
29528         el.dom.style.top = box.y + "px";
29529         this.activePanel.setSize(box.width, box.height);
29530     },
29531     
29532     /**
29533      * Returns the container element for this region.
29534      * @return {Roo.Element}
29535      */
29536     getEl : function(){
29537         return this.activePanel;
29538     },
29539     
29540     /**
29541      * Returns true if this region is currently visible.
29542      * @return {Boolean}
29543      */
29544     isVisible : function(){
29545         return this.activePanel ? true : false;
29546     },
29547     
29548     setActivePanel : function(panel){
29549         panel = this.getPanel(panel);
29550         if(this.activePanel && this.activePanel != panel){
29551             this.activePanel.setActiveState(false);
29552             this.activePanel.getEl().setLeftTop(-10000,-10000);
29553         }
29554         this.activePanel = panel;
29555         panel.setActiveState(true);
29556         if(this.box){
29557             panel.setSize(this.box.width, this.box.height);
29558         }
29559         this.fireEvent("panelactivated", this, panel);
29560         this.fireEvent("invalidated");
29561     },
29562     
29563     /**
29564      * Show the specified panel.
29565      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29566      * @return {Roo.ContentPanel} The shown panel or null
29567      */
29568     showPanel : function(panel){
29569         if(panel = this.getPanel(panel)){
29570             this.setActivePanel(panel);
29571         }
29572         return panel;
29573     },
29574     
29575     /**
29576      * Get the active panel for this region.
29577      * @return {Roo.ContentPanel} The active panel or null
29578      */
29579     getActivePanel : function(){
29580         return this.activePanel;
29581     },
29582     
29583     /**
29584      * Add the passed ContentPanel(s)
29585      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29586      * @return {Roo.ContentPanel} The panel added (if only one was added)
29587      */
29588     add : function(panel){
29589         if(arguments.length > 1){
29590             for(var i = 0, len = arguments.length; i < len; i++) {
29591                 this.add(arguments[i]);
29592             }
29593             return null;
29594         }
29595         if(this.hasPanel(panel)){
29596             this.showPanel(panel);
29597             return panel;
29598         }
29599         var el = panel.getEl();
29600         if(el.dom.parentNode != this.mgr.el.dom){
29601             this.mgr.el.dom.appendChild(el.dom);
29602         }
29603         if(panel.setRegion){
29604             panel.setRegion(this);
29605         }
29606         this.panels.add(panel);
29607         el.setStyle("position", "absolute");
29608         if(!panel.background){
29609             this.setActivePanel(panel);
29610             if(this.config.initialSize && this.panels.getCount()==1){
29611                 this.resizeTo(this.config.initialSize);
29612             }
29613         }
29614         this.fireEvent("paneladded", this, panel);
29615         return panel;
29616     },
29617     
29618     /**
29619      * Returns true if the panel is in this region.
29620      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29621      * @return {Boolean}
29622      */
29623     hasPanel : function(panel){
29624         if(typeof panel == "object"){ // must be panel obj
29625             panel = panel.getId();
29626         }
29627         return this.getPanel(panel) ? true : false;
29628     },
29629     
29630     /**
29631      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29632      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29633      * @param {Boolean} preservePanel Overrides the config preservePanel option
29634      * @return {Roo.ContentPanel} The panel that was removed
29635      */
29636     remove : function(panel, preservePanel){
29637         panel = this.getPanel(panel);
29638         if(!panel){
29639             return null;
29640         }
29641         var e = {};
29642         this.fireEvent("beforeremove", this, panel, e);
29643         if(e.cancel === true){
29644             return null;
29645         }
29646         var panelId = panel.getId();
29647         this.panels.removeKey(panelId);
29648         return panel;
29649     },
29650     
29651     /**
29652      * Returns the panel specified or null if it's not in this region.
29653      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29654      * @return {Roo.ContentPanel}
29655      */
29656     getPanel : function(id){
29657         if(typeof id == "object"){ // must be panel obj
29658             return id;
29659         }
29660         return this.panels.get(id);
29661     },
29662     
29663     /**
29664      * Returns this regions position (north/south/east/west/center).
29665      * @return {String} 
29666      */
29667     getPosition: function(){
29668         return this.position;    
29669     }
29670 });/*
29671  * Based on:
29672  * Ext JS Library 1.1.1
29673  * Copyright(c) 2006-2007, Ext JS, LLC.
29674  *
29675  * Originally Released Under LGPL - original licence link has changed is not relivant.
29676  *
29677  * Fork - LGPL
29678  * <script type="text/javascript">
29679  */
29680  
29681 /**
29682  * @class Roo.LayoutRegion
29683  * @extends Roo.BasicLayoutRegion
29684  * This class represents a region in a layout manager.
29685  * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
29686  * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
29687  * @cfg {Boolean} floatable False to disable floating (defaults to true)
29688  * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
29689  * @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})
29690  * @cfg {String} tabPosition "top" or "bottom" (defaults to "bottom")
29691  * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
29692  * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
29693  * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
29694  * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
29695  * @cfg {String} title The title for the region (overrides panel titles)
29696  * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
29697  * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
29698  * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
29699  * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
29700  * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
29701  * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
29702  * the space available, similar to FireFox 1.5 tabs (defaults to false)
29703  * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
29704  * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
29705  * @cfg {Boolean} showPin True to show a pin button
29706 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
29707 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
29708 * @cfg {Boolean} disableTabTips True to disable tab tooltips
29709 * @cfg {Number} width  For East/West panels
29710 * @cfg {Number} height For North/South panels
29711 * @cfg {Boolean} split To show the splitter
29712  */
29713 Roo.LayoutRegion = function(mgr, config, pos){
29714     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
29715     var dh = Roo.DomHelper;
29716     /** This region's container element 
29717     * @type Roo.Element */
29718     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
29719     /** This region's title element 
29720     * @type Roo.Element */
29721
29722     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
29723         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
29724         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
29725     ]}, true);
29726     this.titleEl.enableDisplayMode();
29727     /** This region's title text element 
29728     * @type HTMLElement */
29729     this.titleTextEl = this.titleEl.dom.firstChild;
29730     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
29731     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
29732     this.closeBtn.enableDisplayMode();
29733     this.closeBtn.on("click", this.closeClicked, this);
29734     this.closeBtn.hide();
29735
29736     this.createBody(config);
29737     this.visible = true;
29738     this.collapsed = false;
29739
29740     if(config.hideWhenEmpty){
29741         this.hide();
29742         this.on("paneladded", this.validateVisibility, this);
29743         this.on("panelremoved", this.validateVisibility, this);
29744     }
29745     this.applyConfig(config);
29746 };
29747
29748 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
29749
29750     createBody : function(){
29751         /** This region's body element 
29752         * @type Roo.Element */
29753         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
29754     },
29755
29756     applyConfig : function(c){
29757         if(c.collapsible && this.position != "center" && !this.collapsedEl){
29758             var dh = Roo.DomHelper;
29759             if(c.titlebar !== false){
29760                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
29761                 this.collapseBtn.on("click", this.collapse, this);
29762                 this.collapseBtn.enableDisplayMode();
29763
29764                 if(c.showPin === true || this.showPin){
29765                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
29766                     this.stickBtn.enableDisplayMode();
29767                     this.stickBtn.on("click", this.expand, this);
29768                     this.stickBtn.hide();
29769                 }
29770             }
29771             /** This region's collapsed element
29772             * @type Roo.Element */
29773             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
29774                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
29775             ]}, true);
29776             if(c.floatable !== false){
29777                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
29778                this.collapsedEl.on("click", this.collapseClick, this);
29779             }
29780
29781             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
29782                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
29783                    id: "message", unselectable: "on", style:{"float":"left"}});
29784                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
29785              }
29786             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
29787             this.expandBtn.on("click", this.expand, this);
29788         }
29789         if(this.collapseBtn){
29790             this.collapseBtn.setVisible(c.collapsible == true);
29791         }
29792         this.cmargins = c.cmargins || this.cmargins ||
29793                          (this.position == "west" || this.position == "east" ?
29794                              {top: 0, left: 2, right:2, bottom: 0} :
29795                              {top: 2, left: 0, right:0, bottom: 2});
29796         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29797         this.bottomTabs = c.tabPosition != "top";
29798         this.autoScroll = c.autoScroll || false;
29799         if(this.autoScroll){
29800             this.bodyEl.setStyle("overflow", "auto");
29801         }else{
29802             this.bodyEl.setStyle("overflow", "hidden");
29803         }
29804         //if(c.titlebar !== false){
29805             if((!c.titlebar && !c.title) || c.titlebar === false){
29806                 this.titleEl.hide();
29807             }else{
29808                 this.titleEl.show();
29809                 if(c.title){
29810                     this.titleTextEl.innerHTML = c.title;
29811                 }
29812             }
29813         //}
29814         this.duration = c.duration || .30;
29815         this.slideDuration = c.slideDuration || .45;
29816         this.config = c;
29817         if(c.collapsed){
29818             this.collapse(true);
29819         }
29820         if(c.hidden){
29821             this.hide();
29822         }
29823     },
29824     /**
29825      * Returns true if this region is currently visible.
29826      * @return {Boolean}
29827      */
29828     isVisible : function(){
29829         return this.visible;
29830     },
29831
29832     /**
29833      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
29834      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
29835      */
29836     setCollapsedTitle : function(title){
29837         title = title || "&#160;";
29838         if(this.collapsedTitleTextEl){
29839             this.collapsedTitleTextEl.innerHTML = title;
29840         }
29841     },
29842
29843     getBox : function(){
29844         var b;
29845         if(!this.collapsed){
29846             b = this.el.getBox(false, true);
29847         }else{
29848             b = this.collapsedEl.getBox(false, true);
29849         }
29850         return b;
29851     },
29852
29853     getMargins : function(){
29854         return this.collapsed ? this.cmargins : this.margins;
29855     },
29856
29857     highlight : function(){
29858         this.el.addClass("x-layout-panel-dragover");
29859     },
29860
29861     unhighlight : function(){
29862         this.el.removeClass("x-layout-panel-dragover");
29863     },
29864
29865     updateBox : function(box){
29866         this.box = box;
29867         if(!this.collapsed){
29868             this.el.dom.style.left = box.x + "px";
29869             this.el.dom.style.top = box.y + "px";
29870             this.updateBody(box.width, box.height);
29871         }else{
29872             this.collapsedEl.dom.style.left = box.x + "px";
29873             this.collapsedEl.dom.style.top = box.y + "px";
29874             this.collapsedEl.setSize(box.width, box.height);
29875         }
29876         if(this.tabs){
29877             this.tabs.autoSizeTabs();
29878         }
29879     },
29880
29881     updateBody : function(w, h){
29882         if(w !== null){
29883             this.el.setWidth(w);
29884             w -= this.el.getBorderWidth("rl");
29885             if(this.config.adjustments){
29886                 w += this.config.adjustments[0];
29887             }
29888         }
29889         if(h !== null){
29890             this.el.setHeight(h);
29891             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
29892             h -= this.el.getBorderWidth("tb");
29893             if(this.config.adjustments){
29894                 h += this.config.adjustments[1];
29895             }
29896             this.bodyEl.setHeight(h);
29897             if(this.tabs){
29898                 h = this.tabs.syncHeight(h);
29899             }
29900         }
29901         if(this.panelSize){
29902             w = w !== null ? w : this.panelSize.width;
29903             h = h !== null ? h : this.panelSize.height;
29904         }
29905         if(this.activePanel){
29906             var el = this.activePanel.getEl();
29907             w = w !== null ? w : el.getWidth();
29908             h = h !== null ? h : el.getHeight();
29909             this.panelSize = {width: w, height: h};
29910             this.activePanel.setSize(w, h);
29911         }
29912         if(Roo.isIE && this.tabs){
29913             this.tabs.el.repaint();
29914         }
29915     },
29916
29917     /**
29918      * Returns the container element for this region.
29919      * @return {Roo.Element}
29920      */
29921     getEl : function(){
29922         return this.el;
29923     },
29924
29925     /**
29926      * Hides this region.
29927      */
29928     hide : function(){
29929         if(!this.collapsed){
29930             this.el.dom.style.left = "-2000px";
29931             this.el.hide();
29932         }else{
29933             this.collapsedEl.dom.style.left = "-2000px";
29934             this.collapsedEl.hide();
29935         }
29936         this.visible = false;
29937         this.fireEvent("visibilitychange", this, false);
29938     },
29939
29940     /**
29941      * Shows this region if it was previously hidden.
29942      */
29943     show : function(){
29944         if(!this.collapsed){
29945             this.el.show();
29946         }else{
29947             this.collapsedEl.show();
29948         }
29949         this.visible = true;
29950         this.fireEvent("visibilitychange", this, true);
29951     },
29952
29953     closeClicked : function(){
29954         if(this.activePanel){
29955             this.remove(this.activePanel);
29956         }
29957     },
29958
29959     collapseClick : function(e){
29960         if(this.isSlid){
29961            e.stopPropagation();
29962            this.slideIn();
29963         }else{
29964            e.stopPropagation();
29965            this.slideOut();
29966         }
29967     },
29968
29969     /**
29970      * Collapses this region.
29971      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
29972      */
29973     collapse : function(skipAnim){
29974         if(this.collapsed) return;
29975         this.collapsed = true;
29976         if(this.split){
29977             this.split.el.hide();
29978         }
29979         if(this.config.animate && skipAnim !== true){
29980             this.fireEvent("invalidated", this);
29981             this.animateCollapse();
29982         }else{
29983             this.el.setLocation(-20000,-20000);
29984             this.el.hide();
29985             this.collapsedEl.show();
29986             this.fireEvent("collapsed", this);
29987             this.fireEvent("invalidated", this);
29988         }
29989     },
29990
29991     animateCollapse : function(){
29992         // overridden
29993     },
29994
29995     /**
29996      * Expands this region if it was previously collapsed.
29997      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
29998      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
29999      */
30000     expand : function(e, skipAnim){
30001         if(e) e.stopPropagation();
30002         if(!this.collapsed || this.el.hasActiveFx()) return;
30003         if(this.isSlid){
30004             this.afterSlideIn();
30005             skipAnim = true;
30006         }
30007         this.collapsed = false;
30008         if(this.config.animate && skipAnim !== true){
30009             this.animateExpand();
30010         }else{
30011             this.el.show();
30012             if(this.split){
30013                 this.split.el.show();
30014             }
30015             this.collapsedEl.setLocation(-2000,-2000);
30016             this.collapsedEl.hide();
30017             this.fireEvent("invalidated", this);
30018             this.fireEvent("expanded", this);
30019         }
30020     },
30021
30022     animateExpand : function(){
30023         // overridden
30024     },
30025
30026     initTabs : function(){
30027         this.bodyEl.setStyle("overflow", "hidden");
30028         var ts = new Roo.TabPanel(this.bodyEl.dom, {
30029             tabPosition: this.bottomTabs ? 'bottom' : 'top',
30030             disableTooltips: this.config.disableTabTips
30031         });
30032         if(this.config.hideTabs){
30033             ts.stripWrap.setDisplayed(false);
30034         }
30035         this.tabs = ts;
30036         ts.resizeTabs = this.config.resizeTabs === true;
30037         ts.minTabWidth = this.config.minTabWidth || 40;
30038         ts.maxTabWidth = this.config.maxTabWidth || 250;
30039         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30040         ts.monitorResize = false;
30041         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30042         ts.bodyEl.addClass('x-layout-tabs-body');
30043         this.panels.each(this.initPanelAsTab, this);
30044     },
30045
30046     initPanelAsTab : function(panel){
30047         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30048                     this.config.closeOnTab && panel.isClosable());
30049         if(panel.tabTip !== undefined){
30050             ti.setTooltip(panel.tabTip);
30051         }
30052         ti.on("activate", function(){
30053               this.setActivePanel(panel);
30054         }, this);
30055         if(this.config.closeOnTab){
30056             ti.on("beforeclose", function(t, e){
30057                 e.cancel = true;
30058                 this.remove(panel);
30059             }, this);
30060         }
30061         return ti;
30062     },
30063
30064     updatePanelTitle : function(panel, title){
30065         if(this.activePanel == panel){
30066             this.updateTitle(title);
30067         }
30068         if(this.tabs){
30069             var ti = this.tabs.getTab(panel.getEl().id);
30070             ti.setText(title);
30071             if(panel.tabTip !== undefined){
30072                 ti.setTooltip(panel.tabTip);
30073             }
30074         }
30075     },
30076
30077     updateTitle : function(title){
30078         if(this.titleTextEl && !this.config.title){
30079             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
30080         }
30081     },
30082
30083     setActivePanel : function(panel){
30084         panel = this.getPanel(panel);
30085         if(this.activePanel && this.activePanel != panel){
30086             this.activePanel.setActiveState(false);
30087         }
30088         this.activePanel = panel;
30089         panel.setActiveState(true);
30090         if(this.panelSize){
30091             panel.setSize(this.panelSize.width, this.panelSize.height);
30092         }
30093         if(this.closeBtn){
30094             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30095         }
30096         this.updateTitle(panel.getTitle());
30097         if(this.tabs){
30098             this.fireEvent("invalidated", this);
30099         }
30100         this.fireEvent("panelactivated", this, panel);
30101     },
30102
30103     /**
30104      * Shows the specified panel.
30105      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30106      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30107      */
30108     showPanel : function(panel){
30109         if(panel = this.getPanel(panel)){
30110             if(this.tabs){
30111                 var tab = this.tabs.getTab(panel.getEl().id);
30112                 if(tab.isHidden()){
30113                     this.tabs.unhideTab(tab.id);
30114                 }
30115                 tab.activate();
30116             }else{
30117                 this.setActivePanel(panel);
30118             }
30119         }
30120         return panel;
30121     },
30122
30123     /**
30124      * Get the active panel for this region.
30125      * @return {Roo.ContentPanel} The active panel or null
30126      */
30127     getActivePanel : function(){
30128         return this.activePanel;
30129     },
30130
30131     validateVisibility : function(){
30132         if(this.panels.getCount() < 1){
30133             this.updateTitle("&#160;");
30134             this.closeBtn.hide();
30135             this.hide();
30136         }else{
30137             if(!this.isVisible()){
30138                 this.show();
30139             }
30140         }
30141     },
30142
30143     /**
30144      * Adds the passed ContentPanel(s) to this region.
30145      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30146      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30147      */
30148     add : function(panel){
30149         if(arguments.length > 1){
30150             for(var i = 0, len = arguments.length; i < len; i++) {
30151                 this.add(arguments[i]);
30152             }
30153             return null;
30154         }
30155         if(this.hasPanel(panel)){
30156             this.showPanel(panel);
30157             return panel;
30158         }
30159         panel.setRegion(this);
30160         this.panels.add(panel);
30161         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30162             this.bodyEl.dom.appendChild(panel.getEl().dom);
30163             if(panel.background !== true){
30164                 this.setActivePanel(panel);
30165             }
30166             this.fireEvent("paneladded", this, panel);
30167             return panel;
30168         }
30169         if(!this.tabs){
30170             this.initTabs();
30171         }else{
30172             this.initPanelAsTab(panel);
30173         }
30174         if(panel.background !== true){
30175             this.tabs.activate(panel.getEl().id);
30176         }
30177         this.fireEvent("paneladded", this, panel);
30178         return panel;
30179     },
30180
30181     /**
30182      * Hides the tab for the specified panel.
30183      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30184      */
30185     hidePanel : function(panel){
30186         if(this.tabs && (panel = this.getPanel(panel))){
30187             this.tabs.hideTab(panel.getEl().id);
30188         }
30189     },
30190
30191     /**
30192      * Unhides the tab for a previously hidden panel.
30193      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30194      */
30195     unhidePanel : function(panel){
30196         if(this.tabs && (panel = this.getPanel(panel))){
30197             this.tabs.unhideTab(panel.getEl().id);
30198         }
30199     },
30200
30201     clearPanels : function(){
30202         while(this.panels.getCount() > 0){
30203              this.remove(this.panels.first());
30204         }
30205     },
30206
30207     /**
30208      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30209      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30210      * @param {Boolean} preservePanel Overrides the config preservePanel option
30211      * @return {Roo.ContentPanel} The panel that was removed
30212      */
30213     remove : function(panel, preservePanel){
30214         panel = this.getPanel(panel);
30215         if(!panel){
30216             return null;
30217         }
30218         var e = {};
30219         this.fireEvent("beforeremove", this, panel, e);
30220         if(e.cancel === true){
30221             return null;
30222         }
30223         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30224         var panelId = panel.getId();
30225         this.panels.removeKey(panelId);
30226         if(preservePanel){
30227             document.body.appendChild(panel.getEl().dom);
30228         }
30229         if(this.tabs){
30230             this.tabs.removeTab(panel.getEl().id);
30231         }else if (!preservePanel){
30232             this.bodyEl.dom.removeChild(panel.getEl().dom);
30233         }
30234         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30235             var p = this.panels.first();
30236             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30237             tempEl.appendChild(p.getEl().dom);
30238             this.bodyEl.update("");
30239             this.bodyEl.dom.appendChild(p.getEl().dom);
30240             tempEl = null;
30241             this.updateTitle(p.getTitle());
30242             this.tabs = null;
30243             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30244             this.setActivePanel(p);
30245         }
30246         panel.setRegion(null);
30247         if(this.activePanel == panel){
30248             this.activePanel = null;
30249         }
30250         if(this.config.autoDestroy !== false && preservePanel !== true){
30251             try{panel.destroy();}catch(e){}
30252         }
30253         this.fireEvent("panelremoved", this, panel);
30254         return panel;
30255     },
30256
30257     /**
30258      * Returns the TabPanel component used by this region
30259      * @return {Roo.TabPanel}
30260      */
30261     getTabs : function(){
30262         return this.tabs;
30263     },
30264
30265     createTool : function(parentEl, className){
30266         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30267             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30268         btn.addClassOnOver("x-layout-tools-button-over");
30269         return btn;
30270     }
30271 });/*
30272  * Based on:
30273  * Ext JS Library 1.1.1
30274  * Copyright(c) 2006-2007, Ext JS, LLC.
30275  *
30276  * Originally Released Under LGPL - original licence link has changed is not relivant.
30277  *
30278  * Fork - LGPL
30279  * <script type="text/javascript">
30280  */
30281  
30282
30283
30284 /**
30285  * @class Roo.SplitLayoutRegion
30286  * @extends Roo.LayoutRegion
30287  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30288  */
30289 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30290     this.cursor = cursor;
30291     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30292 };
30293
30294 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30295     splitTip : "Drag to resize.",
30296     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30297     useSplitTips : false,
30298
30299     applyConfig : function(config){
30300         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30301         if(config.split){
30302             if(!this.split){
30303                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30304                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30305                 /** The SplitBar for this region 
30306                 * @type Roo.SplitBar */
30307                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30308                 this.split.on("moved", this.onSplitMove, this);
30309                 this.split.useShim = config.useShim === true;
30310                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30311                 if(this.useSplitTips){
30312                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30313                 }
30314                 if(config.collapsible){
30315                     this.split.el.on("dblclick", this.collapse,  this);
30316                 }
30317             }
30318             if(typeof config.minSize != "undefined"){
30319                 this.split.minSize = config.minSize;
30320             }
30321             if(typeof config.maxSize != "undefined"){
30322                 this.split.maxSize = config.maxSize;
30323             }
30324             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30325                 this.hideSplitter();
30326             }
30327         }
30328     },
30329
30330     getHMaxSize : function(){
30331          var cmax = this.config.maxSize || 10000;
30332          var center = this.mgr.getRegion("center");
30333          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30334     },
30335
30336     getVMaxSize : function(){
30337          var cmax = this.config.maxSize || 10000;
30338          var center = this.mgr.getRegion("center");
30339          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30340     },
30341
30342     onSplitMove : function(split, newSize){
30343         this.fireEvent("resized", this, newSize);
30344     },
30345     
30346     /** 
30347      * Returns the {@link Roo.SplitBar} for this region.
30348      * @return {Roo.SplitBar}
30349      */
30350     getSplitBar : function(){
30351         return this.split;
30352     },
30353     
30354     hide : function(){
30355         this.hideSplitter();
30356         Roo.SplitLayoutRegion.superclass.hide.call(this);
30357     },
30358
30359     hideSplitter : function(){
30360         if(this.split){
30361             this.split.el.setLocation(-2000,-2000);
30362             this.split.el.hide();
30363         }
30364     },
30365
30366     show : function(){
30367         if(this.split){
30368             this.split.el.show();
30369         }
30370         Roo.SplitLayoutRegion.superclass.show.call(this);
30371     },
30372     
30373     beforeSlide: function(){
30374         if(Roo.isGecko){// firefox overflow auto bug workaround
30375             this.bodyEl.clip();
30376             if(this.tabs) this.tabs.bodyEl.clip();
30377             if(this.activePanel){
30378                 this.activePanel.getEl().clip();
30379                 
30380                 if(this.activePanel.beforeSlide){
30381                     this.activePanel.beforeSlide();
30382                 }
30383             }
30384         }
30385     },
30386     
30387     afterSlide : function(){
30388         if(Roo.isGecko){// firefox overflow auto bug workaround
30389             this.bodyEl.unclip();
30390             if(this.tabs) this.tabs.bodyEl.unclip();
30391             if(this.activePanel){
30392                 this.activePanel.getEl().unclip();
30393                 if(this.activePanel.afterSlide){
30394                     this.activePanel.afterSlide();
30395                 }
30396             }
30397         }
30398     },
30399
30400     initAutoHide : function(){
30401         if(this.autoHide !== false){
30402             if(!this.autoHideHd){
30403                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30404                 this.autoHideHd = {
30405                     "mouseout": function(e){
30406                         if(!e.within(this.el, true)){
30407                             st.delay(500);
30408                         }
30409                     },
30410                     "mouseover" : function(e){
30411                         st.cancel();
30412                     },
30413                     scope : this
30414                 };
30415             }
30416             this.el.on(this.autoHideHd);
30417         }
30418     },
30419
30420     clearAutoHide : function(){
30421         if(this.autoHide !== false){
30422             this.el.un("mouseout", this.autoHideHd.mouseout);
30423             this.el.un("mouseover", this.autoHideHd.mouseover);
30424         }
30425     },
30426
30427     clearMonitor : function(){
30428         Roo.get(document).un("click", this.slideInIf, this);
30429     },
30430
30431     // these names are backwards but not changed for compat
30432     slideOut : function(){
30433         if(this.isSlid || this.el.hasActiveFx()){
30434             return;
30435         }
30436         this.isSlid = true;
30437         if(this.collapseBtn){
30438             this.collapseBtn.hide();
30439         }
30440         this.closeBtnState = this.closeBtn.getStyle('display');
30441         this.closeBtn.hide();
30442         if(this.stickBtn){
30443             this.stickBtn.show();
30444         }
30445         this.el.show();
30446         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30447         this.beforeSlide();
30448         this.el.setStyle("z-index", 10001);
30449         this.el.slideIn(this.getSlideAnchor(), {
30450             callback: function(){
30451                 this.afterSlide();
30452                 this.initAutoHide();
30453                 Roo.get(document).on("click", this.slideInIf, this);
30454                 this.fireEvent("slideshow", this);
30455             },
30456             scope: this,
30457             block: true
30458         });
30459     },
30460
30461     afterSlideIn : function(){
30462         this.clearAutoHide();
30463         this.isSlid = false;
30464         this.clearMonitor();
30465         this.el.setStyle("z-index", "");
30466         if(this.collapseBtn){
30467             this.collapseBtn.show();
30468         }
30469         this.closeBtn.setStyle('display', this.closeBtnState);
30470         if(this.stickBtn){
30471             this.stickBtn.hide();
30472         }
30473         this.fireEvent("slidehide", this);
30474     },
30475
30476     slideIn : function(cb){
30477         if(!this.isSlid || this.el.hasActiveFx()){
30478             Roo.callback(cb);
30479             return;
30480         }
30481         this.isSlid = false;
30482         this.beforeSlide();
30483         this.el.slideOut(this.getSlideAnchor(), {
30484             callback: function(){
30485                 this.el.setLeftTop(-10000, -10000);
30486                 this.afterSlide();
30487                 this.afterSlideIn();
30488                 Roo.callback(cb);
30489             },
30490             scope: this,
30491             block: true
30492         });
30493     },
30494     
30495     slideInIf : function(e){
30496         if(!e.within(this.el)){
30497             this.slideIn();
30498         }
30499     },
30500
30501     animateCollapse : function(){
30502         this.beforeSlide();
30503         this.el.setStyle("z-index", 20000);
30504         var anchor = this.getSlideAnchor();
30505         this.el.slideOut(anchor, {
30506             callback : function(){
30507                 this.el.setStyle("z-index", "");
30508                 this.collapsedEl.slideIn(anchor, {duration:.3});
30509                 this.afterSlide();
30510                 this.el.setLocation(-10000,-10000);
30511                 this.el.hide();
30512                 this.fireEvent("collapsed", this);
30513             },
30514             scope: this,
30515             block: true
30516         });
30517     },
30518
30519     animateExpand : function(){
30520         this.beforeSlide();
30521         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30522         this.el.setStyle("z-index", 20000);
30523         this.collapsedEl.hide({
30524             duration:.1
30525         });
30526         this.el.slideIn(this.getSlideAnchor(), {
30527             callback : function(){
30528                 this.el.setStyle("z-index", "");
30529                 this.afterSlide();
30530                 if(this.split){
30531                     this.split.el.show();
30532                 }
30533                 this.fireEvent("invalidated", this);
30534                 this.fireEvent("expanded", this);
30535             },
30536             scope: this,
30537             block: true
30538         });
30539     },
30540
30541     anchors : {
30542         "west" : "left",
30543         "east" : "right",
30544         "north" : "top",
30545         "south" : "bottom"
30546     },
30547
30548     sanchors : {
30549         "west" : "l",
30550         "east" : "r",
30551         "north" : "t",
30552         "south" : "b"
30553     },
30554
30555     canchors : {
30556         "west" : "tl-tr",
30557         "east" : "tr-tl",
30558         "north" : "tl-bl",
30559         "south" : "bl-tl"
30560     },
30561
30562     getAnchor : function(){
30563         return this.anchors[this.position];
30564     },
30565
30566     getCollapseAnchor : function(){
30567         return this.canchors[this.position];
30568     },
30569
30570     getSlideAnchor : function(){
30571         return this.sanchors[this.position];
30572     },
30573
30574     getAlignAdj : function(){
30575         var cm = this.cmargins;
30576         switch(this.position){
30577             case "west":
30578                 return [0, 0];
30579             break;
30580             case "east":
30581                 return [0, 0];
30582             break;
30583             case "north":
30584                 return [0, 0];
30585             break;
30586             case "south":
30587                 return [0, 0];
30588             break;
30589         }
30590     },
30591
30592     getExpandAdj : function(){
30593         var c = this.collapsedEl, cm = this.cmargins;
30594         switch(this.position){
30595             case "west":
30596                 return [-(cm.right+c.getWidth()+cm.left), 0];
30597             break;
30598             case "east":
30599                 return [cm.right+c.getWidth()+cm.left, 0];
30600             break;
30601             case "north":
30602                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30603             break;
30604             case "south":
30605                 return [0, cm.top+cm.bottom+c.getHeight()];
30606             break;
30607         }
30608     }
30609 });/*
30610  * Based on:
30611  * Ext JS Library 1.1.1
30612  * Copyright(c) 2006-2007, Ext JS, LLC.
30613  *
30614  * Originally Released Under LGPL - original licence link has changed is not relivant.
30615  *
30616  * Fork - LGPL
30617  * <script type="text/javascript">
30618  */
30619 /*
30620  * These classes are private internal classes
30621  */
30622 Roo.CenterLayoutRegion = function(mgr, config){
30623     Roo.LayoutRegion.call(this, mgr, config, "center");
30624     this.visible = true;
30625     this.minWidth = config.minWidth || 20;
30626     this.minHeight = config.minHeight || 20;
30627 };
30628
30629 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30630     hide : function(){
30631         // center panel can't be hidden
30632     },
30633     
30634     show : function(){
30635         // center panel can't be hidden
30636     },
30637     
30638     getMinWidth: function(){
30639         return this.minWidth;
30640     },
30641     
30642     getMinHeight: function(){
30643         return this.minHeight;
30644     }
30645 });
30646
30647
30648 Roo.NorthLayoutRegion = function(mgr, config){
30649     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
30650     if(this.split){
30651         this.split.placement = Roo.SplitBar.TOP;
30652         this.split.orientation = Roo.SplitBar.VERTICAL;
30653         this.split.el.addClass("x-layout-split-v");
30654     }
30655     var size = config.initialSize || config.height;
30656     if(typeof size != "undefined"){
30657         this.el.setHeight(size);
30658     }
30659 };
30660 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
30661     orientation: Roo.SplitBar.VERTICAL,
30662     getBox : function(){
30663         if(this.collapsed){
30664             return this.collapsedEl.getBox();
30665         }
30666         var box = this.el.getBox();
30667         if(this.split){
30668             box.height += this.split.el.getHeight();
30669         }
30670         return box;
30671     },
30672     
30673     updateBox : function(box){
30674         if(this.split && !this.collapsed){
30675             box.height -= this.split.el.getHeight();
30676             this.split.el.setLeft(box.x);
30677             this.split.el.setTop(box.y+box.height);
30678             this.split.el.setWidth(box.width);
30679         }
30680         if(this.collapsed){
30681             this.updateBody(box.width, null);
30682         }
30683         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30684     }
30685 });
30686
30687 Roo.SouthLayoutRegion = function(mgr, config){
30688     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
30689     if(this.split){
30690         this.split.placement = Roo.SplitBar.BOTTOM;
30691         this.split.orientation = Roo.SplitBar.VERTICAL;
30692         this.split.el.addClass("x-layout-split-v");
30693     }
30694     var size = config.initialSize || config.height;
30695     if(typeof size != "undefined"){
30696         this.el.setHeight(size);
30697     }
30698 };
30699 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
30700     orientation: Roo.SplitBar.VERTICAL,
30701     getBox : function(){
30702         if(this.collapsed){
30703             return this.collapsedEl.getBox();
30704         }
30705         var box = this.el.getBox();
30706         if(this.split){
30707             var sh = this.split.el.getHeight();
30708             box.height += sh;
30709             box.y -= sh;
30710         }
30711         return box;
30712     },
30713     
30714     updateBox : function(box){
30715         if(this.split && !this.collapsed){
30716             var sh = this.split.el.getHeight();
30717             box.height -= sh;
30718             box.y += sh;
30719             this.split.el.setLeft(box.x);
30720             this.split.el.setTop(box.y-sh);
30721             this.split.el.setWidth(box.width);
30722         }
30723         if(this.collapsed){
30724             this.updateBody(box.width, null);
30725         }
30726         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30727     }
30728 });
30729
30730 Roo.EastLayoutRegion = function(mgr, config){
30731     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
30732     if(this.split){
30733         this.split.placement = Roo.SplitBar.RIGHT;
30734         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30735         this.split.el.addClass("x-layout-split-h");
30736     }
30737     var size = config.initialSize || config.width;
30738     if(typeof size != "undefined"){
30739         this.el.setWidth(size);
30740     }
30741 };
30742 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
30743     orientation: Roo.SplitBar.HORIZONTAL,
30744     getBox : function(){
30745         if(this.collapsed){
30746             return this.collapsedEl.getBox();
30747         }
30748         var box = this.el.getBox();
30749         if(this.split){
30750             var sw = this.split.el.getWidth();
30751             box.width += sw;
30752             box.x -= sw;
30753         }
30754         return box;
30755     },
30756
30757     updateBox : function(box){
30758         if(this.split && !this.collapsed){
30759             var sw = this.split.el.getWidth();
30760             box.width -= sw;
30761             this.split.el.setLeft(box.x);
30762             this.split.el.setTop(box.y);
30763             this.split.el.setHeight(box.height);
30764             box.x += sw;
30765         }
30766         if(this.collapsed){
30767             this.updateBody(null, box.height);
30768         }
30769         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30770     }
30771 });
30772
30773 Roo.WestLayoutRegion = function(mgr, config){
30774     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
30775     if(this.split){
30776         this.split.placement = Roo.SplitBar.LEFT;
30777         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30778         this.split.el.addClass("x-layout-split-h");
30779     }
30780     var size = config.initialSize || config.width;
30781     if(typeof size != "undefined"){
30782         this.el.setWidth(size);
30783     }
30784 };
30785 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
30786     orientation: Roo.SplitBar.HORIZONTAL,
30787     getBox : function(){
30788         if(this.collapsed){
30789             return this.collapsedEl.getBox();
30790         }
30791         var box = this.el.getBox();
30792         if(this.split){
30793             box.width += this.split.el.getWidth();
30794         }
30795         return box;
30796     },
30797     
30798     updateBox : function(box){
30799         if(this.split && !this.collapsed){
30800             var sw = this.split.el.getWidth();
30801             box.width -= sw;
30802             this.split.el.setLeft(box.x+box.width);
30803             this.split.el.setTop(box.y);
30804             this.split.el.setHeight(box.height);
30805         }
30806         if(this.collapsed){
30807             this.updateBody(null, box.height);
30808         }
30809         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30810     }
30811 });
30812 /*
30813  * Based on:
30814  * Ext JS Library 1.1.1
30815  * Copyright(c) 2006-2007, Ext JS, LLC.
30816  *
30817  * Originally Released Under LGPL - original licence link has changed is not relivant.
30818  *
30819  * Fork - LGPL
30820  * <script type="text/javascript">
30821  */
30822  
30823  
30824 /*
30825  * Private internal class for reading and applying state
30826  */
30827 Roo.LayoutStateManager = function(layout){
30828      // default empty state
30829      this.state = {
30830         north: {},
30831         south: {},
30832         east: {},
30833         west: {}       
30834     };
30835 };
30836
30837 Roo.LayoutStateManager.prototype = {
30838     init : function(layout, provider){
30839         this.provider = provider;
30840         var state = provider.get(layout.id+"-layout-state");
30841         if(state){
30842             var wasUpdating = layout.isUpdating();
30843             if(!wasUpdating){
30844                 layout.beginUpdate();
30845             }
30846             for(var key in state){
30847                 if(typeof state[key] != "function"){
30848                     var rstate = state[key];
30849                     var r = layout.getRegion(key);
30850                     if(r && rstate){
30851                         if(rstate.size){
30852                             r.resizeTo(rstate.size);
30853                         }
30854                         if(rstate.collapsed == true){
30855                             r.collapse(true);
30856                         }else{
30857                             r.expand(null, true);
30858                         }
30859                     }
30860                 }
30861             }
30862             if(!wasUpdating){
30863                 layout.endUpdate();
30864             }
30865             this.state = state; 
30866         }
30867         this.layout = layout;
30868         layout.on("regionresized", this.onRegionResized, this);
30869         layout.on("regioncollapsed", this.onRegionCollapsed, this);
30870         layout.on("regionexpanded", this.onRegionExpanded, this);
30871     },
30872     
30873     storeState : function(){
30874         this.provider.set(this.layout.id+"-layout-state", this.state);
30875     },
30876     
30877     onRegionResized : function(region, newSize){
30878         this.state[region.getPosition()].size = newSize;
30879         this.storeState();
30880     },
30881     
30882     onRegionCollapsed : function(region){
30883         this.state[region.getPosition()].collapsed = true;
30884         this.storeState();
30885     },
30886     
30887     onRegionExpanded : function(region){
30888         this.state[region.getPosition()].collapsed = false;
30889         this.storeState();
30890     }
30891 };/*
30892  * Based on:
30893  * Ext JS Library 1.1.1
30894  * Copyright(c) 2006-2007, Ext JS, LLC.
30895  *
30896  * Originally Released Under LGPL - original licence link has changed is not relivant.
30897  *
30898  * Fork - LGPL
30899  * <script type="text/javascript">
30900  */
30901 /**
30902  * @class Roo.ContentPanel
30903  * @extends Roo.util.Observable
30904  * A basic ContentPanel element.
30905  * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes  (defaults to false)
30906  * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
30907  * @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
30908  * @cfg {Boolean} closable True if the panel can be closed/removed
30909  * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
30910  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
30911  * @cfg {Toolbar} toolbar A toolbar for this panel
30912  * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
30913  * @cfg {String} title The title for this panel
30914  * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
30915  * @cfg {String} url Calls {@link #setUrl} with this value
30916  * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
30917  * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
30918  * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
30919  * @constructor
30920  * Create a new ContentPanel.
30921  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
30922  * @param {String/Object} config A string to set only the title or a config object
30923  * @param {String} content (optional) Set the HTML content for this panel
30924  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
30925  */
30926 Roo.ContentPanel = function(el, config, content){
30927     
30928      
30929     /*
30930     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
30931         config = el;
30932         el = Roo.id();
30933     }
30934     if (config && config.parentLayout) { 
30935         el = config.parentLayout.el.createChild(); 
30936     }
30937     */
30938     if(el.autoCreate){ // xtype is available if this is called from factory
30939         config = el;
30940         el = Roo.id();
30941     }
30942     this.el = Roo.get(el);
30943     if(!this.el && config && config.autoCreate){
30944         if(typeof config.autoCreate == "object"){
30945             if(!config.autoCreate.id){
30946                 config.autoCreate.id = config.id||el;
30947             }
30948             this.el = Roo.DomHelper.append(document.body,
30949                         config.autoCreate, true);
30950         }else{
30951             this.el = Roo.DomHelper.append(document.body,
30952                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
30953         }
30954     }
30955     this.closable = false;
30956     this.loaded = false;
30957     this.active = false;
30958     if(typeof config == "string"){
30959         this.title = config;
30960     }else{
30961         Roo.apply(this, config);
30962     }
30963     
30964     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
30965         this.wrapEl = this.el.wrap();    
30966         this.toolbar = new Roo.Toolbar(this.el.insertSibling(false, 'before'), [] , this.toolbar);
30967         
30968     }
30969     
30970     
30971     
30972     if(this.resizeEl){
30973         this.resizeEl = Roo.get(this.resizeEl, true);
30974     }else{
30975         this.resizeEl = this.el;
30976     }
30977     this.addEvents({
30978         /**
30979          * @event activate
30980          * Fires when this panel is activated. 
30981          * @param {Roo.ContentPanel} this
30982          */
30983         "activate" : true,
30984         /**
30985          * @event deactivate
30986          * Fires when this panel is activated. 
30987          * @param {Roo.ContentPanel} this
30988          */
30989         "deactivate" : true,
30990
30991         /**
30992          * @event resize
30993          * Fires when this panel is resized if fitToFrame is true.
30994          * @param {Roo.ContentPanel} this
30995          * @param {Number} width The width after any component adjustments
30996          * @param {Number} height The height after any component adjustments
30997          */
30998         "resize" : true
30999     });
31000     if(this.autoScroll){
31001         this.resizeEl.setStyle("overflow", "auto");
31002     } else {
31003         // fix randome scrolling
31004         this.el.on('scroll', function() {
31005             this.scrollTo('top',0); 
31006         });
31007     }
31008     content = content || this.content;
31009     if(content){
31010         this.setContent(content);
31011     }
31012     if(config && config.url){
31013         this.setUrl(this.url, this.params, this.loadOnce);
31014     }
31015     
31016     
31017     
31018     Roo.ContentPanel.superclass.constructor.call(this);
31019 };
31020
31021 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31022     tabTip:'',
31023     setRegion : function(region){
31024         this.region = region;
31025         if(region){
31026            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31027         }else{
31028            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31029         } 
31030     },
31031     
31032     /**
31033      * Returns the toolbar for this Panel if one was configured. 
31034      * @return {Roo.Toolbar} 
31035      */
31036     getToolbar : function(){
31037         return this.toolbar;
31038     },
31039     
31040     setActiveState : function(active){
31041         this.active = active;
31042         if(!active){
31043             this.fireEvent("deactivate", this);
31044         }else{
31045             this.fireEvent("activate", this);
31046         }
31047     },
31048     /**
31049      * Updates this panel's element
31050      * @param {String} content The new content
31051      * @param {Boolean} loadScripts (optional) true to look for and process scripts
31052     */
31053     setContent : function(content, loadScripts){
31054         this.el.update(content, loadScripts);
31055     },
31056
31057     ignoreResize : function(w, h){
31058         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31059             return true;
31060         }else{
31061             this.lastSize = {width: w, height: h};
31062             return false;
31063         }
31064     },
31065     /**
31066      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31067      * @return {Roo.UpdateManager} The UpdateManager
31068      */
31069     getUpdateManager : function(){
31070         return this.el.getUpdateManager();
31071     },
31072      /**
31073      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31074      * @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:
31075 <pre><code>
31076 panel.load({
31077     url: "your-url.php",
31078     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31079     callback: yourFunction,
31080     scope: yourObject, //(optional scope)
31081     discardUrl: false,
31082     nocache: false,
31083     text: "Loading...",
31084     timeout: 30,
31085     scripts: false
31086 });
31087 </code></pre>
31088      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31089      * 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.
31090      * @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}
31091      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31092      * @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.
31093      * @return {Roo.ContentPanel} this
31094      */
31095     load : function(){
31096         var um = this.el.getUpdateManager();
31097         um.update.apply(um, arguments);
31098         return this;
31099     },
31100
31101
31102     /**
31103      * 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.
31104      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31105      * @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)
31106      * @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)
31107      * @return {Roo.UpdateManager} The UpdateManager
31108      */
31109     setUrl : function(url, params, loadOnce){
31110         if(this.refreshDelegate){
31111             this.removeListener("activate", this.refreshDelegate);
31112         }
31113         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31114         this.on("activate", this.refreshDelegate);
31115         return this.el.getUpdateManager();
31116     },
31117     
31118     _handleRefresh : function(url, params, loadOnce){
31119         if(!loadOnce || !this.loaded){
31120             var updater = this.el.getUpdateManager();
31121             updater.update(url, params, this._setLoaded.createDelegate(this));
31122         }
31123     },
31124     
31125     _setLoaded : function(){
31126         this.loaded = true;
31127     }, 
31128     
31129     /**
31130      * Returns this panel's id
31131      * @return {String} 
31132      */
31133     getId : function(){
31134         return this.el.id;
31135     },
31136     
31137     /** 
31138      * Returns this panel's element - used by regiosn to add.
31139      * @return {Roo.Element} 
31140      */
31141     getEl : function(){
31142         return this.wrapEl || this.el;
31143     },
31144     
31145     adjustForComponents : function(width, height){
31146         if(this.resizeEl != this.el){
31147             width -= this.el.getFrameWidth('lr');
31148             height -= this.el.getFrameWidth('tb');
31149         }
31150         if(this.toolbar){
31151             var te = this.toolbar.getEl();
31152             height -= te.getHeight();
31153             te.setWidth(width);
31154         }
31155         if(this.adjustments){
31156             width += this.adjustments[0];
31157             height += this.adjustments[1];
31158         }
31159         return {"width": width, "height": height};
31160     },
31161     
31162     setSize : function(width, height){
31163         if(this.fitToFrame && !this.ignoreResize(width, height)){
31164             if(this.fitContainer && this.resizeEl != this.el){
31165                 this.el.setSize(width, height);
31166             }
31167             var size = this.adjustForComponents(width, height);
31168             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31169             this.fireEvent('resize', this, size.width, size.height);
31170         }
31171     },
31172     
31173     /**
31174      * Returns this panel's title
31175      * @return {String} 
31176      */
31177     getTitle : function(){
31178         return this.title;
31179     },
31180     
31181     /**
31182      * Set this panel's title
31183      * @param {String} title
31184      */
31185     setTitle : function(title){
31186         this.title = title;
31187         if(this.region){
31188             this.region.updatePanelTitle(this, title);
31189         }
31190     },
31191     
31192     /**
31193      * Returns true is this panel was configured to be closable
31194      * @return {Boolean} 
31195      */
31196     isClosable : function(){
31197         return this.closable;
31198     },
31199     
31200     beforeSlide : function(){
31201         this.el.clip();
31202         this.resizeEl.clip();
31203     },
31204     
31205     afterSlide : function(){
31206         this.el.unclip();
31207         this.resizeEl.unclip();
31208     },
31209     
31210     /**
31211      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31212      *   Will fail silently if the {@link #setUrl} method has not been called.
31213      *   This does not activate the panel, just updates its content.
31214      */
31215     refresh : function(){
31216         if(this.refreshDelegate){
31217            this.loaded = false;
31218            this.refreshDelegate();
31219         }
31220     },
31221     
31222     /**
31223      * Destroys this panel
31224      */
31225     destroy : function(){
31226         this.el.removeAllListeners();
31227         var tempEl = document.createElement("span");
31228         tempEl.appendChild(this.el.dom);
31229         tempEl.innerHTML = "";
31230         this.el.remove();
31231         this.el = null;
31232     },
31233     
31234       /**
31235      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31236      * <pre><code>
31237
31238 layout.addxtype({
31239        xtype : 'Form',
31240        items: [ .... ]
31241    }
31242 );
31243
31244 </code></pre>
31245      * @param {Object} cfg Xtype definition of item to add.
31246      */
31247     
31248     addxtype : function(cfg) {
31249         // add form..
31250         if (cfg.xtype.match(/^Form$/)) {
31251             var el = this.el.createChild();
31252
31253             this.form = new  Roo.form.Form(cfg);
31254             
31255             
31256             if ( this.form.allItems.length) this.form.render(el.dom);
31257             return this.form;
31258         }
31259         if (['View', 'JsonView'].indexOf(cfg.xtype) > -1) {
31260             // views..
31261             cfg.el = this.el.appendChild(document.createElement("div"));
31262             // factory?
31263             var ret = new Roo[cfg.xtype](cfg);
31264             ret.render(false, ''); // render blank..
31265             return ret;
31266             
31267         }
31268         return false;
31269         
31270     }
31271 });
31272
31273 /**
31274  * @class Roo.GridPanel
31275  * @extends Roo.ContentPanel
31276  * @constructor
31277  * Create a new GridPanel.
31278  * @param {Roo.grid.Grid} grid The grid for this panel
31279  * @param {String/Object} config A string to set only the panel's title, or a config object
31280  */
31281 Roo.GridPanel = function(grid, config){
31282     
31283   
31284     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31285         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31286         
31287     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31288     
31289     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31290     
31291     if(this.toolbar){
31292         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31293     }
31294     // xtype created footer. - not sure if will work as we normally have to render first..
31295     if (this.footer && !this.footer.el && this.footer.xtype) {
31296         
31297         this.footer.container = this.grid.getView().getFooterPanel(true);
31298         this.footer.dataSource = this.grid.dataSource;
31299         this.footer = Roo.factory(this.footer, Roo);
31300         
31301     }
31302     
31303     grid.monitorWindowResize = false; // turn off autosizing
31304     grid.autoHeight = false;
31305     grid.autoWidth = false;
31306     this.grid = grid;
31307     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31308 };
31309
31310 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31311     getId : function(){
31312         return this.grid.id;
31313     },
31314     
31315     /**
31316      * Returns the grid for this panel
31317      * @return {Roo.grid.Grid} 
31318      */
31319     getGrid : function(){
31320         return this.grid;    
31321     },
31322     
31323     setSize : function(width, height){
31324         if(!this.ignoreResize(width, height)){
31325             var grid = this.grid;
31326             var size = this.adjustForComponents(width, height);
31327             grid.getGridEl().setSize(size.width, size.height);
31328             grid.autoSize();
31329         }
31330     },
31331     
31332     beforeSlide : function(){
31333         this.grid.getView().scroller.clip();
31334     },
31335     
31336     afterSlide : function(){
31337         this.grid.getView().scroller.unclip();
31338     },
31339     
31340     destroy : function(){
31341         this.grid.destroy();
31342         delete this.grid;
31343         Roo.GridPanel.superclass.destroy.call(this); 
31344     }
31345 });
31346
31347
31348 /**
31349  * @class Roo.NestedLayoutPanel
31350  * @extends Roo.ContentPanel
31351  * @constructor
31352  * Create a new NestedLayoutPanel.
31353  * 
31354  * 
31355  * @param {Roo.BorderLayout} layout The layout for this panel
31356  * @param {String/Object} config A string to set only the title or a config object
31357  */
31358 Roo.NestedLayoutPanel = function(layout, config)
31359 {
31360     // construct with only one argument..
31361     /* FIXME - implement nicer consturctors
31362     if (layout.layout) {
31363         config = layout;
31364         layout = config.layout;
31365         delete config.layout;
31366     }
31367     if (layout.xtype && !layout.getEl) {
31368         // then layout needs constructing..
31369         layout = Roo.factory(layout, Roo);
31370     }
31371     */
31372     
31373     
31374     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31375     
31376     layout.monitorWindowResize = false; // turn off autosizing
31377     this.layout = layout;
31378     this.layout.getEl().addClass("x-layout-nested-layout");
31379     
31380     
31381     
31382     
31383 };
31384
31385 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31386
31387     setSize : function(width, height){
31388         if(!this.ignoreResize(width, height)){
31389             var size = this.adjustForComponents(width, height);
31390             var el = this.layout.getEl();
31391             el.setSize(size.width, size.height);
31392             var touch = el.dom.offsetWidth;
31393             this.layout.layout();
31394             // ie requires a double layout on the first pass
31395             if(Roo.isIE && !this.initialized){
31396                 this.initialized = true;
31397                 this.layout.layout();
31398             }
31399         }
31400     },
31401     
31402     // activate all subpanels if not currently active..
31403     
31404     setActiveState : function(active){
31405         this.active = active;
31406         if(!active){
31407             this.fireEvent("deactivate", this);
31408             return;
31409         }
31410         
31411         this.fireEvent("activate", this);
31412         // not sure if this should happen before or after..
31413         if (!this.layout) {
31414             return; // should not happen..
31415         }
31416         var reg = false;
31417         for (var r in this.layout.regions) {
31418             reg = this.layout.getRegion(r);
31419             if (reg.getActivePanel()) {
31420                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31421                 reg.setActivePanel(reg.getActivePanel());
31422                 continue;
31423             }
31424             if (!reg.panels.length) {
31425                 continue;
31426             }
31427             reg.showPanel(reg.getPanel(0));
31428         }
31429         
31430         
31431         
31432         
31433     },
31434     
31435     /**
31436      * Returns the nested BorderLayout for this panel
31437      * @return {Roo.BorderLayout} 
31438      */
31439     getLayout : function(){
31440         return this.layout;
31441     },
31442     
31443      /**
31444      * Adds a xtype elements to the layout of the nested panel
31445      * <pre><code>
31446
31447 panel.addxtype({
31448        xtype : 'ContentPanel',
31449        region: 'west',
31450        items: [ .... ]
31451    }
31452 );
31453
31454 panel.addxtype({
31455         xtype : 'NestedLayoutPanel',
31456         region: 'west',
31457         layout: {
31458            center: { },
31459            west: { }   
31460         },
31461         items : [ ... list of content panels or nested layout panels.. ]
31462    }
31463 );
31464 </code></pre>
31465      * @param {Object} cfg Xtype definition of item to add.
31466      */
31467     addxtype : function(cfg) {
31468         return this.layout.addxtype(cfg);
31469     
31470     }
31471 });
31472
31473 Roo.ScrollPanel = function(el, config, content){
31474     config = config || {};
31475     config.fitToFrame = true;
31476     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31477     
31478     this.el.dom.style.overflow = "hidden";
31479     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31480     this.el.removeClass("x-layout-inactive-content");
31481     this.el.on("mousewheel", this.onWheel, this);
31482
31483     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31484     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31485     up.unselectable(); down.unselectable();
31486     up.on("click", this.scrollUp, this);
31487     down.on("click", this.scrollDown, this);
31488     up.addClassOnOver("x-scroller-btn-over");
31489     down.addClassOnOver("x-scroller-btn-over");
31490     up.addClassOnClick("x-scroller-btn-click");
31491     down.addClassOnClick("x-scroller-btn-click");
31492     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31493
31494     this.resizeEl = this.el;
31495     this.el = wrap; this.up = up; this.down = down;
31496 };
31497
31498 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31499     increment : 100,
31500     wheelIncrement : 5,
31501     scrollUp : function(){
31502         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31503     },
31504
31505     scrollDown : function(){
31506         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31507     },
31508
31509     afterScroll : function(){
31510         var el = this.resizeEl;
31511         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31512         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31513         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31514     },
31515
31516     setSize : function(){
31517         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31518         this.afterScroll();
31519     },
31520
31521     onWheel : function(e){
31522         var d = e.getWheelDelta();
31523         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31524         this.afterScroll();
31525         e.stopEvent();
31526     },
31527
31528     setContent : function(content, loadScripts){
31529         this.resizeEl.update(content, loadScripts);
31530     }
31531
31532 });
31533
31534
31535
31536
31537
31538
31539
31540
31541
31542 /**
31543  * @class Roo.TreePanel
31544  * @extends Roo.ContentPanel
31545  * @constructor
31546  * Create a new TreePanel. - defaults to fit/scoll contents.
31547  * @param {String/Object} config A string to set only the panel's title, or a config object
31548  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
31549  */
31550 Roo.TreePanel = function(config){
31551     var el = config.el;
31552     var tree = config.tree;
31553     delete config.tree; 
31554     delete config.el; // hopefull!
31555     
31556     // wrapper for IE7 strict & safari scroll issue
31557     
31558     var treeEl = el.createChild();
31559     config.resizeEl = treeEl;
31560     
31561     
31562     
31563     Roo.TreePanel.superclass.constructor.call(this, el, config);
31564  
31565  
31566     this.tree = new Roo.tree.TreePanel(treeEl , tree);
31567     //console.log(tree);
31568     this.on('activate', function()
31569     {
31570         if (this.tree.rendered) {
31571             return;
31572         }
31573         //console.log('render tree');
31574         this.tree.render();
31575     });
31576     
31577     this.on('resize',  function (cp, w, h) {
31578             this.tree.innerCt.setWidth(w);
31579             this.tree.innerCt.setHeight(h);
31580             this.tree.innerCt.setStyle('overflow-y', 'auto');
31581     });
31582
31583         
31584     
31585 };
31586
31587 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
31588     fitToFrame : true,
31589     autoScroll : true
31590 });
31591
31592
31593
31594
31595
31596
31597
31598
31599
31600
31601
31602 /*
31603  * Based on:
31604  * Ext JS Library 1.1.1
31605  * Copyright(c) 2006-2007, Ext JS, LLC.
31606  *
31607  * Originally Released Under LGPL - original licence link has changed is not relivant.
31608  *
31609  * Fork - LGPL
31610  * <script type="text/javascript">
31611  */
31612  
31613
31614 /**
31615  * @class Roo.ReaderLayout
31616  * @extends Roo.BorderLayout
31617  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
31618  * center region containing two nested regions (a top one for a list view and one for item preview below),
31619  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
31620  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
31621  * expedites the setup of the overall layout and regions for this common application style.
31622  * Example:
31623  <pre><code>
31624 var reader = new Roo.ReaderLayout();
31625 var CP = Roo.ContentPanel;  // shortcut for adding
31626
31627 reader.beginUpdate();
31628 reader.add("north", new CP("north", "North"));
31629 reader.add("west", new CP("west", {title: "West"}));
31630 reader.add("east", new CP("east", {title: "East"}));
31631
31632 reader.regions.listView.add(new CP("listView", "List"));
31633 reader.regions.preview.add(new CP("preview", "Preview"));
31634 reader.endUpdate();
31635 </code></pre>
31636 * @constructor
31637 * Create a new ReaderLayout
31638 * @param {Object} config Configuration options
31639 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
31640 * document.body if omitted)
31641 */
31642 Roo.ReaderLayout = function(config, renderTo){
31643     var c = config || {size:{}};
31644     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
31645         north: c.north !== false ? Roo.apply({
31646             split:false,
31647             initialSize: 32,
31648             titlebar: false
31649         }, c.north) : false,
31650         west: c.west !== false ? Roo.apply({
31651             split:true,
31652             initialSize: 200,
31653             minSize: 175,
31654             maxSize: 400,
31655             titlebar: true,
31656             collapsible: true,
31657             animate: true,
31658             margins:{left:5,right:0,bottom:5,top:5},
31659             cmargins:{left:5,right:5,bottom:5,top:5}
31660         }, c.west) : false,
31661         east: c.east !== false ? Roo.apply({
31662             split:true,
31663             initialSize: 200,
31664             minSize: 175,
31665             maxSize: 400,
31666             titlebar: true,
31667             collapsible: true,
31668             animate: true,
31669             margins:{left:0,right:5,bottom:5,top:5},
31670             cmargins:{left:5,right:5,bottom:5,top:5}
31671         }, c.east) : false,
31672         center: Roo.apply({
31673             tabPosition: 'top',
31674             autoScroll:false,
31675             closeOnTab: true,
31676             titlebar:false,
31677             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
31678         }, c.center)
31679     });
31680
31681     this.el.addClass('x-reader');
31682
31683     this.beginUpdate();
31684
31685     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
31686         south: c.preview !== false ? Roo.apply({
31687             split:true,
31688             initialSize: 200,
31689             minSize: 100,
31690             autoScroll:true,
31691             collapsible:true,
31692             titlebar: true,
31693             cmargins:{top:5,left:0, right:0, bottom:0}
31694         }, c.preview) : false,
31695         center: Roo.apply({
31696             autoScroll:false,
31697             titlebar:false,
31698             minHeight:200
31699         }, c.listView)
31700     });
31701     this.add('center', new Roo.NestedLayoutPanel(inner,
31702             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
31703
31704     this.endUpdate();
31705
31706     this.regions.preview = inner.getRegion('south');
31707     this.regions.listView = inner.getRegion('center');
31708 };
31709
31710 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
31711  * Based on:
31712  * Ext JS Library 1.1.1
31713  * Copyright(c) 2006-2007, Ext JS, LLC.
31714  *
31715  * Originally Released Under LGPL - original licence link has changed is not relivant.
31716  *
31717  * Fork - LGPL
31718  * <script type="text/javascript">
31719  */
31720  
31721 /**
31722  * @class Roo.grid.Grid
31723  * @extends Roo.util.Observable
31724  * This class represents the primary interface of a component based grid control.
31725  * <br><br>Usage:<pre><code>
31726  var grid = new Roo.grid.Grid("my-container-id", {
31727      ds: myDataStore,
31728      cm: myColModel,
31729      selModel: mySelectionModel,
31730      autoSizeColumns: true,
31731      monitorWindowResize: false,
31732      trackMouseOver: true
31733  });
31734  // set any options
31735  grid.render();
31736  * </code></pre>
31737  * <b>Common Problems:</b><br/>
31738  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
31739  * element will correct this<br/>
31740  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
31741  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
31742  * are unpredictable.<br/>
31743  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
31744  * grid to calculate dimensions/offsets.<br/>
31745   * @constructor
31746  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
31747  * The container MUST have some type of size defined for the grid to fill. The container will be
31748  * automatically set to position relative if it isn't already.
31749  * @param {Object} config A config object that sets properties on this grid.
31750  */
31751 Roo.grid.Grid = function(container, config){
31752         // initialize the container
31753         this.container = Roo.get(container);
31754         this.container.update("");
31755         this.container.setStyle("overflow", "hidden");
31756     this.container.addClass('x-grid-container');
31757
31758     this.id = this.container.id;
31759
31760     Roo.apply(this, config);
31761     // check and correct shorthanded configs
31762     if(this.ds){
31763         this.dataSource = this.ds;
31764         delete this.ds;
31765     }
31766     if(this.cm){
31767         this.colModel = this.cm;
31768         delete this.cm;
31769     }
31770     if(this.sm){
31771         this.selModel = this.sm;
31772         delete this.sm;
31773     }
31774
31775     if (this.selModel) {
31776         this.selModel = Roo.factory(this.selModel, Roo.grid);
31777         this.sm = this.selModel;
31778         this.sm.xmodule = this.xmodule || false;
31779     }
31780     if (typeof(this.colModel.config) == 'undefined') {
31781         this.colModel = new Roo.grid.ColumnModel(this.colModel);
31782         this.cm = this.colModel;
31783         this.cm.xmodule = this.xmodule || false;
31784     }
31785     if (this.dataSource) {
31786         this.dataSource= Roo.factory(this.dataSource, Roo.data);
31787         this.ds = this.dataSource;
31788         this.ds.xmodule = this.xmodule || false;
31789         
31790     }
31791     
31792     
31793     
31794     if(this.width){
31795         this.container.setWidth(this.width);
31796     }
31797
31798     if(this.height){
31799         this.container.setHeight(this.height);
31800     }
31801     /** @private */
31802         this.addEvents({
31803             // raw events
31804             /**
31805              * @event click
31806              * The raw click event for the entire grid.
31807              * @param {Roo.EventObject} e
31808              */
31809             "click" : true,
31810             /**
31811              * @event dblclick
31812              * The raw dblclick event for the entire grid.
31813              * @param {Roo.EventObject} e
31814              */
31815             "dblclick" : true,
31816             /**
31817              * @event contextmenu
31818              * The raw contextmenu event for the entire grid.
31819              * @param {Roo.EventObject} e
31820              */
31821             "contextmenu" : true,
31822             /**
31823              * @event mousedown
31824              * The raw mousedown event for the entire grid.
31825              * @param {Roo.EventObject} e
31826              */
31827             "mousedown" : true,
31828             /**
31829              * @event mouseup
31830              * The raw mouseup event for the entire grid.
31831              * @param {Roo.EventObject} e
31832              */
31833             "mouseup" : true,
31834             /**
31835              * @event mouseover
31836              * The raw mouseover event for the entire grid.
31837              * @param {Roo.EventObject} e
31838              */
31839             "mouseover" : true,
31840             /**
31841              * @event mouseout
31842              * The raw mouseout event for the entire grid.
31843              * @param {Roo.EventObject} e
31844              */
31845             "mouseout" : true,
31846             /**
31847              * @event keypress
31848              * The raw keypress event for the entire grid.
31849              * @param {Roo.EventObject} e
31850              */
31851             "keypress" : true,
31852             /**
31853              * @event keydown
31854              * The raw keydown event for the entire grid.
31855              * @param {Roo.EventObject} e
31856              */
31857             "keydown" : true,
31858
31859             // custom events
31860
31861             /**
31862              * @event cellclick
31863              * Fires when a cell is clicked
31864              * @param {Grid} this
31865              * @param {Number} rowIndex
31866              * @param {Number} columnIndex
31867              * @param {Roo.EventObject} e
31868              */
31869             "cellclick" : true,
31870             /**
31871              * @event celldblclick
31872              * Fires when a cell is double clicked
31873              * @param {Grid} this
31874              * @param {Number} rowIndex
31875              * @param {Number} columnIndex
31876              * @param {Roo.EventObject} e
31877              */
31878             "celldblclick" : true,
31879             /**
31880              * @event rowclick
31881              * Fires when a row is clicked
31882              * @param {Grid} this
31883              * @param {Number} rowIndex
31884              * @param {Roo.EventObject} e
31885              */
31886             "rowclick" : true,
31887             /**
31888              * @event rowdblclick
31889              * Fires when a row is double clicked
31890              * @param {Grid} this
31891              * @param {Number} rowIndex
31892              * @param {Roo.EventObject} e
31893              */
31894             "rowdblclick" : true,
31895             /**
31896              * @event headerclick
31897              * Fires when a header is clicked
31898              * @param {Grid} this
31899              * @param {Number} columnIndex
31900              * @param {Roo.EventObject} e
31901              */
31902             "headerclick" : true,
31903             /**
31904              * @event headerdblclick
31905              * Fires when a header cell is double clicked
31906              * @param {Grid} this
31907              * @param {Number} columnIndex
31908              * @param {Roo.EventObject} e
31909              */
31910             "headerdblclick" : true,
31911             /**
31912              * @event rowcontextmenu
31913              * Fires when a row is right clicked
31914              * @param {Grid} this
31915              * @param {Number} rowIndex
31916              * @param {Roo.EventObject} e
31917              */
31918             "rowcontextmenu" : true,
31919             /**
31920          * @event cellcontextmenu
31921          * Fires when a cell is right clicked
31922          * @param {Grid} this
31923          * @param {Number} rowIndex
31924          * @param {Number} cellIndex
31925          * @param {Roo.EventObject} e
31926          */
31927          "cellcontextmenu" : true,
31928             /**
31929              * @event headercontextmenu
31930              * Fires when a header is right clicked
31931              * @param {Grid} this
31932              * @param {Number} columnIndex
31933              * @param {Roo.EventObject} e
31934              */
31935             "headercontextmenu" : true,
31936             /**
31937              * @event bodyscroll
31938              * Fires when the body element is scrolled
31939              * @param {Number} scrollLeft
31940              * @param {Number} scrollTop
31941              */
31942             "bodyscroll" : true,
31943             /**
31944              * @event columnresize
31945              * Fires when the user resizes a column
31946              * @param {Number} columnIndex
31947              * @param {Number} newSize
31948              */
31949             "columnresize" : true,
31950             /**
31951              * @event columnmove
31952              * Fires when the user moves a column
31953              * @param {Number} oldIndex
31954              * @param {Number} newIndex
31955              */
31956             "columnmove" : true,
31957             /**
31958              * @event startdrag
31959              * Fires when row(s) start being dragged
31960              * @param {Grid} this
31961              * @param {Roo.GridDD} dd The drag drop object
31962              * @param {event} e The raw browser event
31963              */
31964             "startdrag" : true,
31965             /**
31966              * @event enddrag
31967              * Fires when a drag operation is complete
31968              * @param {Grid} this
31969              * @param {Roo.GridDD} dd The drag drop object
31970              * @param {event} e The raw browser event
31971              */
31972             "enddrag" : true,
31973             /**
31974              * @event dragdrop
31975              * Fires when dragged row(s) are dropped on a valid DD target
31976              * @param {Grid} this
31977              * @param {Roo.GridDD} dd The drag drop object
31978              * @param {String} targetId The target drag drop object
31979              * @param {event} e The raw browser event
31980              */
31981             "dragdrop" : true,
31982             /**
31983              * @event dragover
31984              * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
31985              * @param {Grid} this
31986              * @param {Roo.GridDD} dd The drag drop object
31987              * @param {String} targetId The target drag drop object
31988              * @param {event} e The raw browser event
31989              */
31990             "dragover" : true,
31991             /**
31992              * @event dragenter
31993              *  Fires when the dragged row(s) first cross another DD target while being dragged
31994              * @param {Grid} this
31995              * @param {Roo.GridDD} dd The drag drop object
31996              * @param {String} targetId The target drag drop object
31997              * @param {event} e The raw browser event
31998              */
31999             "dragenter" : true,
32000             /**
32001              * @event dragout
32002              * Fires when the dragged row(s) leave another DD target while being dragged
32003              * @param {Grid} this
32004              * @param {Roo.GridDD} dd The drag drop object
32005              * @param {String} targetId The target drag drop object
32006              * @param {event} e The raw browser event
32007              */
32008             "dragout" : true,
32009         /**
32010          * @event render
32011          * Fires when the grid is rendered
32012          * @param {Grid} grid
32013          */
32014         render : true
32015     });
32016
32017     Roo.grid.Grid.superclass.constructor.call(this);
32018 };
32019 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32020     
32021     /**
32022      * @cfg {String} ddGroup - drag drop group.
32023          */
32024     
32025     /**
32026      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32027          */
32028         minColumnWidth : 25,
32029
32030     /**
32031          * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32032          * <b>on initial render.</b> It is more efficient to explicitly size the columns
32033          * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
32034          */
32035         autoSizeColumns : false,
32036
32037         /**
32038          * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32039          */
32040         autoSizeHeaders : true,
32041
32042         /**
32043          * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32044          */
32045         monitorWindowResize : true,
32046
32047         /**
32048          * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32049          * rows measured to get a columns size. Default is 0 (all rows).
32050          */
32051         maxRowsToMeasure : 0,
32052
32053         /**
32054          * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32055          */
32056         trackMouseOver : true,
32057
32058     /**
32059          * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32060          */
32061     
32062         /**
32063          * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32064          */
32065         enableDragDrop : false,
32066
32067         /**
32068          * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32069          */
32070         enableColumnMove : true,
32071
32072         /**
32073          * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32074          */
32075         enableColumnHide : true,
32076
32077         /**
32078          * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32079          */
32080         enableRowHeightSync : false,
32081
32082         /**
32083          * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32084          */
32085         stripeRows : true,
32086
32087         /**
32088          * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32089          */
32090         autoHeight : false,
32091
32092     /**
32093      * @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.
32094      */
32095     autoExpandColumn : false,
32096
32097     /**
32098     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32099     * Default is 50.
32100     */
32101     autoExpandMin : 50,
32102
32103     /**
32104     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32105     */
32106     autoExpandMax : 1000,
32107
32108     /**
32109          * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32110          */
32111         view : null,
32112
32113         /**
32114      * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32115          */
32116         loadMask : false,
32117     /**
32118      * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
32119          */
32120         dropTarget: false,
32121     // private
32122     rendered : false,
32123
32124     /**
32125     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32126     * of a fixed width. Default is false.
32127     */
32128     /**
32129     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32130     */
32131     /**
32132      * Called once after all setup has been completed and the grid is ready to be rendered.
32133      * @return {Roo.grid.Grid} this
32134      */
32135     render : function(){
32136         var c = this.container;
32137         // try to detect autoHeight/width mode
32138         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32139             this.autoHeight = true;
32140         }
32141         var view = this.getView();
32142         view.init(this);
32143
32144         c.on("click", this.onClick, this);
32145         c.on("dblclick", this.onDblClick, this);
32146         c.on("contextmenu", this.onContextMenu, this);
32147         c.on("keydown", this.onKeyDown, this);
32148
32149         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32150
32151         this.getSelectionModel().init(this);
32152
32153         view.render();
32154
32155         if(this.loadMask){
32156             this.loadMask = new Roo.LoadMask(this.container,
32157                     Roo.apply({store:this.dataSource}, this.loadMask));
32158         }
32159         
32160         
32161         if (this.toolbar && this.toolbar.xtype) {
32162             this.toolbar.container = this.getView().getHeaderPanel(true);
32163             this.toolbar = new Ext.Toolbar(this.toolbar);
32164         }
32165         if (this.footer && this.footer.xtype) {
32166             this.footer.dataSource = this.getDataSource();
32167             this.footer.container = this.getView().getFooterPanel(true);
32168             this.footer = Roo.factory(this.footer, Roo);
32169         }
32170         if (this.dropTarget && this.dropTarget.xtype) {
32171             delete this.dropTarget.xtype;
32172             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32173         }
32174         
32175         
32176         this.rendered = true;
32177         this.fireEvent('render', this);
32178         return this;
32179     },
32180
32181         /**
32182          * Reconfigures the grid to use a different Store and Column Model.
32183          * The View will be bound to the new objects and refreshed.
32184          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32185          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32186          */
32187     reconfigure : function(dataSource, colModel){
32188         if(this.loadMask){
32189             this.loadMask.destroy();
32190             this.loadMask = new Roo.LoadMask(this.container,
32191                     Roo.apply({store:dataSource}, this.loadMask));
32192         }
32193         this.view.bind(dataSource, colModel);
32194         this.dataSource = dataSource;
32195         this.colModel = colModel;
32196         this.view.refresh(true);
32197     },
32198
32199     // private
32200     onKeyDown : function(e){
32201         this.fireEvent("keydown", e);
32202     },
32203
32204     /**
32205      * Destroy this grid.
32206      * @param {Boolean} removeEl True to remove the element
32207      */
32208     destroy : function(removeEl, keepListeners){
32209         if(this.loadMask){
32210             this.loadMask.destroy();
32211         }
32212         var c = this.container;
32213         c.removeAllListeners();
32214         this.view.destroy();
32215         this.colModel.purgeListeners();
32216         if(!keepListeners){
32217             this.purgeListeners();
32218         }
32219         c.update("");
32220         if(removeEl === true){
32221             c.remove();
32222         }
32223     },
32224
32225     // private
32226     processEvent : function(name, e){
32227         this.fireEvent(name, e);
32228         var t = e.getTarget();
32229         var v = this.view;
32230         var header = v.findHeaderIndex(t);
32231         if(header !== false){
32232             this.fireEvent("header" + name, this, header, e);
32233         }else{
32234             var row = v.findRowIndex(t);
32235             var cell = v.findCellIndex(t);
32236             if(row !== false){
32237                 this.fireEvent("row" + name, this, row, e);
32238                 if(cell !== false){
32239                     this.fireEvent("cell" + name, this, row, cell, e);
32240                 }
32241             }
32242         }
32243     },
32244
32245     // private
32246     onClick : function(e){
32247         this.processEvent("click", e);
32248     },
32249
32250     // private
32251     onContextMenu : function(e, t){
32252         this.processEvent("contextmenu", e);
32253     },
32254
32255     // private
32256     onDblClick : function(e){
32257         this.processEvent("dblclick", e);
32258     },
32259
32260     // private
32261     walkCells : function(row, col, step, fn, scope){
32262         var cm = this.colModel, clen = cm.getColumnCount();
32263         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32264         if(step < 0){
32265             if(col < 0){
32266                 row--;
32267                 first = false;
32268             }
32269             while(row >= 0){
32270                 if(!first){
32271                     col = clen-1;
32272                 }
32273                 first = false;
32274                 while(col >= 0){
32275                     if(fn.call(scope || this, row, col, cm) === true){
32276                         return [row, col];
32277                     }
32278                     col--;
32279                 }
32280                 row--;
32281             }
32282         } else {
32283             if(col >= clen){
32284                 row++;
32285                 first = false;
32286             }
32287             while(row < rlen){
32288                 if(!first){
32289                     col = 0;
32290                 }
32291                 first = false;
32292                 while(col < clen){
32293                     if(fn.call(scope || this, row, col, cm) === true){
32294                         return [row, col];
32295                     }
32296                     col++;
32297                 }
32298                 row++;
32299             }
32300         }
32301         return null;
32302     },
32303
32304     // private
32305     getSelections : function(){
32306         return this.selModel.getSelections();
32307     },
32308
32309     /**
32310      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32311      * but if manual update is required this method will initiate it.
32312      */
32313     autoSize : function(){
32314         if(this.rendered){
32315             this.view.layout();
32316             if(this.view.adjustForScroll){
32317                 this.view.adjustForScroll();
32318             }
32319         }
32320     },
32321
32322     /**
32323      * Returns the grid's underlying element.
32324      * @return {Element} The element
32325      */
32326     getGridEl : function(){
32327         return this.container;
32328     },
32329
32330     // private for compatibility, overridden by editor grid
32331     stopEditing : function(){},
32332
32333     /**
32334      * Returns the grid's SelectionModel.
32335      * @return {SelectionModel}
32336      */
32337     getSelectionModel : function(){
32338         if(!this.selModel){
32339             this.selModel = new Roo.grid.RowSelectionModel();
32340         }
32341         return this.selModel;
32342     },
32343
32344     /**
32345      * Returns the grid's DataSource.
32346      * @return {DataSource}
32347      */
32348     getDataSource : function(){
32349         return this.dataSource;
32350     },
32351
32352     /**
32353      * Returns the grid's ColumnModel.
32354      * @return {ColumnModel}
32355      */
32356     getColumnModel : function(){
32357         return this.colModel;
32358     },
32359
32360     /**
32361      * Returns the grid's GridView object.
32362      * @return {GridView}
32363      */
32364     getView : function(){
32365         if(!this.view){
32366             this.view = new Roo.grid.GridView(this.viewConfig);
32367         }
32368         return this.view;
32369     },
32370     /**
32371      * Called to get grid's drag proxy text, by default returns this.ddText.
32372      * @return {String}
32373      */
32374     getDragDropText : function(){
32375         var count = this.selModel.getCount();
32376         return String.format(this.ddText, count, count == 1 ? '' : 's');
32377     }
32378 });
32379 /**
32380  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32381  * %0 is replaced with the number of selected rows.
32382  * @type String
32383  */
32384 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
32385  * Based on:
32386  * Ext JS Library 1.1.1
32387  * Copyright(c) 2006-2007, Ext JS, LLC.
32388  *
32389  * Originally Released Under LGPL - original licence link has changed is not relivant.
32390  *
32391  * Fork - LGPL
32392  * <script type="text/javascript">
32393  */
32394  
32395 Roo.grid.AbstractGridView = function(){
32396         this.grid = null;
32397         
32398         this.events = {
32399             "beforerowremoved" : true,
32400             "beforerowsinserted" : true,
32401             "beforerefresh" : true,
32402             "rowremoved" : true,
32403             "rowsinserted" : true,
32404             "rowupdated" : true,
32405             "refresh" : true
32406         };
32407     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32408 };
32409
32410 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32411     rowClass : "x-grid-row",
32412     cellClass : "x-grid-cell",
32413     tdClass : "x-grid-td",
32414     hdClass : "x-grid-hd",
32415     splitClass : "x-grid-hd-split",
32416     
32417         init: function(grid){
32418         this.grid = grid;
32419                 var cid = this.grid.getGridEl().id;
32420         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32421         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32422         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32423         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32424         },
32425         
32426         getColumnRenderers : function(){
32427         var renderers = [];
32428         var cm = this.grid.colModel;
32429         var colCount = cm.getColumnCount();
32430         for(var i = 0; i < colCount; i++){
32431             renderers[i] = cm.getRenderer(i);
32432         }
32433         return renderers;
32434     },
32435     
32436     getColumnIds : function(){
32437         var ids = [];
32438         var cm = this.grid.colModel;
32439         var colCount = cm.getColumnCount();
32440         for(var i = 0; i < colCount; i++){
32441             ids[i] = cm.getColumnId(i);
32442         }
32443         return ids;
32444     },
32445     
32446     getDataIndexes : function(){
32447         if(!this.indexMap){
32448             this.indexMap = this.buildIndexMap();
32449         }
32450         return this.indexMap.colToData;
32451     },
32452     
32453     getColumnIndexByDataIndex : function(dataIndex){
32454         if(!this.indexMap){
32455             this.indexMap = this.buildIndexMap();
32456         }
32457         return this.indexMap.dataToCol[dataIndex];
32458     },
32459     
32460     /**
32461      * Set a css style for a column dynamically. 
32462      * @param {Number} colIndex The index of the column
32463      * @param {String} name The css property name
32464      * @param {String} value The css value
32465      */
32466     setCSSStyle : function(colIndex, name, value){
32467         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
32468         Roo.util.CSS.updateRule(selector, name, value);
32469     },
32470     
32471     generateRules : function(cm){
32472         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
32473         Roo.util.CSS.removeStyleSheet(rulesId);
32474         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32475             var cid = cm.getColumnId(i);
32476             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
32477                          this.tdSelector, cid, " {\n}\n",
32478                          this.hdSelector, cid, " {\n}\n",
32479                          this.splitSelector, cid, " {\n}\n");
32480         }
32481         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
32482     }
32483 });/*
32484  * Based on:
32485  * Ext JS Library 1.1.1
32486  * Copyright(c) 2006-2007, Ext JS, LLC.
32487  *
32488  * Originally Released Under LGPL - original licence link has changed is not relivant.
32489  *
32490  * Fork - LGPL
32491  * <script type="text/javascript">
32492  */
32493
32494 // private
32495 // This is a support class used internally by the Grid components
32496 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
32497     this.grid = grid;
32498     this.view = grid.getView();
32499     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32500     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
32501     if(hd2){
32502         this.setHandleElId(Roo.id(hd));
32503         this.setOuterHandleElId(Roo.id(hd2));
32504     }
32505     this.scroll = false;
32506 };
32507 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
32508     maxDragWidth: 120,
32509     getDragData : function(e){
32510         var t = Roo.lib.Event.getTarget(e);
32511         var h = this.view.findHeaderCell(t);
32512         if(h){
32513             return {ddel: h.firstChild, header:h};
32514         }
32515         return false;
32516     },
32517
32518     onInitDrag : function(e){
32519         this.view.headersDisabled = true;
32520         var clone = this.dragData.ddel.cloneNode(true);
32521         clone.id = Roo.id();
32522         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
32523         this.proxy.update(clone);
32524         return true;
32525     },
32526
32527     afterValidDrop : function(){
32528         var v = this.view;
32529         setTimeout(function(){
32530             v.headersDisabled = false;
32531         }, 50);
32532     },
32533
32534     afterInvalidDrop : function(){
32535         var v = this.view;
32536         setTimeout(function(){
32537             v.headersDisabled = false;
32538         }, 50);
32539     }
32540 });
32541 /*
32542  * Based on:
32543  * Ext JS Library 1.1.1
32544  * Copyright(c) 2006-2007, Ext JS, LLC.
32545  *
32546  * Originally Released Under LGPL - original licence link has changed is not relivant.
32547  *
32548  * Fork - LGPL
32549  * <script type="text/javascript">
32550  */
32551 // private
32552 // This is a support class used internally by the Grid components
32553 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
32554     this.grid = grid;
32555     this.view = grid.getView();
32556     // split the proxies so they don't interfere with mouse events
32557     this.proxyTop = Roo.DomHelper.append(document.body, {
32558         cls:"col-move-top", html:"&#160;"
32559     }, true);
32560     this.proxyBottom = Roo.DomHelper.append(document.body, {
32561         cls:"col-move-bottom", html:"&#160;"
32562     }, true);
32563     this.proxyTop.hide = this.proxyBottom.hide = function(){
32564         this.setLeftTop(-100,-100);
32565         this.setStyle("visibility", "hidden");
32566     };
32567     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32568     // temporarily disabled
32569     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
32570     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
32571 };
32572 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
32573     proxyOffsets : [-4, -9],
32574     fly: Roo.Element.fly,
32575
32576     getTargetFromEvent : function(e){
32577         var t = Roo.lib.Event.getTarget(e);
32578         var cindex = this.view.findCellIndex(t);
32579         if(cindex !== false){
32580             return this.view.getHeaderCell(cindex);
32581         }
32582     },
32583
32584     nextVisible : function(h){
32585         var v = this.view, cm = this.grid.colModel;
32586         h = h.nextSibling;
32587         while(h){
32588             if(!cm.isHidden(v.getCellIndex(h))){
32589                 return h;
32590             }
32591             h = h.nextSibling;
32592         }
32593         return null;
32594     },
32595
32596     prevVisible : function(h){
32597         var v = this.view, cm = this.grid.colModel;
32598         h = h.prevSibling;
32599         while(h){
32600             if(!cm.isHidden(v.getCellIndex(h))){
32601                 return h;
32602             }
32603             h = h.prevSibling;
32604         }
32605         return null;
32606     },
32607
32608     positionIndicator : function(h, n, e){
32609         var x = Roo.lib.Event.getPageX(e);
32610         var r = Roo.lib.Dom.getRegion(n.firstChild);
32611         var px, pt, py = r.top + this.proxyOffsets[1];
32612         if((r.right - x) <= (r.right-r.left)/2){
32613             px = r.right+this.view.borderWidth;
32614             pt = "after";
32615         }else{
32616             px = r.left;
32617             pt = "before";
32618         }
32619         var oldIndex = this.view.getCellIndex(h);
32620         var newIndex = this.view.getCellIndex(n);
32621
32622         if(this.grid.colModel.isFixed(newIndex)){
32623             return false;
32624         }
32625
32626         var locked = this.grid.colModel.isLocked(newIndex);
32627
32628         if(pt == "after"){
32629             newIndex++;
32630         }
32631         if(oldIndex < newIndex){
32632             newIndex--;
32633         }
32634         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
32635             return false;
32636         }
32637         px +=  this.proxyOffsets[0];
32638         this.proxyTop.setLeftTop(px, py);
32639         this.proxyTop.show();
32640         if(!this.bottomOffset){
32641             this.bottomOffset = this.view.mainHd.getHeight();
32642         }
32643         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
32644         this.proxyBottom.show();
32645         return pt;
32646     },
32647
32648     onNodeEnter : function(n, dd, e, data){
32649         if(data.header != n){
32650             this.positionIndicator(data.header, n, e);
32651         }
32652     },
32653
32654     onNodeOver : function(n, dd, e, data){
32655         var result = false;
32656         if(data.header != n){
32657             result = this.positionIndicator(data.header, n, e);
32658         }
32659         if(!result){
32660             this.proxyTop.hide();
32661             this.proxyBottom.hide();
32662         }
32663         return result ? this.dropAllowed : this.dropNotAllowed;
32664     },
32665
32666     onNodeOut : function(n, dd, e, data){
32667         this.proxyTop.hide();
32668         this.proxyBottom.hide();
32669     },
32670
32671     onNodeDrop : function(n, dd, e, data){
32672         var h = data.header;
32673         if(h != n){
32674             var cm = this.grid.colModel;
32675             var x = Roo.lib.Event.getPageX(e);
32676             var r = Roo.lib.Dom.getRegion(n.firstChild);
32677             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
32678             var oldIndex = this.view.getCellIndex(h);
32679             var newIndex = this.view.getCellIndex(n);
32680             var locked = cm.isLocked(newIndex);
32681             if(pt == "after"){
32682                 newIndex++;
32683             }
32684             if(oldIndex < newIndex){
32685                 newIndex--;
32686             }
32687             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
32688                 return false;
32689             }
32690             cm.setLocked(oldIndex, locked, true);
32691             cm.moveColumn(oldIndex, newIndex);
32692             this.grid.fireEvent("columnmove", oldIndex, newIndex);
32693             return true;
32694         }
32695         return false;
32696     }
32697 });
32698 /*
32699  * Based on:
32700  * Ext JS Library 1.1.1
32701  * Copyright(c) 2006-2007, Ext JS, LLC.
32702  *
32703  * Originally Released Under LGPL - original licence link has changed is not relivant.
32704  *
32705  * Fork - LGPL
32706  * <script type="text/javascript">
32707  */
32708   
32709 /**
32710  * @class Roo.grid.GridView
32711  * @extends Roo.util.Observable
32712  *
32713  * @constructor
32714  * @param {Object} config
32715  */
32716 Roo.grid.GridView = function(config){
32717     Roo.grid.GridView.superclass.constructor.call(this);
32718     this.el = null;
32719
32720     Roo.apply(this, config);
32721 };
32722
32723 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
32724
32725     /**
32726      * Override this function to apply custom css classes to rows during rendering
32727      * @param {Record} record The record
32728      * @param {Number} index
32729      * @method getRowClass
32730      */
32731     rowClass : "x-grid-row",
32732
32733     cellClass : "x-grid-col",
32734
32735     tdClass : "x-grid-td",
32736
32737     hdClass : "x-grid-hd",
32738
32739     splitClass : "x-grid-split",
32740
32741     sortClasses : ["sort-asc", "sort-desc"],
32742
32743     enableMoveAnim : false,
32744
32745     hlColor: "C3DAF9",
32746
32747     dh : Roo.DomHelper,
32748
32749     fly : Roo.Element.fly,
32750
32751     css : Roo.util.CSS,
32752
32753     borderWidth: 1,
32754
32755     splitOffset: 3,
32756
32757     scrollIncrement : 22,
32758
32759     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
32760
32761     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
32762
32763     bind : function(ds, cm){
32764         if(this.ds){
32765             this.ds.un("load", this.onLoad, this);
32766             this.ds.un("datachanged", this.onDataChange, this);
32767             this.ds.un("add", this.onAdd, this);
32768             this.ds.un("remove", this.onRemove, this);
32769             this.ds.un("update", this.onUpdate, this);
32770             this.ds.un("clear", this.onClear, this);
32771         }
32772         if(ds){
32773             ds.on("load", this.onLoad, this);
32774             ds.on("datachanged", this.onDataChange, this);
32775             ds.on("add", this.onAdd, this);
32776             ds.on("remove", this.onRemove, this);
32777             ds.on("update", this.onUpdate, this);
32778             ds.on("clear", this.onClear, this);
32779         }
32780         this.ds = ds;
32781
32782         if(this.cm){
32783             this.cm.un("widthchange", this.onColWidthChange, this);
32784             this.cm.un("headerchange", this.onHeaderChange, this);
32785             this.cm.un("hiddenchange", this.onHiddenChange, this);
32786             this.cm.un("columnmoved", this.onColumnMove, this);
32787             this.cm.un("columnlockchange", this.onColumnLock, this);
32788         }
32789         if(cm){
32790             this.generateRules(cm);
32791             cm.on("widthchange", this.onColWidthChange, this);
32792             cm.on("headerchange", this.onHeaderChange, this);
32793             cm.on("hiddenchange", this.onHiddenChange, this);
32794             cm.on("columnmoved", this.onColumnMove, this);
32795             cm.on("columnlockchange", this.onColumnLock, this);
32796         }
32797         this.cm = cm;
32798     },
32799
32800     init: function(grid){
32801                 Roo.grid.GridView.superclass.init.call(this, grid);
32802
32803                 this.bind(grid.dataSource, grid.colModel);
32804
32805             grid.on("headerclick", this.handleHeaderClick, this);
32806
32807         if(grid.trackMouseOver){
32808             grid.on("mouseover", this.onRowOver, this);
32809                 grid.on("mouseout", this.onRowOut, this);
32810             }
32811             grid.cancelTextSelection = function(){};
32812                 this.gridId = grid.id;
32813
32814                 var tpls = this.templates || {};
32815
32816                 if(!tpls.master){
32817                     tpls.master = new Roo.Template(
32818                        '<div class="x-grid" hidefocus="true">',
32819                           '<div class="x-grid-topbar"></div>',
32820                           '<div class="x-grid-scroller"><div></div></div>',
32821                           '<div class="x-grid-locked">',
32822                               '<div class="x-grid-header">{lockedHeader}</div>',
32823                               '<div class="x-grid-body">{lockedBody}</div>',
32824                           "</div>",
32825                           '<div class="x-grid-viewport">',
32826                               '<div class="x-grid-header">{header}</div>',
32827                               '<div class="x-grid-body">{body}</div>',
32828                           "</div>",
32829                           '<div class="x-grid-bottombar"></div>',
32830                           '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
32831                           '<div class="x-grid-resize-proxy">&#160;</div>',
32832                        "</div>"
32833                     );
32834                     tpls.master.disableformats = true;
32835                 }
32836
32837                 if(!tpls.header){
32838                     tpls.header = new Roo.Template(
32839                        '<table border="0" cellspacing="0" cellpadding="0">',
32840                        '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
32841                        "</table>{splits}"
32842                     );
32843                     tpls.header.disableformats = true;
32844                 }
32845                 tpls.header.compile();
32846
32847                 if(!tpls.hcell){
32848                     tpls.hcell = new Roo.Template(
32849                         '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
32850                         '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
32851                         "</div></td>"
32852                      );
32853                      tpls.hcell.disableFormats = true;
32854                 }
32855                 tpls.hcell.compile();
32856
32857                 if(!tpls.hsplit){
32858                     tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
32859                     tpls.hsplit.disableFormats = true;
32860                 }
32861                 tpls.hsplit.compile();
32862
32863                 if(!tpls.body){
32864                     tpls.body = new Roo.Template(
32865                        '<table border="0" cellspacing="0" cellpadding="0">',
32866                        "<tbody>{rows}</tbody>",
32867                        "</table>"
32868                     );
32869                     tpls.body.disableFormats = true;
32870                 }
32871                 tpls.body.compile();
32872
32873                 if(!tpls.row){
32874                     tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
32875                     tpls.row.disableFormats = true;
32876                 }
32877                 tpls.row.compile();
32878
32879                 if(!tpls.cell){
32880                     tpls.cell = new Roo.Template(
32881                         '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
32882                         '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
32883                         "</td>"
32884                     );
32885             tpls.cell.disableFormats = true;
32886         }
32887                 tpls.cell.compile();
32888
32889                 this.templates = tpls;
32890         },
32891
32892         // remap these for backwards compat
32893     onColWidthChange : function(){
32894         this.updateColumns.apply(this, arguments);
32895     },
32896     onHeaderChange : function(){
32897         this.updateHeaders.apply(this, arguments);
32898     }, 
32899     onHiddenChange : function(){
32900         this.handleHiddenChange.apply(this, arguments);
32901     },
32902     onColumnMove : function(){
32903         this.handleColumnMove.apply(this, arguments);
32904     },
32905     onColumnLock : function(){
32906         this.handleLockChange.apply(this, arguments);
32907     },
32908
32909     onDataChange : function(){
32910         this.refresh();
32911         this.updateHeaderSortState();
32912     },
32913
32914         onClear : function(){
32915         this.refresh();
32916     },
32917
32918         onUpdate : function(ds, record){
32919         this.refreshRow(record);
32920     },
32921
32922     refreshRow : function(record){
32923         var ds = this.ds, index;
32924         if(typeof record == 'number'){
32925             index = record;
32926             record = ds.getAt(index);
32927         }else{
32928             index = ds.indexOf(record);
32929         }
32930         this.insertRows(ds, index, index, true);
32931         this.onRemove(ds, record, index+1, true);
32932         this.syncRowHeights(index, index);
32933         this.layout();
32934         this.fireEvent("rowupdated", this, index, record);
32935     },
32936
32937     onAdd : function(ds, records, index){
32938         this.insertRows(ds, index, index + (records.length-1));
32939     },
32940
32941     onRemove : function(ds, record, index, isUpdate){
32942         if(isUpdate !== true){
32943             this.fireEvent("beforerowremoved", this, index, record);
32944         }
32945         var bt = this.getBodyTable(), lt = this.getLockedTable();
32946         if(bt.rows[index]){
32947             bt.firstChild.removeChild(bt.rows[index]);
32948         }
32949         if(lt.rows[index]){
32950             lt.firstChild.removeChild(lt.rows[index]);
32951         }
32952         if(isUpdate !== true){
32953             this.stripeRows(index);
32954             this.syncRowHeights(index, index);
32955             this.layout();
32956             this.fireEvent("rowremoved", this, index, record);
32957         }
32958     },
32959
32960     onLoad : function(){
32961         this.scrollToTop();
32962     },
32963
32964     /**
32965      * Scrolls the grid to the top
32966      */
32967     scrollToTop : function(){
32968         if(this.scroller){
32969             this.scroller.dom.scrollTop = 0;
32970             this.syncScroll();
32971         }
32972     },
32973
32974     /**
32975      * Gets a panel in the header of the grid that can be used for toolbars etc.
32976      * After modifying the contents of this panel a call to grid.autoSize() may be
32977      * required to register any changes in size.
32978      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
32979      * @return Roo.Element
32980      */
32981     getHeaderPanel : function(doShow){
32982         if(doShow){
32983             this.headerPanel.show();
32984         }
32985         return this.headerPanel;
32986         },
32987
32988         /**
32989      * Gets a panel in the footer of the grid that can be used for toolbars etc.
32990      * After modifying the contents of this panel a call to grid.autoSize() may be
32991      * required to register any changes in size.
32992      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
32993      * @return Roo.Element
32994      */
32995     getFooterPanel : function(doShow){
32996         if(doShow){
32997             this.footerPanel.show();
32998         }
32999         return this.footerPanel;
33000         },
33001
33002         initElements : function(){
33003             var E = Roo.Element;
33004             var el = this.grid.getGridEl().dom.firstChild;
33005             var cs = el.childNodes;
33006
33007             this.el = new E(el);
33008             this.headerPanel = new E(el.firstChild);
33009             this.headerPanel.enableDisplayMode("block");
33010
33011         this.scroller = new E(cs[1]);
33012             this.scrollSizer = new E(this.scroller.dom.firstChild);
33013
33014             this.lockedWrap = new E(cs[2]);
33015             this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33016             this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33017
33018             this.mainWrap = new E(cs[3]);
33019             this.mainHd = new E(this.mainWrap.dom.firstChild);
33020             this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33021
33022             this.footerPanel = new E(cs[4]);
33023             this.footerPanel.enableDisplayMode("block");
33024
33025         this.focusEl = new E(cs[5]);
33026         this.focusEl.swallowEvent("click", true);
33027         this.resizeProxy = new E(cs[6]);
33028
33029             this.headerSelector = String.format(
33030                '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33031                this.lockedHd.id, this.mainHd.id
33032             );
33033
33034             this.splitterSelector = String.format(
33035                '#{0} div.x-grid-split, #{1} div.x-grid-split',
33036                this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33037             );
33038     },
33039     idToCssName : function(s)
33040     {
33041         return s.replace(/[^a-z0-9]+/ig, '-');
33042     },
33043
33044         getHeaderCell : function(index){
33045             return Roo.DomQuery.select(this.headerSelector)[index];
33046         },
33047
33048         getHeaderCellMeasure : function(index){
33049             return this.getHeaderCell(index).firstChild;
33050         },
33051
33052         getHeaderCellText : function(index){
33053             return this.getHeaderCell(index).firstChild.firstChild;
33054         },
33055
33056         getLockedTable : function(){
33057             return this.lockedBody.dom.firstChild;
33058         },
33059
33060         getBodyTable : function(){
33061             return this.mainBody.dom.firstChild;
33062         },
33063
33064         getLockedRow : function(index){
33065             return this.getLockedTable().rows[index];
33066         },
33067
33068         getRow : function(index){
33069             return this.getBodyTable().rows[index];
33070         },
33071
33072         getRowComposite : function(index){
33073             if(!this.rowEl){
33074                 this.rowEl = new Roo.CompositeElementLite();
33075             }
33076         var els = [], lrow, mrow;
33077         if(lrow = this.getLockedRow(index)){
33078             els.push(lrow);
33079         }
33080         if(mrow = this.getRow(index)){
33081             els.push(mrow);
33082         }
33083         this.rowEl.elements = els;
33084             return this.rowEl;
33085         },
33086
33087         getCell : function(rowIndex, colIndex){
33088             var locked = this.cm.getLockedCount();
33089             var source;
33090             if(colIndex < locked){
33091                 source = this.lockedBody.dom.firstChild;
33092             }else{
33093                 source = this.mainBody.dom.firstChild;
33094                 colIndex -= locked;
33095             }
33096         return source.rows[rowIndex].childNodes[colIndex];
33097         },
33098
33099         getCellText : function(rowIndex, colIndex){
33100             return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33101         },
33102
33103         getCellBox : function(cell){
33104             var b = this.fly(cell).getBox();
33105         if(Roo.isOpera){ // opera fails to report the Y
33106             b.y = cell.offsetTop + this.mainBody.getY();
33107         }
33108         return b;
33109     },
33110
33111     getCellIndex : function(cell){
33112         var id = String(cell.className).match(this.cellRE);
33113         if(id){
33114             return parseInt(id[1], 10);
33115         }
33116         return 0;
33117     },
33118
33119     findHeaderIndex : function(n){
33120         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33121         return r ? this.getCellIndex(r) : false;
33122     },
33123
33124     findHeaderCell : function(n){
33125         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33126         return r ? r : false;
33127     },
33128
33129     findRowIndex : function(n){
33130         if(!n){
33131             return false;
33132         }
33133         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33134         return r ? r.rowIndex : false;
33135     },
33136
33137     findCellIndex : function(node){
33138         var stop = this.el.dom;
33139         while(node && node != stop){
33140             if(this.findRE.test(node.className)){
33141                 return this.getCellIndex(node);
33142             }
33143             node = node.parentNode;
33144         }
33145         return false;
33146     },
33147
33148     getColumnId : function(index){
33149             return this.cm.getColumnId(index);
33150         },
33151
33152         getSplitters : function(){
33153             if(this.splitterSelector){
33154                return Roo.DomQuery.select(this.splitterSelector);
33155             }else{
33156                 return null;
33157             }
33158         },
33159
33160         getSplitter : function(index){
33161             return this.getSplitters()[index];
33162         },
33163
33164     onRowOver : function(e, t){
33165         var row;
33166         if((row = this.findRowIndex(t)) !== false){
33167             this.getRowComposite(row).addClass("x-grid-row-over");
33168         }
33169     },
33170
33171     onRowOut : function(e, t){
33172         var row;
33173         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33174             this.getRowComposite(row).removeClass("x-grid-row-over");
33175         }
33176     },
33177
33178     renderHeaders : function(){
33179             var cm = this.cm;
33180         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33181         var cb = [], lb = [], sb = [], lsb = [], p = {};
33182         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33183             p.cellId = "x-grid-hd-0-" + i;
33184             p.splitId = "x-grid-csplit-0-" + i;
33185             p.id = cm.getColumnId(i);
33186             p.title = cm.getColumnTooltip(i) || "";
33187             p.value = cm.getColumnHeader(i) || "";
33188             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33189             if(!cm.isLocked(i)){
33190                 cb[cb.length] = ct.apply(p);
33191                 sb[sb.length] = st.apply(p);
33192             }else{
33193                 lb[lb.length] = ct.apply(p);
33194                 lsb[lsb.length] = st.apply(p);
33195             }
33196         }
33197         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33198                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33199         },
33200
33201         updateHeaders : function(){
33202         var html = this.renderHeaders();
33203         this.lockedHd.update(html[0]);
33204         this.mainHd.update(html[1]);
33205     },
33206
33207     /**
33208      * Focuses the specified row.
33209      * @param {Number} row The row index
33210      */
33211     focusRow : function(row){
33212         var x = this.scroller.dom.scrollLeft;
33213         this.focusCell(row, 0, false);
33214         this.scroller.dom.scrollLeft = x;
33215     },
33216
33217     /**
33218      * Focuses the specified cell.
33219      * @param {Number} row The row index
33220      * @param {Number} col The column index
33221      * @param {Boolean} hscroll false to disable horizontal scrolling
33222      */
33223     focusCell : function(row, col, hscroll){
33224         var el = this.ensureVisible(row, col, hscroll);
33225         this.focusEl.alignTo(el, "tl-tl");
33226         if(Roo.isGecko){
33227             this.focusEl.focus();
33228         }else{
33229             this.focusEl.focus.defer(1, this.focusEl);
33230         }
33231     },
33232
33233     /**
33234      * Scrolls the specified cell into view
33235      * @param {Number} row The row index
33236      * @param {Number} col The column index
33237      * @param {Boolean} hscroll false to disable horizontal scrolling
33238      */
33239     ensureVisible : function(row, col, hscroll){
33240         if(typeof row != "number"){
33241             row = row.rowIndex;
33242         }
33243         if(row < 0 && row >= this.ds.getCount()){
33244             return;
33245         }
33246         col = (col !== undefined ? col : 0);
33247         var cm = this.grid.colModel;
33248         while(cm.isHidden(col)){
33249             col++;
33250         }
33251
33252         var el = this.getCell(row, col);
33253         if(!el){
33254             return;
33255         }
33256         var c = this.scroller.dom;
33257
33258         var ctop = parseInt(el.offsetTop, 10);
33259         var cleft = parseInt(el.offsetLeft, 10);
33260         var cbot = ctop + el.offsetHeight;
33261         var cright = cleft + el.offsetWidth;
33262
33263         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33264         var stop = parseInt(c.scrollTop, 10);
33265         var sleft = parseInt(c.scrollLeft, 10);
33266         var sbot = stop + ch;
33267         var sright = sleft + c.clientWidth;
33268
33269         if(ctop < stop){
33270                 c.scrollTop = ctop;
33271         }else if(cbot > sbot){
33272             c.scrollTop = cbot-ch;
33273         }
33274
33275         if(hscroll !== false){
33276             if(cleft < sleft){
33277                 c.scrollLeft = cleft;
33278             }else if(cright > sright){
33279                 c.scrollLeft = cright-c.clientWidth;
33280             }
33281         }
33282         return el;
33283     },
33284
33285     updateColumns : function(){
33286         this.grid.stopEditing();
33287         var cm = this.grid.colModel, colIds = this.getColumnIds();
33288         //var totalWidth = cm.getTotalWidth();
33289         var pos = 0;
33290         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33291             //if(cm.isHidden(i)) continue;
33292             var w = cm.getColumnWidth(i);
33293             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33294             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33295         }
33296         this.updateSplitters();
33297     },
33298
33299     generateRules : function(cm){
33300         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33301         Roo.util.CSS.removeStyleSheet(rulesId);
33302         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33303             var cid = cm.getColumnId(i);
33304             var align = '';
33305             if(cm.config[i].align){
33306                 align = 'text-align:'+cm.config[i].align+';';
33307             }
33308             var hidden = '';
33309             if(cm.isHidden(i)){
33310                 hidden = 'display:none;';
33311             }
33312             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33313             ruleBuf.push(
33314                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33315                     this.hdSelector, cid, " {\n", align, width, "}\n",
33316                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
33317                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
33318         }
33319         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33320     },
33321
33322     updateSplitters : function(){
33323         var cm = this.cm, s = this.getSplitters();
33324         if(s){ // splitters not created yet
33325             var pos = 0, locked = true;
33326             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33327                 if(cm.isHidden(i)) continue;
33328                 var w = cm.getColumnWidth(i);
33329                 if(!cm.isLocked(i) && locked){
33330                     pos = 0;
33331                     locked = false;
33332                 }
33333                 pos += w;
33334                 s[i].style.left = (pos-this.splitOffset) + "px";
33335             }
33336         }
33337     },
33338
33339     handleHiddenChange : function(colModel, colIndex, hidden){
33340         if(hidden){
33341             this.hideColumn(colIndex);
33342         }else{
33343             this.unhideColumn(colIndex);
33344         }
33345     },
33346
33347     hideColumn : function(colIndex){
33348         var cid = this.getColumnId(colIndex);
33349         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33350         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33351         if(Roo.isSafari){
33352             this.updateHeaders();
33353         }
33354         this.updateSplitters();
33355         this.layout();
33356     },
33357
33358     unhideColumn : function(colIndex){
33359         var cid = this.getColumnId(colIndex);
33360         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33361         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33362
33363         if(Roo.isSafari){
33364             this.updateHeaders();
33365         }
33366         this.updateSplitters();
33367         this.layout();
33368     },
33369
33370     insertRows : function(dm, firstRow, lastRow, isUpdate){
33371         if(firstRow == 0 && lastRow == dm.getCount()-1){
33372             this.refresh();
33373         }else{
33374             if(!isUpdate){
33375                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33376             }
33377             var s = this.getScrollState();
33378             var markup = this.renderRows(firstRow, lastRow);
33379             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33380             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33381             this.restoreScroll(s);
33382             if(!isUpdate){
33383                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33384                 this.syncRowHeights(firstRow, lastRow);
33385                 this.stripeRows(firstRow);
33386                 this.layout();
33387             }
33388         }
33389     },
33390
33391     bufferRows : function(markup, target, index){
33392         var before = null, trows = target.rows, tbody = target.tBodies[0];
33393         if(index < trows.length){
33394             before = trows[index];
33395         }
33396         var b = document.createElement("div");
33397         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
33398         var rows = b.firstChild.rows;
33399         for(var i = 0, len = rows.length; i < len; i++){
33400             if(before){
33401                 tbody.insertBefore(rows[0], before);
33402             }else{
33403                 tbody.appendChild(rows[0]);
33404             }
33405         }
33406         b.innerHTML = "";
33407         b = null;
33408     },
33409
33410     deleteRows : function(dm, firstRow, lastRow){
33411         if(dm.getRowCount()<1){
33412             this.fireEvent("beforerefresh", this);
33413             this.mainBody.update("");
33414             this.lockedBody.update("");
33415             this.fireEvent("refresh", this);
33416         }else{
33417             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
33418             var bt = this.getBodyTable();
33419             var tbody = bt.firstChild;
33420             var rows = bt.rows;
33421             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
33422                 tbody.removeChild(rows[firstRow]);
33423             }
33424             this.stripeRows(firstRow);
33425             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
33426         }
33427     },
33428
33429     updateRows : function(dataSource, firstRow, lastRow){
33430         var s = this.getScrollState();
33431         this.refresh();
33432         this.restoreScroll(s);
33433     },
33434
33435     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
33436         if(!noRefresh){
33437            this.refresh();
33438         }
33439         this.updateHeaderSortState();
33440     },
33441
33442     getScrollState : function(){
33443         var sb = this.scroller.dom;
33444         return {left: sb.scrollLeft, top: sb.scrollTop};
33445     },
33446
33447     stripeRows : function(startRow){
33448         if(!this.grid.stripeRows || this.ds.getCount() < 1){
33449             return;
33450         }
33451         startRow = startRow || 0;
33452         var rows = this.getBodyTable().rows;
33453         var lrows = this.getLockedTable().rows;
33454         var cls = ' x-grid-row-alt ';
33455         for(var i = startRow, len = rows.length; i < len; i++){
33456             var row = rows[i], lrow = lrows[i];
33457             var isAlt = ((i+1) % 2 == 0);
33458             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
33459             if(isAlt == hasAlt){
33460                 continue;
33461             }
33462             if(isAlt){
33463                 row.className += " x-grid-row-alt";
33464             }else{
33465                 row.className = row.className.replace("x-grid-row-alt", "");
33466             }
33467             if(lrow){
33468                 lrow.className = row.className;
33469             }
33470         }
33471     },
33472
33473     restoreScroll : function(state){
33474         var sb = this.scroller.dom;
33475         sb.scrollLeft = state.left;
33476         sb.scrollTop = state.top;
33477         this.syncScroll();
33478     },
33479
33480     syncScroll : function(){
33481         var sb = this.scroller.dom;
33482         var sh = this.mainHd.dom;
33483         var bs = this.mainBody.dom;
33484         var lv = this.lockedBody.dom;
33485         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
33486         lv.scrollTop = bs.scrollTop = sb.scrollTop;
33487     },
33488
33489     handleScroll : function(e){
33490         this.syncScroll();
33491         var sb = this.scroller.dom;
33492         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
33493         e.stopEvent();
33494     },
33495
33496     handleWheel : function(e){
33497         var d = e.getWheelDelta();
33498         this.scroller.dom.scrollTop -= d*22;
33499         // set this here to prevent jumpy scrolling on large tables
33500         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
33501         e.stopEvent();
33502     },
33503
33504     renderRows : function(startRow, endRow){
33505         // pull in all the crap needed to render rows
33506         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
33507         var colCount = cm.getColumnCount();
33508
33509         if(ds.getCount() < 1){
33510             return ["", ""];
33511         }
33512
33513         // build a map for all the columns
33514         var cs = [];
33515         for(var i = 0; i < colCount; i++){
33516             var name = cm.getDataIndex(i);
33517             cs[i] = {
33518                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
33519                 renderer : cm.getRenderer(i),
33520                 id : cm.getColumnId(i),
33521                 locked : cm.isLocked(i)
33522             };
33523         }
33524
33525         startRow = startRow || 0;
33526         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
33527
33528         // records to render
33529         var rs = ds.getRange(startRow, endRow);
33530
33531         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
33532     },
33533
33534     // As much as I hate to duplicate code, this was branched because FireFox really hates
33535     // [].join("") on strings. The performance difference was substantial enough to
33536     // branch this function
33537     doRender : Roo.isGecko ?
33538             function(cs, rs, ds, startRow, colCount, stripe){
33539                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33540                 // buffers
33541                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33542                 for(var j = 0, len = rs.length; j < len; j++){
33543                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
33544                     for(var i = 0; i < colCount; i++){
33545                         c = cs[i];
33546                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33547                         p.id = c.id;
33548                         p.css = p.attr = "";
33549                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33550                         if(p.value == undefined || p.value === "") p.value = "&#160;";
33551                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33552                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
33553                         }
33554                         var markup = ct.apply(p);
33555                         if(!c.locked){
33556                             cb+= markup;
33557                         }else{
33558                             lcb+= markup;
33559                         }
33560                     }
33561                     var alt = [];
33562                     if(stripe && ((rowIndex+1) % 2 == 0)){
33563                         alt[0] = "x-grid-row-alt";
33564                     }
33565                     if(r.dirty){
33566                         alt[1] = " x-grid-dirty-row";
33567                     }
33568                     rp.cells = lcb;
33569                     if(this.getRowClass){
33570                         alt[2] = this.getRowClass(r, rowIndex);
33571                     }
33572                     rp.alt = alt.join(" ");
33573                     lbuf+= rt.apply(rp);
33574                     rp.cells = cb;
33575                     buf+=  rt.apply(rp);
33576                 }
33577                 return [lbuf, buf];
33578             } :
33579             function(cs, rs, ds, startRow, colCount, stripe){
33580                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33581                 // buffers
33582                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33583                 for(var j = 0, len = rs.length; j < len; j++){
33584                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
33585                     for(var i = 0; i < colCount; i++){
33586                         c = cs[i];
33587                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33588                         p.id = c.id;
33589                         p.css = p.attr = "";
33590                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33591                         if(p.value == undefined || p.value === "") p.value = "&#160;";
33592                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33593                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
33594                         }
33595                         var markup = ct.apply(p);
33596                         if(!c.locked){
33597                             cb[cb.length] = markup;
33598                         }else{
33599                             lcb[lcb.length] = markup;
33600                         }
33601                     }
33602                     var alt = [];
33603                     if(stripe && ((rowIndex+1) % 2 == 0)){
33604                         alt[0] = "x-grid-row-alt";
33605                     }
33606                     if(r.dirty){
33607                         alt[1] = " x-grid-dirty-row";
33608                     }
33609                     rp.cells = lcb;
33610                     if(this.getRowClass){
33611                         alt[2] = this.getRowClass(r, rowIndex);
33612                     }
33613                     rp.alt = alt.join(" ");
33614                     rp.cells = lcb.join("");
33615                     lbuf[lbuf.length] = rt.apply(rp);
33616                     rp.cells = cb.join("");
33617                     buf[buf.length] =  rt.apply(rp);
33618                 }
33619                 return [lbuf.join(""), buf.join("")];
33620             },
33621
33622     renderBody : function(){
33623         var markup = this.renderRows();
33624         var bt = this.templates.body;
33625         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
33626     },
33627
33628     /**
33629      * Refreshes the grid
33630      * @param {Boolean} headersToo
33631      */
33632     refresh : function(headersToo){
33633         this.fireEvent("beforerefresh", this);
33634         this.grid.stopEditing();
33635         var result = this.renderBody();
33636         this.lockedBody.update(result[0]);
33637         this.mainBody.update(result[1]);
33638         if(headersToo === true){
33639             this.updateHeaders();
33640             this.updateColumns();
33641             this.updateSplitters();
33642             this.updateHeaderSortState();
33643         }
33644         this.syncRowHeights();
33645         this.layout();
33646         this.fireEvent("refresh", this);
33647     },
33648
33649     handleColumnMove : function(cm, oldIndex, newIndex){
33650         this.indexMap = null;
33651         var s = this.getScrollState();
33652         this.refresh(true);
33653         this.restoreScroll(s);
33654         this.afterMove(newIndex);
33655     },
33656
33657     afterMove : function(colIndex){
33658         if(this.enableMoveAnim && Roo.enableFx){
33659             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
33660         }
33661     },
33662
33663     updateCell : function(dm, rowIndex, dataIndex){
33664         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
33665         if(typeof colIndex == "undefined"){ // not present in grid
33666             return;
33667         }
33668         var cm = this.grid.colModel;
33669         var cell = this.getCell(rowIndex, colIndex);
33670         var cellText = this.getCellText(rowIndex, colIndex);
33671
33672         var p = {
33673             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
33674             id : cm.getColumnId(colIndex),
33675             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
33676         };
33677         var renderer = cm.getRenderer(colIndex);
33678         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
33679         if(typeof val == "undefined" || val === "") val = "&#160;";
33680         cellText.innerHTML = val;
33681         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
33682         this.syncRowHeights(rowIndex, rowIndex);
33683     },
33684
33685     calcColumnWidth : function(colIndex, maxRowsToMeasure){
33686         var maxWidth = 0;
33687         if(this.grid.autoSizeHeaders){
33688             var h = this.getHeaderCellMeasure(colIndex);
33689             maxWidth = Math.max(maxWidth, h.scrollWidth);
33690         }
33691         var tb, index;
33692         if(this.cm.isLocked(colIndex)){
33693             tb = this.getLockedTable();
33694             index = colIndex;
33695         }else{
33696             tb = this.getBodyTable();
33697             index = colIndex - this.cm.getLockedCount();
33698         }
33699         if(tb && tb.rows){
33700             var rows = tb.rows;
33701             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
33702             for(var i = 0; i < stopIndex; i++){
33703                 var cell = rows[i].childNodes[index].firstChild;
33704                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
33705             }
33706         }
33707         return maxWidth + /*margin for error in IE*/ 5;
33708     },
33709     /**
33710      * Autofit a column to its content.
33711      * @param {Number} colIndex
33712      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
33713      */
33714      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
33715          if(this.cm.isHidden(colIndex)){
33716              return; // can't calc a hidden column
33717          }
33718         if(forceMinSize){
33719             var cid = this.cm.getColumnId(colIndex);
33720             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
33721            if(this.grid.autoSizeHeaders){
33722                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
33723            }
33724         }
33725         var newWidth = this.calcColumnWidth(colIndex);
33726         this.cm.setColumnWidth(colIndex,
33727             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
33728         if(!suppressEvent){
33729             this.grid.fireEvent("columnresize", colIndex, newWidth);
33730         }
33731     },
33732
33733     /**
33734      * Autofits all columns to their content and then expands to fit any extra space in the grid
33735      */
33736      autoSizeColumns : function(){
33737         var cm = this.grid.colModel;
33738         var colCount = cm.getColumnCount();
33739         for(var i = 0; i < colCount; i++){
33740             this.autoSizeColumn(i, true, true);
33741         }
33742         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
33743             this.fitColumns();
33744         }else{
33745             this.updateColumns();
33746             this.layout();
33747         }
33748     },
33749
33750     /**
33751      * Autofits all columns to the grid's width proportionate with their current size
33752      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
33753      */
33754     fitColumns : function(reserveScrollSpace){
33755         var cm = this.grid.colModel;
33756         var colCount = cm.getColumnCount();
33757         var cols = [];
33758         var width = 0;
33759         var i, w;
33760         for (i = 0; i < colCount; i++){
33761             if(!cm.isHidden(i) && !cm.isFixed(i)){
33762                 w = cm.getColumnWidth(i);
33763                 cols.push(i);
33764                 cols.push(w);
33765                 width += w;
33766             }
33767         }
33768         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
33769         if(reserveScrollSpace){
33770             avail -= 17;
33771         }
33772         var frac = (avail - cm.getTotalWidth())/width;
33773         while (cols.length){
33774             w = cols.pop();
33775             i = cols.pop();
33776             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
33777         }
33778         this.updateColumns();
33779         this.layout();
33780     },
33781
33782     onRowSelect : function(rowIndex){
33783         var row = this.getRowComposite(rowIndex);
33784         row.addClass("x-grid-row-selected");
33785     },
33786
33787     onRowDeselect : function(rowIndex){
33788         var row = this.getRowComposite(rowIndex);
33789         row.removeClass("x-grid-row-selected");
33790     },
33791
33792     onCellSelect : function(row, col){
33793         var cell = this.getCell(row, col);
33794         if(cell){
33795             Roo.fly(cell).addClass("x-grid-cell-selected");
33796         }
33797     },
33798
33799     onCellDeselect : function(row, col){
33800         var cell = this.getCell(row, col);
33801         if(cell){
33802             Roo.fly(cell).removeClass("x-grid-cell-selected");
33803         }
33804     },
33805
33806     updateHeaderSortState : function(){
33807         var state = this.ds.getSortState();
33808         if(!state){
33809             return;
33810         }
33811         this.sortState = state;
33812         var sortColumn = this.cm.findColumnIndex(state.field);
33813         if(sortColumn != -1){
33814             var sortDir = state.direction;
33815             var sc = this.sortClasses;
33816             var hds = this.el.select(this.headerSelector).removeClass(sc);
33817             hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
33818         }
33819     },
33820
33821     handleHeaderClick : function(g, index){
33822         if(this.headersDisabled){
33823             return;
33824         }
33825         var dm = g.dataSource, cm = g.colModel;
33826             if(!cm.isSortable(index)){
33827             return;
33828         }
33829             g.stopEditing();
33830         dm.sort(cm.getDataIndex(index));
33831     },
33832
33833
33834     destroy : function(){
33835         if(this.colMenu){
33836             this.colMenu.removeAll();
33837             Roo.menu.MenuMgr.unregister(this.colMenu);
33838             this.colMenu.getEl().remove();
33839             delete this.colMenu;
33840         }
33841         if(this.hmenu){
33842             this.hmenu.removeAll();
33843             Roo.menu.MenuMgr.unregister(this.hmenu);
33844             this.hmenu.getEl().remove();
33845             delete this.hmenu;
33846         }
33847         if(this.grid.enableColumnMove){
33848             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
33849             if(dds){
33850                 for(var dd in dds){
33851                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
33852                         var elid = dds[dd].dragElId;
33853                         dds[dd].unreg();
33854                         Roo.get(elid).remove();
33855                     } else if(dds[dd].config.isTarget){
33856                         dds[dd].proxyTop.remove();
33857                         dds[dd].proxyBottom.remove();
33858                         dds[dd].unreg();
33859                     }
33860                     if(Roo.dd.DDM.locationCache[dd]){
33861                         delete Roo.dd.DDM.locationCache[dd];
33862                     }
33863                 }
33864                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
33865             }
33866         }
33867         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
33868         this.bind(null, null);
33869         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
33870     },
33871
33872     handleLockChange : function(){
33873         this.refresh(true);
33874     },
33875
33876     onDenyColumnLock : function(){
33877
33878     },
33879
33880     onDenyColumnHide : function(){
33881
33882     },
33883
33884     handleHdMenuClick : function(item){
33885         var index = this.hdCtxIndex;
33886         var cm = this.cm, ds = this.ds;
33887         switch(item.id){
33888             case "asc":
33889                 ds.sort(cm.getDataIndex(index), "ASC");
33890                 break;
33891             case "desc":
33892                 ds.sort(cm.getDataIndex(index), "DESC");
33893                 break;
33894             case "lock":
33895                 var lc = cm.getLockedCount();
33896                 if(cm.getColumnCount(true) <= lc+1){
33897                     this.onDenyColumnLock();
33898                     return;
33899                 }
33900                 if(lc != index){
33901                     cm.setLocked(index, true, true);
33902                     cm.moveColumn(index, lc);
33903                     this.grid.fireEvent("columnmove", index, lc);
33904                 }else{
33905                     cm.setLocked(index, true);
33906                 }
33907             break;
33908             case "unlock":
33909                 var lc = cm.getLockedCount();
33910                 if((lc-1) != index){
33911                     cm.setLocked(index, false, true);
33912                     cm.moveColumn(index, lc-1);
33913                     this.grid.fireEvent("columnmove", index, lc-1);
33914                 }else{
33915                     cm.setLocked(index, false);
33916                 }
33917             break;
33918             default:
33919                 index = cm.getIndexById(item.id.substr(4));
33920                 if(index != -1){
33921                     if(item.checked && cm.getColumnCount(true) <= 1){
33922                         this.onDenyColumnHide();
33923                         return false;
33924                     }
33925                     cm.setHidden(index, item.checked);
33926                 }
33927         }
33928         return true;
33929     },
33930
33931     beforeColMenuShow : function(){
33932         var cm = this.cm,  colCount = cm.getColumnCount();
33933         this.colMenu.removeAll();
33934         for(var i = 0; i < colCount; i++){
33935             this.colMenu.add(new Roo.menu.CheckItem({
33936                 id: "col-"+cm.getColumnId(i),
33937                 text: cm.getColumnHeader(i),
33938                 checked: !cm.isHidden(i),
33939                 hideOnClick:false
33940             }));
33941         }
33942     },
33943
33944     handleHdCtx : function(g, index, e){
33945         e.stopEvent();
33946         var hd = this.getHeaderCell(index);
33947         this.hdCtxIndex = index;
33948         var ms = this.hmenu.items, cm = this.cm;
33949         ms.get("asc").setDisabled(!cm.isSortable(index));
33950         ms.get("desc").setDisabled(!cm.isSortable(index));
33951         if(this.grid.enableColLock !== false){
33952             ms.get("lock").setDisabled(cm.isLocked(index));
33953             ms.get("unlock").setDisabled(!cm.isLocked(index));
33954         }
33955         this.hmenu.show(hd, "tl-bl");
33956     },
33957
33958     handleHdOver : function(e){
33959         var hd = this.findHeaderCell(e.getTarget());
33960         if(hd && !this.headersDisabled){
33961             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
33962                this.fly(hd).addClass("x-grid-hd-over");
33963             }
33964         }
33965     },
33966
33967     handleHdOut : function(e){
33968         var hd = this.findHeaderCell(e.getTarget());
33969         if(hd){
33970             this.fly(hd).removeClass("x-grid-hd-over");
33971         }
33972     },
33973
33974     handleSplitDblClick : function(e, t){
33975         var i = this.getCellIndex(t);
33976         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
33977             this.autoSizeColumn(i, true);
33978             this.layout();
33979         }
33980     },
33981
33982     render : function(){
33983
33984         var cm = this.cm;
33985         var colCount = cm.getColumnCount();
33986
33987         if(this.grid.monitorWindowResize === true){
33988             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
33989         }
33990         var header = this.renderHeaders();
33991         var body = this.templates.body.apply({rows:""});
33992         var html = this.templates.master.apply({
33993             lockedBody: body,
33994             body: body,
33995             lockedHeader: header[0],
33996             header: header[1]
33997         });
33998
33999         //this.updateColumns();
34000
34001         this.grid.getGridEl().dom.innerHTML = html;
34002
34003         this.initElements();
34004         
34005         // a kludge to fix the random scolling effect in webkit
34006         this.el.on("scroll", function() {
34007             this.el.dom.scrollTop=0; // hopefully not recursive..
34008         },this);
34009
34010         this.scroller.on("scroll", this.handleScroll, this);
34011         this.lockedBody.on("mousewheel", this.handleWheel, this);
34012         this.mainBody.on("mousewheel", this.handleWheel, this);
34013
34014         this.mainHd.on("mouseover", this.handleHdOver, this);
34015         this.mainHd.on("mouseout", this.handleHdOut, this);
34016         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34017                 {delegate: "."+this.splitClass});
34018
34019         this.lockedHd.on("mouseover", this.handleHdOver, this);
34020         this.lockedHd.on("mouseout", this.handleHdOut, this);
34021         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34022                 {delegate: "."+this.splitClass});
34023
34024         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34025             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34026         }
34027
34028         this.updateSplitters();
34029
34030         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34031             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34032             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34033         }
34034
34035         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34036             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34037             this.hmenu.add(
34038                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34039                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34040             );
34041             if(this.grid.enableColLock !== false){
34042                 this.hmenu.add('-',
34043                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34044                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34045                 );
34046             }
34047             if(this.grid.enableColumnHide !== false){
34048
34049                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34050                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34051                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34052
34053                 this.hmenu.add('-',
34054                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34055                 );
34056             }
34057             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34058
34059             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34060         }
34061
34062         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34063             this.dd = new Roo.grid.GridDragZone(this.grid, {
34064                 ddGroup : this.grid.ddGroup || 'GridDD'
34065             });
34066         }
34067
34068         /*
34069         for(var i = 0; i < colCount; i++){
34070             if(cm.isHidden(i)){
34071                 this.hideColumn(i);
34072             }
34073             if(cm.config[i].align){
34074                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34075                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34076             }
34077         }*/
34078         
34079         this.updateHeaderSortState();
34080
34081         this.beforeInitialResize();
34082         this.layout(true);
34083
34084         // two part rendering gives faster view to the user
34085         this.renderPhase2.defer(1, this);
34086     },
34087
34088     renderPhase2 : function(){
34089         // render the rows now
34090         this.refresh();
34091         if(this.grid.autoSizeColumns){
34092             this.autoSizeColumns();
34093         }
34094     },
34095
34096     beforeInitialResize : function(){
34097
34098     },
34099
34100     onColumnSplitterMoved : function(i, w){
34101         this.userResized = true;
34102         var cm = this.grid.colModel;
34103         cm.setColumnWidth(i, w, true);
34104         var cid = cm.getColumnId(i);
34105         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34106         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34107         this.updateSplitters();
34108         this.layout();
34109         this.grid.fireEvent("columnresize", i, w);
34110     },
34111
34112     syncRowHeights : function(startIndex, endIndex){
34113         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34114             startIndex = startIndex || 0;
34115             var mrows = this.getBodyTable().rows;
34116             var lrows = this.getLockedTable().rows;
34117             var len = mrows.length-1;
34118             endIndex = Math.min(endIndex || len, len);
34119             for(var i = startIndex; i <= endIndex; i++){
34120                 var m = mrows[i], l = lrows[i];
34121                 var h = Math.max(m.offsetHeight, l.offsetHeight);
34122                 m.style.height = l.style.height = h + "px";
34123             }
34124         }
34125     },
34126
34127     layout : function(initialRender, is2ndPass){
34128         var g = this.grid;
34129         var auto = g.autoHeight;
34130         var scrollOffset = 16;
34131         var c = g.getGridEl(), cm = this.cm,
34132                 expandCol = g.autoExpandColumn,
34133                 gv = this;
34134         //c.beginMeasure();
34135
34136         if(!c.dom.offsetWidth){ // display:none?
34137             if(initialRender){
34138                 this.lockedWrap.show();
34139                 this.mainWrap.show();
34140             }
34141             return;
34142         }
34143
34144         var hasLock = this.cm.isLocked(0);
34145
34146         var tbh = this.headerPanel.getHeight();
34147         var bbh = this.footerPanel.getHeight();
34148
34149         if(auto){
34150             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34151             var newHeight = ch + c.getBorderWidth("tb");
34152             if(g.maxHeight){
34153                 newHeight = Math.min(g.maxHeight, newHeight);
34154             }
34155             c.setHeight(newHeight);
34156         }
34157
34158         if(g.autoWidth){
34159             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34160         }
34161
34162         var s = this.scroller;
34163
34164         var csize = c.getSize(true);
34165
34166         this.el.setSize(csize.width, csize.height);
34167
34168         this.headerPanel.setWidth(csize.width);
34169         this.footerPanel.setWidth(csize.width);
34170
34171         var hdHeight = this.mainHd.getHeight();
34172         var vw = csize.width;
34173         var vh = csize.height - (tbh + bbh);
34174
34175         s.setSize(vw, vh);
34176
34177         var bt = this.getBodyTable();
34178         var ltWidth = hasLock ?
34179                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34180
34181         var scrollHeight = bt.offsetHeight;
34182         var scrollWidth = ltWidth + bt.offsetWidth;
34183         var vscroll = false, hscroll = false;
34184
34185         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34186
34187         var lw = this.lockedWrap, mw = this.mainWrap;
34188         var lb = this.lockedBody, mb = this.mainBody;
34189
34190         setTimeout(function(){
34191             var t = s.dom.offsetTop;
34192             var w = s.dom.clientWidth,
34193                 h = s.dom.clientHeight;
34194
34195             lw.setTop(t);
34196             lw.setSize(ltWidth, h);
34197
34198             mw.setLeftTop(ltWidth, t);
34199             mw.setSize(w-ltWidth, h);
34200
34201             lb.setHeight(h-hdHeight);
34202             mb.setHeight(h-hdHeight);
34203
34204             if(is2ndPass !== true && !gv.userResized && expandCol){
34205                 // high speed resize without full column calculation
34206                 
34207                 var ci = cm.getIndexById(expandCol);
34208                 if (ci < 0) {
34209                     ci = cm.findColumnIndex(expandCol);
34210                 }
34211                 ci = Math.max(0, ci); // make sure it's got at least the first col.
34212                 var expandId = cm.getColumnId(ci);
34213                 var  tw = cm.getTotalWidth(false);
34214                 var currentWidth = cm.getColumnWidth(ci);
34215                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34216                 if(currentWidth != cw){
34217                     cm.setColumnWidth(ci, cw, true);
34218                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34219                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34220                     gv.updateSplitters();
34221                     gv.layout(false, true);
34222                 }
34223             }
34224
34225             if(initialRender){
34226                 lw.show();
34227                 mw.show();
34228             }
34229             //c.endMeasure();
34230         }, 10);
34231     },
34232
34233     onWindowResize : function(){
34234         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34235             return;
34236         }
34237         this.layout();
34238     },
34239
34240     appendFooter : function(parentEl){
34241         return null;
34242     },
34243
34244     sortAscText : "Sort Ascending",
34245     sortDescText : "Sort Descending",
34246     lockText : "Lock Column",
34247     unlockText : "Unlock Column",
34248     columnsText : "Columns"
34249 });
34250
34251
34252 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
34253     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
34254     this.proxy.el.addClass('x-grid3-col-dd');
34255 };
34256
34257 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
34258     handleMouseDown : function(e){
34259
34260     },
34261
34262     callHandleMouseDown : function(e){
34263         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
34264     }
34265 });