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 });